diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e92f9dff7812ea2396f137cde75c92e8d98eafb0..fde7ec0f2bae5847428bacb3360017f6160fdf1a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,10 +2,10 @@ "name": "Misskey", "dockerComposeFile": "docker-compose.yml", "service": "app", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "workspaceFolder": "/workspace", "features": { "ghcr.io/devcontainers-contrib/features/pnpm:2": {} }, "forwardPorts": [3000], - "postCreateCommand": ".devcontainer/init.sh" + "postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh" } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 6cb21844acf8f64ccece12dee545913acbc5a237..6ec3c86a4a4433f4638f9fc804fa4999aad6e268 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -7,7 +7,7 @@ services: dockerfile: Dockerfile volumes: - - ../..:/workspaces:cached + - ../:/workspace:cached command: sleep infinity @@ -21,7 +21,7 @@ services: networks: - internal_network volumes: - - ../redis:/data + - redis-data:/data healthcheck: test: "redis-cli ping" interval: 5s @@ -37,7 +37,7 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: misskey volumes: - - ../db:/var/lib/postgresql/data + - postgres-data:/var/lib/postgresql/data healthcheck: test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" interval: 5s @@ -45,6 +45,7 @@ services: volumes: postgres-data: + redis-data: networks: internal_network: diff --git a/.devcontainer/init.sh b/.devcontainer/init.sh index 552b229fa51ed5d51d0f4fbbd74883703edc173e..450c3920c32094deca5b6c8c8ef696c4f3db2e6d 100755 --- a/.devcontainer/init.sh +++ b/.devcontainer/init.sh @@ -2,6 +2,7 @@ set -xe +sudo chown -R node /workspace git submodule update --init pnpm install --frozen-lockfile cp .devcontainer/devcontainer.yml .config/default.yml diff --git a/.editorconfig b/.editorconfig index edccf3a9d568fcec8f7fa9c3f36474accd10ae8a..6db1367645cd6a09f3472e73f15093d83303d0de 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,7 @@ indent_style = tab indent_size = 2 charset = utf-8 insert_final_newline = true +end_of_line = lf [*.yml] indent_style = space diff --git a/.gitattributes b/.gitattributes index a175917f31ca16f29edb1bd3fa5e26c0ed1b099b..246ecb0a60a8ca02e9983088a9e69f94acda223d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,4 @@ *.glb -diff -text *.blend -diff -text *.afdesign -diff -text +* text=auto eol=lf diff --git a/.github/workflows/check_copyright_year.yml b/.github/workflows/check_copyright_year.yml index 99799672f2ddeaade259d25c74c1cbc346636db6..8daea44a8347aa88f8fea0ea524951eee34c37b9 100644 --- a/.github/workflows/check_copyright_year.yml +++ b/.github/workflows/check_copyright_year.yml @@ -1,18 +1,18 @@ -name: Check copyright year - -on: - push: - branches: - - master - - develop - -jobs: - check_copyright_year: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3.2.0 - - run: | - if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then - echo "Please change copyright year!" - exit 1 - fi +name: Check copyright year + +on: + push: + branches: + - master + - develop + +jobs: + check_copyright_year: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3.2.0 + - run: | + if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then + echo "Please change copyright year!" + exit 1 + fi diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml index a999dc51e61d50feded70b3870cff6c5c453a8b2..09a2c33e0c83c6396004162abf68e2a3c2121327 100644 --- a/.github/workflows/docker-develop.yml +++ b/.github/workflows/docker-develop.yml @@ -15,7 +15,10 @@ jobs: - name: Check out the repo uses: actions/checkout@v3.3.0 - name: Set up Docker Buildx + id: buildx uses: docker/setup-buildx-action@v2.3.0 + with: + platforms: linux/amd64,linux/arm64 - name: Docker meta id: meta uses: docker/metadata-action@v4 @@ -27,10 +30,13 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push to Docker Hub - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: + builder: ${{ steps.buildx.outputs.name }} context: . push: true + platforms: ${{ steps.buildx.outputs.platforms }} + provenance: false tags: misskey/misskey:develop labels: develop cache-from: type=gha diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d7803ce3ec324a612be435dc3c9eb936a5e6ea79..a465d92eafb1dc7b0565a4151ba3bd2e7ca691c7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -13,6 +13,11 @@ jobs: steps: - name: Check out the repo uses: actions/checkout@v3.3.0 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2.3.0 + with: + platforms: linux/amd64,linux/arm64 - name: Docker meta id: meta uses: docker/metadata-action@v4 @@ -31,9 +36,14 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push to Docker Hub - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: + builder: ${{ steps.buildx.outputs.name }} context: . push: true + platforms: ${{ steps.buildx.outputs.platforms }} + provenance: false tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b88b97ab0c20dd1350e95610e3fc84ae1a6d7a94..5b1f1de4fe544e5bd891b1491629d13cb93e0cbf 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,54 +1,56 @@ -name: Lint - -on: - push: - branches: - - master - - develop - pull_request: - -jobs: - pnpm_install: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3.3.0 - with: - fetch-depth: 0 - submodules: true - - uses: pnpm/action-setup@v2 - with: - version: 7 - run_install: false - - uses: actions/setup-node@v3.6.0 - with: - node-version: 18.x - cache: 'pnpm' - - run: corepack enable - - run: pnpm i --frozen-lockfile - - lint: - needs: [pnpm_install] - runs-on: ubuntu-latest - continue-on-error: true - strategy: - matrix: - workspace: - - backend - - frontend - - sw - steps: - - uses: actions/checkout@v3.3.0 - with: - fetch-depth: 0 - submodules: true - - uses: pnpm/action-setup@v2 - with: - version: 7 - run_install: false - - uses: actions/setup-node@v3.6.0 - with: - node-version: 18.x - cache: 'pnpm' - - run: corepack enable - - run: pnpm i --frozen-lockfile - - run: pnpm --filter ${{ matrix.workspace }} run lint +name: Lint + +on: + push: + branches: + - master + - develop + pull_request: + +jobs: + pnpm_install: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3.3.0 + with: + fetch-depth: 0 + submodules: true + - uses: pnpm/action-setup@v2 + with: + version: 7 + run_install: false + - uses: actions/setup-node@v3.6.0 + with: + node-version: 18.x + cache: 'pnpm' + - run: corepack enable + - run: pnpm i --frozen-lockfile + + lint: + needs: [pnpm_install] + runs-on: ubuntu-latest + continue-on-error: true + strategy: + matrix: + workspace: + - backend + - frontend + - sw + lint: + - eslint + steps: + - uses: actions/checkout@v3.3.0 + with: + fetch-depth: 0 + submodules: true + - uses: pnpm/action-setup@v2 + with: + version: 7 + run_install: false + - uses: actions/setup-node@v3.6.0 + with: + node-version: 18.x + cache: 'pnpm' + - run: corepack enable + - run: pnpm i --frozen-lockfile + - run: pnpm --filter ${{ matrix.workspace }} run ${{ matrix.lint }} diff --git a/.vscode/settings.json b/.vscode/settings.json index b7e7b20c17dd327895b7c2810050d81f9142b47f..c94a34194e24a9e6c8085f1359178981d4013cd1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "search.exclude": { "**/node_modules": true - } + }, + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b659321622c6672a7a6b98917983859c8f1e9aa..22eb62537e5dbdebcc0cf6e32e658cf9287dff03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ You should also include the user name that made the change. --> +## 13.7.0 (2023/02/22) + +### Changes +- ãƒãƒ£ãƒƒãƒˆæ©Ÿèƒ½ãŒå‰Šé™¤ã•ã‚Œã¾ã—㟠+ +### Improvements +- Server: URLプレビュー(summaly)ã¯ãƒ—ãƒã‚シを通ã™ã‚ˆã†ã« +- Client: 2FAè¨å®šã®UIã‚’ã¾ã¨ã‚‚ã«ã—㟠+- ã‚»ã‚ュリティã‚ーã®åå‰ã‚’変更ã§ãるよã†ã« +- enhance(client): add quiz preset for play +- 広告開始時期をè¨å®šã§ãるよã†ã« +- ã¿ã¤ã‘ã‚‹ã§å…¬é–‹ãƒãƒ¼ãƒ«ä¸€è¦§ã¨ãã®ãƒ¡ãƒ³ãƒãƒ¼ã‚’閲覧ã§ãるよã†ã« +- enhance(client): MFMã®x3, x4ãŒå«ã¾ã‚Œã¦ã„ãŸã‚‰ãƒŽãƒ¼ãƒˆã‚’ãŸãŸã‚€ã‚ˆã†ã« +- enhance(client): make possible to reload page of window + +### Bugfixes +- ユーザー検索ダイアãƒã‚°ã§ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’絞ã£ã¦æ¤œç´¢ã§ããªã„å•é¡Œã‚’ä¿®æ£ +- fix(client): MkHeaderåŠã³ãƒ‡ãƒƒã‚ã®ã‚«ãƒ©ãƒ ã§ãƒãƒ£ãƒ³ãƒãƒ«ä¸€è¦§ã‚’é¸æŠžã—ãŸã¨ãã€æœ€å¤§5個ã¾ã§ã—ã‹è¡¨ç¤ºã•ã‚Œãªã„ +- 管ç†ç”»é¢ã®åºƒå‘Šã‚’10個以上見ãˆã‚‹ã‚ˆã†ã« +- Moderation note ãŒä¿å˜ã§ããªã„ +- ユーザーã®ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°æ¤œç´¢ãŒæ©Ÿèƒ½ã—ã¦ã„ãªã„ã®ã‚’ä¿®æ£ + ## 13.6.1 (2023/02/12) ### Improvements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de0a1abb459f8cf236aade32e07c0c262b3e3721..48d8a40deae21c778bcaacb0d0496ac7965c1b8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,7 +113,8 @@ command. ### 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. +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 diff --git a/Dockerfile b/Dockerfile index 0bfd24bd9afe6aea2e2ad3b0abc41a94e9444fe9..b439716bea40c177763e851fb5abc25761c7072a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ +# syntax = docker/dockerfile:1.4 + ARG NODE_VERSION=18.13.0-bullseye FROM node:${NODE_VERSION} AS builder @@ -14,16 +16,16 @@ RUN corepack enable WORKDIR /misskey -COPY ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] -COPY ["scripts", "./scripts"] -COPY ["packages/backend/package.json", "./packages/backend/"] -COPY ["packages/frontend/package.json", "./packages/frontend/"] -COPY ["packages/sw/package.json", "./packages/sw/"] +COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] +COPY --link ["scripts", "./scripts"] +COPY --link ["packages/backend/package.json", "./packages/backend/"] +COPY --link ["packages/frontend/package.json", "./packages/frontend/"] +COPY --link ["packages/sw/package.json", "./packages/sw/"] RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm i --frozen-lockfile --aggregate-output -COPY . ./ +COPY --link . ./ ARG NODE_ENV=production diff --git a/ROADMAP.md b/ROADMAP.md index c95bb8d92b1164453245e863ec08cdfed11c4e05..420f7287582a551adc1fb469631cd1d86c0361a8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,9 +5,9 @@ Also, the later tasks are more indefinite and are subject to change as developme ## (1) Improve maintainability \<current phase\> This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development. -- Make the number of type errors zero (backend) +- ~~Make the number of type errors zero (backend)~~ → Done âœ”ï¸ - Improve CI - - Fix tests + - ~~Fix tests~~ → Done âœ”ï¸ - Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986 - Add more tests - ~~May need to implement a mechanism that allows for DI~~ → Done âœ”ï¸ diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 8f31cf7fb46a6fb0a9e3509fc8a1c895ece7da0b..d151f1dd17355f47dc983cfc0ece96b27c7efb88 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,3 +1,4 @@ apiVersion: v2 name: misskey version: 0.0.0 +description: This chart is created for the purpose of previewing Pull Requests. Do not use this for production use. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000000000000000000000000000000000..7b28f690aef86ab975461ca69f939970e735cf04 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,5 @@ +coverage: + status: + project: + default: + only_pulls: true diff --git a/locales/de-DE.yml b/locales/de-DE.yml index fb80b92850cd7ac95f4a85745a70f512ab0d5c87..5173ea01b756676558894434dd94a1085aa2e093 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -103,6 +103,8 @@ renoted: "Renote getätigt." cantRenote: "Renote dieses Beitrags nicht möglich." cantReRenote: "Renote einer Renote nicht möglich." quote: "Zitieren" +inChannelRenote: "Kanal-interner Renote" +inChannelQuote: "Kanal-internes Zitat" pinnedNote: "Angeheftete Notiz" pinned: "Angeheftet" you: "Du" @@ -467,6 +469,8 @@ youHaveNoGroups: "Keine Gruppen vorhanden" joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene." noHistory: "Kein Verlauf gefunden" signinHistory: "Anmeldungsverlauf" +enableAdvancedMfm: "Erweitertes MFM aktivieren" +enableAnimatedMfm: "Animiertes MFM aktivieren" doing: "In Bearbeitung …" category: "Kategorie" tags: "Schlagwörter" @@ -945,6 +949,14 @@ selectFromPresets: "Aus Vorlagen wählen" achievements: "Errungenschaften" gotInvalidResponseError: "Ungültige Antwort des Servers" gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal." +thisPostMayBeAnnoying: "Dieser Beitrag stört eventuell andere Benutzer." +thisPostMayBeAnnoyingHome: "Zur Startseite schicken" +thisPostMayBeAnnoyingCancel: "Abbrechen" +thisPostMayBeAnnoyingIgnore: "Trotzdem schicken" +collapseRenotes: "Bereits gesehene Renotes verkürzt anzeigen" +internalServerError: "Serverinterner Fehler" +internalServerErrorDescription: "Im Server ist ein unerwarteter Fehler aufgetreten." +copyErrorInfo: "Fehlerdetails kopieren" _achievements: earnedAt: "Freigeschaltet am" _types: diff --git a/locales/en-US.yml b/locales/en-US.yml index beb5242b1c0e867f55119a5fa55d814b4091bfc4..e82772d870c693125013753bd95e9ccdbec54675 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -103,6 +103,8 @@ renoted: "Renoted." cantRenote: "This post can't be renoted." cantReRenote: "A renote can't be renoted." quote: "Quote" +inChannelRenote: "Channel-only Renote" +inChannelQuote: "Channel-only Quote" pinnedNote: "Pinned note" pinned: "Pin to profile" you: "You" @@ -468,7 +470,7 @@ joinOrCreateGroup: "Get invited to a group or create your own." noHistory: "No history available" signinHistory: "Login history" enableAdvancedMfm: "Enable advanced MFM" -enableAnimatedMfm: "Enable MFM with animation" +enableAnimatedMfm: "Enable animated MFM" doing: "Processing..." category: "Category" tags: "Tags" @@ -951,6 +953,10 @@ thisPostMayBeAnnoying: "This note may annoy others." thisPostMayBeAnnoyingHome: "Post to home timeline" thisPostMayBeAnnoyingCancel: "Cancel" thisPostMayBeAnnoyingIgnore: "Post anyway" +collapseRenotes: "Collapse renotes you've already seen" +internalServerError: "Internal Server Error" +internalServerErrorDescription: "The server has run into an unexpected error." +copyErrorInfo: "Copy error details" _achievements: earnedAt: "Unlocked at" _types: diff --git a/locales/it-IT.yml b/locales/it-IT.yml index fbee5d9719c8e8ce72ae361db447f7180cb701e7..1788756ca194ff393231f9fd8d96896dfc2e7197 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -84,12 +84,12 @@ error: "Errore" somethingHappened: "Si è verificato un problema" retry: "Riprova" pageLoadError: "Caricamento pagina non riuscito. " -pageLoadErrorDescription: "Questo viene normalmente causato dalla rete o dalla cache del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." +pageLoadErrorDescription: "Questo problema viene normalmente causato da errori di rete o dalla cache del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." serverIsDead: "Il server non risponde. Si prega di attendere e riprovare più tardi." youShouldUpgradeClient: "Per visualizzare la pagina è necessario aggiornare il client alla nuova versione e ricaricare." enterListName: "Nome della lista" privacy: "Privacy" -makeFollowManuallyApprove: "Richiedi di approvare i follower manualmente" +makeFollowManuallyApprove: "Approva i follower manualmente" defaultNoteVisibility: "Privacy predefinita delle note" follow: "Segui" followRequest: "Richiesta di follow" @@ -103,6 +103,8 @@ renoted: "Rinotato!" cantRenote: "È impossibile rinotare questa nota." cantReRenote: "È impossibile rinotare una Rinota." quote: "Cita" +inChannelRenote: "Rinota nel canale" +inChannelQuote: "Cita nel canale" pinnedNote: "Nota fissata" pinned: "Fissa sul profilo" you: "Tu" @@ -129,6 +131,7 @@ unblockConfirm: "Vuoi davvero sbloccare il profilo?" suspendConfirm: "Vuoi sospendere questo profilo?" unsuspendConfirm: "Vuoi revocare la sospensione si questo profilo?" selectList: "Seleziona una lista" +selectChannel: "Seleziona canale" selectAntenna: "Scegli un'antenna" selectWidget: "Seleziona il riquadro" editWidgets: "Modifica i riquadri" @@ -256,6 +259,8 @@ noMoreHistory: "Non c'è più cronologia da visualizzare" startMessaging: "Nuovo messaggio" nUsersRead: "Letto da {n} persone" agreeTo: "Sono d'accordo con {0}" +agreeBelow: "Accetto quanto riportato sotto" +basicNotesBeforeCreateAccount: "Note importanti" tos: "Termini di servizio" start: "Inizia!" home: "Home" @@ -464,6 +469,8 @@ youHaveNoGroups: "Nessun gruppo" joinOrCreateGroup: "Puoi creare il tuo gruppo o essere invitat@ a gruppi che già esistono." noHistory: "Nessuna cronologia" signinHistory: "Storico degli accessi al profilo" +enableAdvancedMfm: "Attiva MFM avanzati" +enableAnimatedMfm: "Attiva MFM animati" doing: "In corso..." category: "Categoria" tags: "Tag" @@ -860,6 +867,8 @@ failedToFetchAccountInformation: "Impossibile recuperare le informazioni sul pro rateLimitExceeded: "Superato il limite di velocità ." cropImage: "Ritaglio dell'immagine" cropImageAsk: "Si desidera ritagliare l'immagine?" +cropYes: "Ritaglia" +cropNo: "Non ritagliare" file: "Allegati" recentNHours: "Ultime {n} ore" recentNDays: "Ultimi {n} giorni" @@ -938,6 +947,16 @@ cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché preset: "Preimpostato" selectFromPresets: "Seleziona preimpostato" achievements: "Obiettivi raggiunti" +gotInvalidResponseError: "Risposta del server non valida" +gotInvalidResponseErrorDescription: "Il server potrebbe essere irraggiungibile o in manutenzione. Riprova più tardi." +thisPostMayBeAnnoying: "Questa nota potrebbe essere offensiva" +thisPostMayBeAnnoyingHome: "Pubblica sulla timeline principale" +thisPostMayBeAnnoyingCancel: "Annulla" +thisPostMayBeAnnoyingIgnore: "Pubblica lo stesso" +collapseRenotes: "Comprimi i Rinota già letti" +internalServerError: "Errore interno del server" +internalServerErrorDescription: "Si è verificato un errore imprevisto all'interno del server" +copyErrorInfo: "Copia le informazioni sull'errore" _achievements: earnedAt: "Data di conseguimento" _types: @@ -1526,12 +1545,15 @@ _permissions: "read:gallery-likes": "Visualizza i contenuti della galleria." "write:gallery-likes": "Manipolazione dei \"Mi piace\" della galleria." _auth: + shareAccessTitle: "Permessi dell'applicazione" shareAccess: "Vuoi autorizzare {name} ad accedere al tuo profilo?" shareAccessAsk: "Vuoi autorizzare questa App ad accedere al tuo profilo?" + permission: "{name} richiede i permessi seguenti" permissionAsk: "Questa app richiede le seguenti autorizzazioni:" pleaseGoBack: "Si prega di ritornare sulla app" callback: "Ritornando sulla app" denied: "Accesso negato" + pleaseLogin: "Per favore accedi al tuo account per cambiare i permessi dell'applicazione" _antennaSources: all: "Tutte le note" homeTimeline: "Note dagli utenti che segui" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 99ce82ce4c51600f5ca69d6732f35b1c6006420e..3faab45007a4dcaba969e0f8600fbad1f5cb84aa 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -392,17 +392,20 @@ userList: "リスト" about: "æƒ…å ±" aboutMisskey: "Misskeyã«ã¤ã„ã¦" administrator: "管ç†è€…" -token: "トークン" -twoStepAuthentication: "二段階èªè¨¼" +token: "確èªã‚³ãƒ¼ãƒ‰" +2fa: "二è¦ç´ èªè¨¼" +totp: "èªè¨¼ã‚¢ãƒ—リ" +totpDescription: "èªè¨¼ã‚¢ãƒ—リを使ã£ã¦ãƒ¯ãƒ³ã‚¿ã‚¤ãƒ パスワードを入力" moderator: "モデレーター" moderation: "モデレーション" nUsersMentioned: "{n}人ãŒæŠ•ç¨¿" +securityKeyAndPasskey: "ã‚»ã‚ュリティã‚ー・パスã‚ー" securityKey: "ã‚»ã‚ュリティã‚ー" -securityKeyName: "ã‚ーã®åå‰" -registerSecurityKey: "ã‚»ã‚ュリティã‚ーを登録ã™ã‚‹" lastUsed: "最後ã®ä½¿ç”¨" +lastUsedAt: "最後ã®ä½¿ç”¨: {t}" unregister: "登録を解除" -passwordLessLogin: "パスワード無ã—ãƒã‚°ã‚¤ãƒ³" +passwordLessLogin: "パスワードレスãƒã‚°ã‚¤ãƒ³" +passwordLessLoginDescription: "パスワードを使用ã›ãšã€ã‚»ã‚ュリティã‚ーやパスã‚ーãªã©ã®ã¿ã§ãƒã‚°ã‚¤ãƒ³ã—ã¾ã™" resetPassword: "パスワードをリセット" newPasswordIs: "æ–°ã—ã„パスワードã¯ã€Œ{password}ã€ã§ã™" reduceUiAnimation: "UIã®ã‚¢ãƒ‹ãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’減らã™" @@ -417,24 +420,15 @@ markAsReadAllTalkMessages: "ã™ã¹ã¦ã®ãƒãƒ£ãƒƒãƒˆã‚’æ—¢èªã«ã™ã‚‹" help: "ヘルプ" inputMessageHere: "ã“ã“ã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’入力" close: "é–‰ã˜ã‚‹" -group: "グループ" -groups: "グループ" -createGroup: "グループを作æˆ" -ownedGroups: "所有グループ" -joinedGroups: "å‚åŠ ã—ã¦ã„るグループ" invites: "招待" -groupName: "グループå" members: "メンãƒãƒ¼" transfer: "è²æ¸¡" -messagingWithUser: "ユーザーã¨ãƒãƒ£ãƒƒãƒˆ" -messagingWithGroup: "グループã§ãƒãƒ£ãƒƒãƒˆ" title: "タイトル" text: "テã‚スト" enable: "有効ã«ã™ã‚‹" next: "次" retype: "å†å…¥åŠ›" noteOf: "{user}ã®ãƒŽãƒ¼ãƒˆ" -inviteToGroup: "グループã«æ‹›å¾…" quoteAttached: "引用付ã" quoteQuestion: "引用ã¨ã—ã¦æ·»ä»˜ã—ã¾ã™ã‹ï¼Ÿ" noMessagesYet: "ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚ã‚Šã¾ã›ã‚“" @@ -456,17 +450,13 @@ passwordMatched: "一致ã—ã¾ã—ãŸ" passwordNotMatched: "一致ã—ã¦ã„ã¾ã›ã‚“" signinWith: "{x}ã§ãƒã‚°ã‚¤ãƒ³" signinFailed: "ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ãã ã•ã„。" -tapSecurityKey: "ã‚»ã‚ュリティã‚ーã«ã‚¿ãƒƒãƒ" or: "ã‚‚ã—ãã¯" language: "言語" uiLanguage: "UIã®è¡¨ç¤ºè¨€èªž" -groupInvited: "グループã«æ‹›å¾…ã•ã‚Œã¾ã—ãŸ" aboutX: "{x}ã«ã¤ã„ã¦" emojiStyle: "絵文å—ã®ã‚¹ã‚¿ã‚¤ãƒ«" native: "ãƒã‚¤ãƒ†ã‚£ãƒ–" disableDrawer: "メニューをドãƒãƒ¯ãƒ¼ã§è¡¨ç¤ºã—ãªã„" -youHaveNoGroups: "グループãŒã‚ã‚Šã¾ã›ã‚“" -joinOrCreateGroup: "æ—¢å˜ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‹›å¾…ã—ã¦ã‚‚らã†ã‹ã€æ–°ã—ãグループを作æˆã—ã¦ãã ã•ã„。" noHistory: "å±¥æ´ã¯ã‚ã‚Šã¾ã›ã‚“" signinHistory: "ãƒã‚°ã‚¤ãƒ³å±¥æ´" enableAdvancedMfm: "高度ãªMFMを有効ã«ã™ã‚‹" @@ -789,6 +779,7 @@ popularPosts: "人気ã®æŠ•ç¨¿" shareWithNote: "ノートã§å…±æœ‰" ads: "広告" expiration: "期é™" +startingperiod: "開始期間" memo: "メモ" priority: "優先度" high: "高" @@ -840,8 +831,6 @@ deleteAccountConfirm: "アカウントãŒå‰Šé™¤ã•ã‚Œã¾ã™ã€‚よã‚ã—ã„ã§ã™ incorrectPassword: "パスワードãŒé–“é•ã£ã¦ã„ã¾ã™ã€‚" voteConfirm: "「{choice}ã€ã«æŠ•ç¥¨ã—ã¾ã™ã‹ï¼Ÿ" hide: "éš ã™" -leaveGroup: "グループã‹ã‚‰æŠœã‘ã‚‹" -leaveGroupConfirm: "「{name}ã€ã‹ã‚‰æŠœã‘ã¾ã™ã‹ï¼Ÿ" useDrawerReactionPickerForMobile: "モãƒã‚¤ãƒ«ãƒ‡ãƒã‚¤ã‚¹ã®ã¨ãドãƒãƒ¯ãƒ¼ã§è¡¨ç¤º" welcomeBackWithName: "ãŠã‹ãˆã‚Šãªã•ã„ã€{name}ã•ã‚“" clickToFinishEmailVerification: "[{ok}]を押ã—ã¦ã€ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã®ç¢ºèªã‚’完了ã—ã¦ãã ã•ã„。" @@ -957,6 +946,9 @@ collapseRenotes: "見ãŸã“ã¨ã®ã‚ã‚‹Renoteã‚’çœç•¥ã—ã¦è¡¨ç¤º" internalServerError: "サーãƒãƒ¼å†…部エラー" internalServerErrorDescription: "サーãƒãƒ¼å†…部ã§äºˆæœŸã—ãªã„エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚" copyErrorInfo: "ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ãƒ¼" +joinThisServer: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«ç™»éŒ²ã™ã‚‹" +exploreOtherServers: "ä»–ã®ã‚µãƒ¼ãƒãƒ¼ã‚’探ã™" +letsLookAtTimeline: "タイムラインを見ã¦ã¿ã‚‹" _achievements: earnedAt: "ç²å¾—日時" @@ -1532,14 +1524,29 @@ _tutorial: _2fa: alreadyRegistered: "æ—¢ã«è¨å®šã¯å®Œäº†ã—ã¦ã„ã¾ã™ã€‚" - registerDevice: "デãƒã‚¤ã‚¹ã‚’登録" - registerKey: "ã‚ーを登録" + registerTOTP: "èªè¨¼ã‚¢ãƒ—リã®è¨å®šã‚’開始" + passwordToTOTP: "パスワードを入力ã—ã¦ãã ã•ã„" step1: "ã¾ãšã€{a}ã‚„{b}ãªã©ã®èªè¨¼ã‚¢ãƒ—リをãŠä½¿ã„ã®ãƒ‡ãƒã‚¤ã‚¹ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¾ã™ã€‚" step2: "次ã«ã€è¡¨ç¤ºã•ã‚Œã¦ã„ã‚‹QRコードをアプリã§ã‚¹ã‚ャンã—ã¾ã™ã€‚" - step2Url: "デスクトップアプリã§ã¯æ¬¡ã®URLを入力ã—ã¾ã™:" - step3: "アプリã«è¡¨ç¤ºã•ã‚Œã¦ã„るトークンを入力ã—ã¦å®Œäº†ã§ã™ã€‚" - step4: "ã“ã‚Œã‹ã‚‰ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ãƒˆãƒ¼ã‚¯ãƒ³ã‚’入力ã—ã¾ã™ã€‚" - securityKeyInfo: "FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚ュリティã‚ーもã—ãã¯ç«¯æœ«ã®æŒ‡ç´‹èªè¨¼ã‚„PINを使用ã—ã¦ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã‚ˆã†ã«è¨å®šã§ãã¾ã™ã€‚" + step2Click: "QRコードをクリックã™ã‚‹ã¨ã€ãŠä½¿ã„ã®ç«¯æœ«ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã‚‹èªè¨¼ã‚¢ãƒ—リやã‚ーリングã«ç™»éŒ²ã§ãã¾ã™ã€‚" + step2Url: "デスクトップアプリã§ã¯æ¬¡ã®URIを入力ã—ã¾ã™:" + step3Title: "確èªã‚³ãƒ¼ãƒ‰ã‚’入力" + step3: "アプリã«è¡¨ç¤ºã•ã‚Œã¦ã„る確èªã‚³ãƒ¼ãƒ‰ï¼ˆãƒˆãƒ¼ã‚¯ãƒ³ï¼‰ã‚’入力ã—ã¦å®Œäº†ã§ã™ã€‚" + step4: "ã“ã‚Œã‹ã‚‰ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã¨ãã‚‚ã€åŒã˜ã‚ˆã†ã«ç¢ºèªã‚³ãƒ¼ãƒ‰ã‚’入力ã—ã¾ã™ã€‚" + securityKeyNotSupported: "ãŠä½¿ã„ã®ãƒ–ラウザã¯ã‚»ã‚ュリティã‚ーã«å¯¾å¿œã—ã¦ã„ã¾ã›ã‚“。" + registerTOTPBeforeKey: "ã‚»ã‚ュリティã‚ー・パスã‚ーを登録ã™ã‚‹ã«ã¯ã€ã¾ãšèªè¨¼ã‚¢ãƒ—リã®è¨å®šã‚’è¡Œãªã£ã¦ãã ã•ã„。" + securityKeyInfo: "FIDO2をサãƒãƒ¼ãƒˆã™ã‚‹ãƒãƒ¼ãƒ‰ã‚¦ã‚§ã‚¢ã‚»ã‚ュリティã‚ーã€ç«¯æœ«ã®ç”Ÿä½“èªè¨¼ã‚„PINãƒãƒƒã‚¯ã€ãƒ‘スã‚ーã¨ã„ã£ãŸã€WebAuthnç”±æ¥ã®éµã‚’登録ã—ã¾ã™ã€‚" + chromePasskeyNotSupported: "Chromeã®ãƒ‘スã‚ーã¯ç¾åœ¨ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。" + registerSecurityKey: "ã‚»ã‚ュリティã‚ー・パスã‚ーを登録ã™ã‚‹" + securityKeyName: "ã‚ーã®åå‰ã‚’入力" + tapSecurityKey: "ブラウザã®æŒ‡ç¤ºã«å¾“ã„ã€ã‚»ã‚ュリティã‚ーやパスã‚ーを登録ã—ã¦ãã ã•ã„" + removeKey: "ã‚»ã‚ュリティã‚ーを削除" + removeKeyConfirm: "{name}を削除ã—ã¾ã™ã‹ï¼Ÿ" + whyTOTPOnlyRenew: "ã‚»ã‚ュリティã‚ーãŒç™»éŒ²ã•ã‚Œã¦ã„ã‚‹å ´åˆã€èªè¨¼ã‚¢ãƒ—リã®è¨å®šã¯è§£é™¤ã§ãã¾ã›ã‚“。" + renewTOTP: "èªè¨¼ã‚¢ãƒ—リをå†è¨å®š" + renewTOTPConfirm: "今ã¾ã§ã®èªè¨¼ã‚¢ãƒ—リã®ç¢ºèªã‚³ãƒ¼ãƒ‰ã¯ä½¿ç”¨ã§ããªããªã‚Šã¾ã™" + renewTOTPOk: "å†è¨å®šã™ã‚‹" + renewTOTPCancel: "ã‚„ã‚ã¦ãŠã" _permissions: "read:account": "アカウントã®æƒ…å ±ã‚’è¦‹ã‚‹" @@ -1591,7 +1598,6 @@ _antennaSources: homeTimeline: "フォãƒãƒ¼ã—ã¦ã„るユーザーã®ãƒŽãƒ¼ãƒˆ" users: "指定ã—ãŸä¸€äººã¾ãŸã¯è¤‡æ•°ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" userList: "指定ã—ãŸãƒªã‚¹ãƒˆã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" - userGroup: "指定ã—ãŸã‚°ãƒ«ãƒ¼ãƒ—ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆ" _weekday: sunday: "日曜日" @@ -1821,12 +1827,9 @@ _notification: youGotReply: "{name}ã‹ã‚‰ã®ãƒªãƒ—ライ" youGotQuote: "{name}ã«ã‚ˆã‚‹å¼•ç”¨" youRenoted: "{name}ãŒRenoteã—ã¾ã—ãŸ" - youGotMessagingMessageFromUser: "{name}ã‹ã‚‰ã®ãƒãƒ£ãƒƒãƒˆãŒã‚ã‚Šã¾ã™" - youGotMessagingMessageFromGroup: "{name}ã®ãƒãƒ£ãƒƒãƒˆãŒã‚ã‚Šã¾ã™" youWereFollowed: "フォãƒãƒ¼ã•ã‚Œã¾ã—ãŸ" youReceivedFollowRequest: "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ¥ã¾ã—ãŸ" yourFollowRequestAccepted: "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‰¿èªã•ã‚Œã¾ã—ãŸ" - youWereInvitedToGroup: "{userName}ãŒã‚ãªãŸã‚’グループã«æ‹›å¾…ã—ã¾ã—ãŸ" pollEnded: "アンケートã®çµæžœãŒå‡ºã¾ã—ãŸ" unreadAntennaNote: "アンテナ {name}" emptyPushNotificationMessage: "プッシュ通知ã®æ›´æ–°ã‚’ã—ã¾ã—ãŸ" @@ -1843,7 +1846,6 @@ _notification: pollEnded: "アンケートãŒçµ‚了" receiveFollowRequest: "フォãƒãƒ¼ç”³è«‹ã‚’å—ã‘å–ã£ãŸ" followRequestAccepted: "フォãƒãƒ¼ãŒå—ç†ã•ã‚ŒãŸ" - groupInvited: "グループã«æ‹›å¾…ã•ã‚ŒãŸ" app: "連æºã‚¢ãƒ—リã‹ã‚‰ã®é€šçŸ¥" _actions: @@ -1879,3 +1881,7 @@ _deck: channel: "ãƒãƒ£ãƒ³ãƒãƒ«" mentions: "ã‚ãªãŸå®›ã¦" direct: "ダイレクト" + +_dialog: + charactersExceeded: "最大文å—数を超ãˆã¦ã„ã¾ã™ï¼ ç¾åœ¨ {current} / åˆ¶é™ {max}" + charactersBelow: "最å°æ–‡å—数を下回ã£ã¦ã„ã¾ã™ï¼ ç¾åœ¨ {current} / åˆ¶é™ {min}" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index cbbd928b2c3dad83c0354386730d0020f463fec9..6b394a80680be203be202134ac9972f0bc40c98c 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -103,6 +103,8 @@ renoted: "Renoteã—ãŸã§ã€‚" cantRenote: "ã“ã®æŠ•ç¨¿ã¯Renoteã§ãã¸ã‚“らã—ã„。" cantReRenote: "Renote自体ã¯Renoteã§ãã¸ã‚“ã§ã€‚" quote: "引用" +inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…Renote" +inChannelQuote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…引用" pinnedNote: "ピン留ã‚ã•ã‚Œã¨ã‚‹ãƒŽãƒ¼ãƒˆ" pinned: "ピン留ã‚ã—ã¨ã" you: "ã‚ã‚“ãŸ" @@ -948,35 +950,168 @@ achievements: "実績" gotInvalidResponseError: "サーãƒãƒ¼é»™ã£ã¨ã‚‹ã‚ã€çŸ¥ã‚‰ã‚“ã‘ã©" gotInvalidResponseErrorDescription: "サーãƒãƒ¼ã„ã¾æ—¥æ›œæ—¥ã€‚ã¾ãŸãã¦æœˆæ›œæ—¥ã€‚" thisPostMayBeAnnoying: "ã“ã®æŠ•ç¨¿ã¯è¿·æƒ‘ã‹ã‚‚ã—らんã§ã€‚" +thisPostMayBeAnnoyingHome: "ホームã«æŠ•ç¨¿" +thisPostMayBeAnnoyingCancel: "ã‚„ã‚ã¨ã" +thisPostMayBeAnnoyingIgnore: "ã“ã®ã¾ã¾æŠ•ç¨¿" collapseRenotes: "見ãŸã“ã¨ã‚ã‚‹Renoteã¯çœç•¥ã‚„ã§" +internalServerError: "サーãƒãƒ¼å†…部エラー" +internalServerErrorDescription: "サーãƒãƒ¼å†…部ã§ã‚ˆã†åˆ†ã‹ã‚‰ã‚“エラーやã‚" +copyErrorInfo: "ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ãƒ¼" _achievements: earnedAt: "è²°ã£ãŸæ—¥ãƒ" _types: _notes1: title: "ã¾ã„ã©ï¼" description: "åˆã‚ã¦ãƒŽãƒ¼ãƒˆæŠ•ç¨¿ã—ãŸã£ãŸ" + flavor: "Misskeyを楽ã—ã‚“ã§ãªï½ž" _notes10: title: "ノートã®å¤©ä¿å±±" + description: "ノートを10回投稿ã—ãŸ" _notes100: title: "ノートã®çœŸç”°å±±" + description: "ノートを100回投稿ã—ãŸ" _notes500: title: "ノートã®ç”Ÿé§’å±±" + description: "ノートを500回投稿ã—ãŸ" + _notes1000: + title: "ノートã®å±±" + description: "ノートを1,000回投稿ã—ãŸ" _notes5000: title: "箕é¢ã®æ»ã‹ã‚‰ãƒŽãƒ¼ãƒˆ" + description: "ノートを5,000回投稿ã—ãŸ" + _notes10000: + title: "スーパーノート" + description: "ノートを10,000回投稿ã—ãŸ" + _notes20000: + title: "ニードモアノート" + description: "ノートを20,000回投稿ã—ãŸ" + _notes30000: + title: "ノートノートノート" + description: "ノートを30,000回投稿ã—ãŸ" + _notes40000: + title: "ãƒŽãƒ¼ãƒˆå·¥å ´" + description: "ノートを40,000回投稿ã—ãŸ" + _notes50000: + title: "ノートã®æƒ‘星" + description: "ノートを50,000回投稿ã—ãŸ" + _notes60000: + title: "ノートクエーサー" + description: "ノートを60,000回投稿ã—ãŸ" + _notes70000: + title: "ブラックノートホール" + description: "ノートを70,000回投稿ã—ãŸ" + _notes80000: + title: "ノートギャラクシー" + description: "ノートを80,000回投稿ã—ãŸ" + _notes90000: + title: "ノートãƒãƒ¼ã‚¹" + description: "ノートを90,000回投稿ã—ãŸ" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "ノートを100,000回投稿ã—ãŸ" + flavor: "ãã‚“ãªã«æ›¸ãã“ã¨ã‚ã‚‹ã‚“ã‹ï¼Ÿ" _login3: + title: "ãƒ“ã‚®ãƒŠãƒ¼â… " + description: "通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ3æ—¥" flavor: "今日ã‹ã‚‰ãƒ¯ã‚·ã¯ãƒŸã‚¹ã‚ストやã§" + _login7: + title: "ビギナーⅡ" + description: "通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ7æ—¥" + flavor: "慣れã¦ããŸã‚“ã¡ã‚ƒã†ï¼Ÿ" + _login15: + title: "ビギナーⅢ" + description: "通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ15æ—¥" + _login30: + title: "ミスã‚ã‚¹ãƒˆâ… " + description: "通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ30æ—¥" + _login60: + title: "ミスã‚ストⅡ" + description: "通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ60æ—¥" + _login100: + title: "ミスã‚ストⅢ" + description: "通算ãƒã‚°ã‚¤ãƒ³æ—¥æ•°ãŒ100æ—¥" + flavor: "ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€ãƒŸã‚¹ã‚ストã«ã¤ã" + _login200: + title: "å¸¸é€£â… " + _followers500: + title: "基地局" + description: "フォãƒãƒ¯ãƒ¼ãŒ500人を超ã—ãŸ" + _followers1000: + title: "インフルエンサー" + description: "フォãƒãƒ¯ãƒ¼ãŒ1,000人を超ã—ãŸ" + _collectAchievements30: + title: "実績コレクター" + description: "実績を30個以上ç²å¾—ã—ãŸ" + _viewAchievements3min: + title: "実績好ã" + description: "実績一覧を3分以上眺ã‚続ã‘ãŸ" _iLoveMisskey: title: "Misskey好ãã‚„ãã‚“" + description: "\"I ⤠#Misskey\"を投稿ã—ãŸ" + flavor: "Misskeyを使ã£ã¦ãã‚Œã¦ã‚ã‚ŠãŒã¨ã†ãªï½žã€€by 開発ãƒãƒ¼ãƒ " _foundTreasure: title: "ãªã‚“ã§ã‚‚鑑定団" + description: "éš ã•ã‚ŒãŸãŠå®ã‚’発見ã—ãŸ" _client30min: title: "ãã‚“ã" + description: "クライアントを起動ã—ã¦ã‹ã‚‰30分以上経éŽã—ãŸ" _noteDeletedWithin1min: title: "*ãŠãŠã£ã¨ï¼Š" + description: "投稿ã—ã¦ã‹ã‚‰1分以内ã«ãã®æŠ•ç¨¿ã‚’消ã—ãŸ" + _postedAtLateNight: + title: "夜行性" + description: "深夜ã«ãƒŽãƒ¼ãƒˆã‚’投稿ã—ãŸ" + flavor: "ãã‚ãã‚å¯ã‚ˆã‹" + _postedAt0min0sec: + title: "æ™‚å ±" + description: "0分0秒ã«ãƒŽãƒ¼ãƒˆã‚’投稿ã—ãŸ" + flavor: "ãƒãƒƒ ãƒãƒƒ ãƒãƒƒ ピーン" + _selfQuote: + title: "自己言åŠ" + description: "自分ã®ãƒŽãƒ¼ãƒˆã‚’引用ã—ãŸ" + _htl20npm: + title: "æµã‚Œã‚‹TL" + description: "ホームタイムラインã®æµé€ŸãŒ20npmを超ã™" + _viewInstanceChart: + title: "アナリスト" + description: "インスタンスã®ãƒãƒ£ãƒ¼ãƒˆã‚’表示ã—ãŸ" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "スクラッãƒãƒ‘ッド㧠hello worldを出力ã—ãŸ" _open3windows: title: "マド開ã‘ã™ãŽ" + description: "ウィンドウを3ã¤ä»¥ä¸Šé–‹ã„ãŸçŠ¶æ…‹ã«ã—ãŸ" _driveFolderCircularReference: title: "環状線" + description: "ドライブã®ãƒ•ã‚©ãƒ«ãƒ€ã‚’å†å¸°çš„ãªå…¥ã‚Œåã«ã—よã†ã¨ã—ãŸ" + _reactWithoutRead: + title: "ã¡ã‚ƒã‚“ã¨èªã‚“ã ã‚“ã‹ï¼Ÿ" + description: "100æ–‡å—以上ã®ãƒ†ã‚ストをå«ã‚€ãƒŽãƒ¼ãƒˆã«æŠ•ç¨¿ã•ã‚Œã¦ã‹ã‚‰3秒以内ã«ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã—ãŸ" + _clickedClickHere: + title: "ã“ã“をクリック" + description: "ã“ã“をクリックã—ãŸ" + _justPlainLucky: + title: "å˜ãªã‚‹ãƒ©ãƒƒã‚ー" + description: "10秒ã”ã¨ã«0.005ï¼…ã®ç¢ºçŽ‡ã§ç²å¾—" + _setNameToSyuilo: + title: "神様コンプレックス" + description: "åå‰ã‚’ syuilo ã«è¨å®šã—ãŸ" + _passedSinceAccountCreated1: + title: "一周年" + description: "アカウント作æˆã‹ã‚‰1年経éŽã—ãŸ" + _passedSinceAccountCreated2: + title: "二周年" + description: "アカウント作æˆã‹ã‚‰2年経éŽã—ãŸ" + _passedSinceAccountCreated3: + title: "三周年" + description: "アカウント作æˆã‹ã‚‰3年経éŽã—ãŸ" + _loggedInOnBirthday: + title: "ãƒãƒƒãƒ”ーãƒãƒ¼ã‚¹ãƒ‡ãƒ¼ï¼" + description: "誕生日ã«ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + _loggedInOnNewYearsDay: + title: "ã‚ã‘ã¾ã—ã¦ãŠã‚ã§ã¨ã†ã”ã–ã„ã¾ã™ï¼" + description: "元旦ã«ãƒã‚°ã‚¤ãƒ³ã—ãŸ" + flavor: "今年も弊インスタンスをよã‚ã—ããŠé¡˜ã„ã—ã¾ã™" _role: new: "ãƒãƒ¼ãƒ«ã®ä½œæˆ" edit: "ãƒãƒ¼ãƒ«ã®ç·¨é›†" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 27882e50f6cdc212fcb7bfc33ab3f56516207eaf..6ada5dc206db7215701102a996b7c2e22038e5f3 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1113,6 +1113,8 @@ _achievements: _loggedInOnNewYearsDay: title: "З Ðовим роком!" description: "Увійшли в перший день року" + _cookieClicked: + flavor: "Чекайте, це вірний Ñайт?" _brainDiver: title: "Brain Diver" description: "Відправити поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° \"Brain Diver\"" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 71ca55d9e5f42362e291dea6f64fb368ec7cc179..8ee699ce32d23608537f0175e97126503cbedd23 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -103,6 +103,8 @@ renoted: "已转å‘。" cantRenote: "è¯¥å¸–æ— æ³•è½¬å‘。" cantReRenote: "转å‘æ— æ³•è¢«å†æ¬¡è½¬å‘。" quote: "引用" +inChannelRenote: "在频é“内转å‘" +inChannelQuote: "在频é“内引用" pinnedNote: "已置顶的帖å" pinned: "置顶" you: "您" @@ -951,6 +953,10 @@ thisPostMayBeAnnoying: "这个帖åå¯èƒ½ä¼šè®©å…¶ä»–人感到困扰。" thisPostMayBeAnnoyingHome: "å‘到首页" thisPostMayBeAnnoyingCancel: "å–消" thisPostMayBeAnnoyingIgnore: "å°±è¿™æ ·å‘布" +collapseRenotes: "çœç•¥æ˜¾ç¤ºå·²ç»çœ‹è¿‡çš„转å‘内容" +internalServerError: "内部æœåŠ¡å™¨é”™è¯¯" +internalServerErrorDescription: "内部æœåŠ¡å™¨å‘生了预期外的错误" +copyErrorInfo: "å¤åˆ¶é”™è¯¯ä¿¡æ¯" _achievements: earnedAt: "è¾¾æˆæ—¶é—´" _types: diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index f99ca91e63635bf83ab6138d2091e820724e7631..b0f9a631daf379a7898e37150e68a13bd1436f61 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -103,6 +103,8 @@ renoted: "轉傳æˆåŠŸ" cantRenote: "無法轉發æ¤è²¼æ–‡ã€‚" cantReRenote: "無法轉傳之å‰å·²ç¶“轉傳éŽçš„內容。" quote: "引用" +inChannelRenote: "åœ¨é »é“內轉發" +inChannelQuote: "åœ¨é »é“內引用" pinnedNote: "å·²ç½®é ‚çš„è²¼æ–‡" pinned: "ç½®é ‚" you: "您" @@ -952,6 +954,9 @@ thisPostMayBeAnnoyingHome: "發布到首é " thisPostMayBeAnnoyingCancel: "退出" thisPostMayBeAnnoyingIgnore: "直接發布貼文" collapseRenotes: "çœç•¥é¡¯ç¤ºå·²çœ‹éŽçš„轉發貼文" +internalServerError: "內部伺æœå™¨éŒ¯èª¤" +internalServerErrorDescription: "內部伺æœå™¨ç™¼ç”Ÿäº†éžé 期的錯誤。" +copyErrorInfo: "複製錯誤資訊" _achievements: earnedAt: "ç²å¾—日期" _types: diff --git a/package.json b/package.json index 5a6b807d66ed382ed1d862e32fa21a84fe095d13..a251e062abb5861184501fb51414ad211461fab1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.6.1", + "version": "13.7.0", "codename": "nasubi", "repository": { "type": "git", @@ -54,12 +54,12 @@ "devDependencies": { "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/eslint-plugin": "5.52.0", + "@typescript-eslint/parser": "5.52.0", "cross-env": "7.0.3", - "cypress": "12.5.1", - "eslint": "8.33.0", - "start-server-and-test": "1.15.3" + "cypress": "12.6.0", + "eslint": "8.34.0", + "start-server-and-test": "1.15.4" }, "optionalDependencies": { "@tensorflow/tfjs-core": "4.2.0" diff --git a/packages/backend/jest-resolver.cjs b/packages/backend/jest-resolver.cjs deleted file mode 100644 index 4424b800dc80220b39b46e0053051768eece7c99..0000000000000000000000000000000000000000 --- a/packages/backend/jest-resolver.cjs +++ /dev/null @@ -1,14 +0,0 @@ -// https://github.com/facebook/jest/issues/12270#issuecomment-1194746382 - -const nativeModule = require('node:module'); - -function resolver(module, options) { - const { basedir, defaultResolver } = options; - try { - return defaultResolver(module, options); - } catch (error) { - return nativeModule.createRequire(basedir).resolve(module); - } -} - -module.exports = resolver; diff --git a/packages/backend/jest.config.cjs b/packages/backend/jest.config.cjs index f0a3dc16c29440d86221f0ed4c570a5ba245fe92..2f11f6a3e9a663b68ba7bfd3af736fe59bc52893 100644 --- a/packages/backend/jest.config.cjs +++ b/packages/backend/jest.config.cjs @@ -83,7 +83,14 @@ module.exports = { // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module moduleNameMapper: { - "^@/(.*?).js": "<rootDir>/src/$1.ts", + // Do not resolve .wasm.js to .wasm by the rule below + '^(.+)\\.wasm\\.js$': '$1.wasm.js', + // SWC converts @/foo/bar.js to `../../src/foo/bar.js`, and then this rule + // converts it again to `../../src/foo/bar` which then can be resolved to + // `.ts` files. + // See https://github.com/swc-project/jest/issues/64#issuecomment-1029753225 + // TODO: Use `--allowImportingTsExtensions` on TypeScript 5.0 so that we can + // directly import `.ts` files without this hack. '^(\\.{1,2}/.*)\\.js$': '$1', }, @@ -112,7 +119,7 @@ module.exports = { // resetModules: false, // A path to a custom resolver - resolver: './jest-resolver.cjs', + // resolver: './jest-resolver.cjs', // Automatically restore mock state between every test restoreMocks: true, diff --git a/packages/backend/migration/1676434944993-drop-group.js b/packages/backend/migration/1676434944993-drop-group.js new file mode 100644 index 0000000000000000000000000000000000000000..bdf3c1034ebdc07f441b1f6dca6d30212325c800 --- /dev/null +++ b/packages/backend/migration/1676434944993-drop-group.js @@ -0,0 +1,35 @@ +export class dropGroup1676434944993 { + name = 'dropGroup1676434944993' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`); + await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`); + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`); + await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`); + await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum" AS ENUM('home', 'all', 'users', 'list')`); + await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum" USING "src"::"text"::"public"."antenna_src_enum"`); + await queryRunner.query(`DROP TYPE "public"."antenna_src_enum_old"`); + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app')`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow","receiveFollowRequest"]'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow", "receiveFollowRequest", "groupInvited"]'`); + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`); + await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list', 'group')`); + await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum_old" USING "src"::"text"::"public"."antenna_src_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."antenna_src_enum"`); + await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum_old" RENAME TO "antenna_src_enum"`); + await queryRunner.query(`ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`); + await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`); + await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } +} diff --git a/packages/backend/migration/1676438468213-ad3.js b/packages/backend/migration/1676438468213-ad3.js new file mode 100644 index 0000000000000000000000000000000000000000..bf1f384adc94d6399376074f46d0306748fc4fc1 --- /dev/null +++ b/packages/backend/migration/1676438468213-ad3.js @@ -0,0 +1,9 @@ +export class ad1676438468213 { + name = 'ad1676438468213'; + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "ad" ADD "startsAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "startsAt"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 0351e02e0ffede4a62cf703440f7d8a008b83e86..7ea1ec3c7f31c97fd545510fa1c8e471e251c4dc 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -11,7 +11,9 @@ "watch:swc": "swc src -d built -D -w", "build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json", "watch": "node watch.mjs", - "lint": "tsc --noEmit && eslint --quiet \"src/**/*.ts\"", + "typecheck": "tsc --noEmit", + "eslint": "eslint --quiet \"src/**/*.ts\"", + "lint": "pnpm typecheck && pnpm eslint", "jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand", "jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand", "jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache", @@ -23,30 +25,30 @@ "@tensorflow/tfjs-node": "4.2.0" }, "dependencies": { - "@bull-board/api": "4.11.1", - "@bull-board/fastify": "4.11.1", - "@bull-board/ui": "4.11.1", + "@bull-board/api": "4.12.1", + "@bull-board/fastify": "4.12.1", + "@bull-board/ui": "4.12.1", "@discordapp/twemoji": "14.0.2", "@fastify/accepts": "4.1.0", "@fastify/cookie": "8.3.0", "@fastify/cors": "8.2.0", "@fastify/http-proxy": "8.4.0", - "@fastify/multipart": "7.4.0", - "@fastify/static": "6.8.0", + "@fastify/multipart": "7.4.1", + "@fastify/static": "6.9.0", "@fastify/view": "7.4.1", - "@nestjs/common": "9.3.7", - "@nestjs/core": "9.3.7", - "@nestjs/testing": "9.3.7", + "@nestjs/common": "9.3.9", + "@nestjs/core": "9.3.9", + "@nestjs/testing": "9.3.9", "@peertube/http-signature": "1.7.0", "@sinonjs/fake-timers": "10.0.2", "accepts": "1.3.8", "ajv": "8.12.0", "archiver": "5.3.1", "autwh": "0.1.0", - "aws-sdk": "2.1295.0", + "aws-sdk": "2.1318.0", "bcryptjs": "2.4.3", - "blurhash": "2.0.4", - "bull": "4.10.3", + "blurhash": "2.0.5", + "bull": "4.10.4", "cacheable-lookup": "6.1.0", "cbor": "8.1.0", "chalk": "5.2.0", @@ -58,12 +60,13 @@ "date-fns": "2.29.3", "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", - "fastify": "4.12.0", + "fastify": "4.13.0", "feed": "4.2.2", - "file-type": "18.2.0", + "file-type": "18.2.1", "fluent-ffmpeg": "2.1.2", "form-data": "4.0.0", "got": "12.5.3", + "happy-dom": "^8.7.0", "hpagent": "1.2.0", "ioredis": "4.28.5", "ip-cidr": "3.1.0", @@ -83,6 +86,7 @@ "nsfwjs": "2.4.2", "oauth": "0.10.0", "os-utils": "0.0.14", + "otpauth": "^9.0.2", "parse5": "7.1.2", "pg": "8.9.0", "private-ip": "3.0.0", @@ -102,15 +106,14 @@ "rss-parser": "3.12.0", "rxjs": "7.8.0", "s-age": "1.1.2", - "sanitize-html": "2.9.0", + "sanitize-html": "2.10.0", "seedrandom": "3.0.5", "semver": "7.3.8", "sharp": "0.31.3", - "speakeasy": "2.0.0", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", - "summaly": "2.7.0", - "systeminformation": "5.17.8", + "summaly": "github:misskey-dev/summaly", + "systeminformation": "5.17.9", "tinycolor2": "1.6.0", "tmp": "0.2.1", "tsc-alias": "1.8.2", @@ -124,14 +127,14 @@ "vary": "1.1.2", "web-push": "3.5.0", "websocket": "1.0.34", - "ws": "8.12.0", + "ws": "8.12.1", "xev": "3.0.2" }, "devDependencies": { - "@jest/globals": "29.4.2", + "@jest/globals": "29.4.3", "@redocly/openapi-core": "1.0.0-beta.123", - "@swc/cli": "0.1.61", - "@swc/core": "1.3.34", + "@swc/cli": "0.1.62", + "@swc/core": "1.3.35", "@swc/jest": "0.2.24", "@types/accepts": "1.3.5", "@types/archiver": "5.3.1", @@ -149,7 +152,7 @@ "@types/jsonld": "1.5.8", "@types/jsrsasign": "10.5.5", "@types/mime-types": "2.1.1", - "@types/node": "18.13.0", + "@types/node": "18.14.0", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.7", "@types/oauth": "0.9.1", @@ -165,7 +168,6 @@ "@types/semver": "7.3.13", "@types/sharp": "0.31.1", "@types/sinonjs__fake-timers": "8.1.2", - "@types/speakeasy": "2.0.7", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", "@types/unzipper": "0.10.5", @@ -174,13 +176,13 @@ "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/eslint-plugin": "5.52.0", + "@typescript-eslint/parser": "5.52.0", "cross-env": "7.0.3", - "eslint": "8.33.0", + "eslint": "8.34.0", "eslint-plugin-import": "2.27.5", "execa": "6.1.0", - "jest": "29.4.2", - "jest-mock": "29.4.2" + "jest": "29.4.3", + "jest-mock": "29.4.3" } } diff --git a/packages/backend/src/@types/redis-lock.d.ts b/packages/backend/src/@types/redis-lock.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9242656a9888dffdafe2c8cd2024ef9fb37b825c --- /dev/null +++ b/packages/backend/src/@types/redis-lock.d.ts @@ -0,0 +1,8 @@ +declare module 'redis-lock' { + import type Redis from 'ioredis'; + + type Lock = (lockName: string, timeout?: number, taskToPerform?: () => Promise<void>) => void; + function redisLock(client: Redis.Redis, retryDelay: number): Lock; + + export = redisLock; +} diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 5f6dfca0ca96a6fac255b94cc9209d3b8a1a8a77..d8ba7b169d5894f520ff0c6bc2a5c213454b01a4 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -32,7 +32,7 @@ export class AccountUpdateService { // フォãƒãƒ¯ãƒ¼ãŒãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã¤æŠ•ç¨¿è€…ãŒãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ãªã‚‰Updateã‚’é…ä¿¡ if (this.userEntityService.isLocalUser(user)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user)); + const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user)); this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index a71327e9474535fc2f8ed8cbe694dc9d876c3bf4..0e7254593478e5a7492d49d667fe53efb3d05a10 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -12,7 +12,7 @@ import { PushNotificationService } from '@/core/PushNotificationService.js'; import * as Acct from '@/misc/acct.js'; import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; -import type { MutingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { MutingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserListJoiningsRepository } from '@/models/index.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { StreamMessages } from '@/server/api/stream/types.js'; @@ -39,9 +39,6 @@ export class AntennaService implements OnApplicationShutdown { @Inject(DI.antennasRepository) private antennasRepository: AntennasRepository, - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: UserListJoiningsRepository, @@ -160,14 +157,6 @@ export class AntennaService implements OnApplicationShutdown { })).map(x => x.userId); if (!listUsers.includes(note.userId)) return false; - } else if (antenna.src === 'group') { - const joining = await this.userGroupJoiningsRepository.findOneByOrFail({ id: antenna.userGroupJoiningId! }); - - const groupUsers = (await this.userGroupJoiningsRepository.findBy({ - userGroupId: joining.userGroupId, - })).map(x => x.userId); - - if (!groupUsers.includes(note.userId)) return false; } else if (antenna.src === 'users') { const accts = antenna.users.map(x => { const { username, host } = Acct.parse(x); diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 5f3072a4156fe383b1eb0329e9319f05f5282ce0..ee179b7f01f6cd2de6f3218dbaa9043b6852b14e 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -12,7 +12,7 @@ const retryDelay = 100; @Injectable() export class AppLockService { - private lock: (key: string, timeout?: number) => Promise<() => void>; + private lock: (key: string, timeout?: number, _?: (() => Promise<void>) | undefined) => Promise<() => void>; constructor( @Inject(DI.redis) diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 6a6d1b864a47905097a57f2da1d9dccb811a3881..491d8ab1131e67891e9f2d190f7ffcd3c9da43a3 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -1,5 +1,4 @@ import { Module } from '@nestjs/common'; -import { DI } from '../di-symbols.js'; import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; import { AntennaService } from './AntennaService.js'; @@ -22,7 +21,6 @@ import { IdService } from './IdService.js'; import { ImageProcessingService } from './ImageProcessingService.js'; import { InstanceActorService } from './InstanceActorService.js'; import { InternalStorageService } from './InternalStorageService.js'; -import { MessagingService } from './MessagingService.js'; import { MetaService } from './MetaService.js'; import { MfmService } from './MfmService.js'; import { ModerationLogService } from './ModerationLogService.js'; @@ -82,7 +80,6 @@ import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js'; import { HashtagEntityService } from './entities/HashtagEntityService.js'; import { InstanceEntityService } from './entities/InstanceEntityService.js'; -import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js'; import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js'; import { MutingEntityService } from './entities/MutingEntityService.js'; import { NoteEntityService } from './entities/NoteEntityService.js'; @@ -93,8 +90,6 @@ import { PageEntityService } from './entities/PageEntityService.js'; import { PageLikeEntityService } from './entities/PageLikeEntityService.js'; import { SigninEntityService } from './entities/SigninEntityService.js'; import { UserEntityService } from './entities/UserEntityService.js'; -import { UserGroupEntityService } from './entities/UserGroupEntityService.js'; -import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js'; import { UserListEntityService } from './entities/UserListEntityService.js'; import { FlashEntityService } from './entities/FlashEntityService.js'; import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; @@ -146,7 +141,6 @@ const $IdService: Provider = { provide: 'IdService', useExisting: IdService }; const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService }; const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService }; const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService }; -const $MessagingService: Provider = { provide: 'MessagingService', useExisting: MessagingService }; const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService }; const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService }; const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService }; @@ -207,7 +201,6 @@ const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService const $GalleryPostEntityService: Provider = { provide: 'GalleryPostEntityService', useExisting: GalleryPostEntityService }; const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useExisting: HashtagEntityService }; const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useExisting: InstanceEntityService }; -const $MessagingMessageEntityService: Provider = { provide: 'MessagingMessageEntityService', useExisting: MessagingMessageEntityService }; const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useExisting: ModerationLogEntityService }; const $MutingEntityService: Provider = { provide: 'MutingEntityService', useExisting: MutingEntityService }; const $NoteEntityService: Provider = { provide: 'NoteEntityService', useExisting: NoteEntityService }; @@ -218,8 +211,6 @@ const $PageEntityService: Provider = { provide: 'PageEntityService', useExisting const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService }; const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService }; const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService }; -const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService }; -const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService }; const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService }; const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; @@ -273,7 +264,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ImageProcessingService, InstanceActorService, InternalStorageService, - MessagingService, MetaService, MfmService, ModerationLogService, @@ -333,7 +323,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting GalleryPostEntityService, HashtagEntityService, InstanceEntityService, - MessagingMessageEntityService, ModerationLogEntityService, MutingEntityService, NoteEntityService, @@ -344,8 +333,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PageLikeEntityService, SigninEntityService, UserEntityService, - UserGroupEntityService, - UserGroupInvitationEntityService, UserListEntityService, FlashEntityService, FlashLikeEntityService, @@ -394,7 +381,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ImageProcessingService, $InstanceActorService, $InternalStorageService, - $MessagingService, $MetaService, $MfmService, $ModerationLogService, @@ -454,7 +440,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $GalleryPostEntityService, $HashtagEntityService, $InstanceEntityService, - $MessagingMessageEntityService, $ModerationLogEntityService, $MutingEntityService, $NoteEntityService, @@ -465,8 +450,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PageLikeEntityService, $SigninEntityService, $UserEntityService, - $UserGroupEntityService, - $UserGroupInvitationEntityService, $UserListEntityService, $FlashEntityService, $FlashLikeEntityService, @@ -516,7 +499,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ImageProcessingService, InstanceActorService, InternalStorageService, - MessagingService, MetaService, MfmService, ModerationLogService, @@ -575,7 +557,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting GalleryPostEntityService, HashtagEntityService, InstanceEntityService, - MessagingMessageEntityService, ModerationLogEntityService, MutingEntityService, NoteEntityService, @@ -586,8 +567,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PageLikeEntityService, SigninEntityService, UserEntityService, - UserGroupEntityService, - UserGroupInvitationEntityService, UserListEntityService, FlashEntityService, FlashLikeEntityService, @@ -636,7 +615,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ImageProcessingService, $InstanceActorService, $InternalStorageService, - $MessagingService, $MetaService, $MfmService, $ModerationLogService, @@ -695,7 +673,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $GalleryPostEntityService, $HashtagEntityService, $InstanceEntityService, - $MessagingMessageEntityService, $ModerationLogEntityService, $MutingEntityService, $NoteEntityService, @@ -706,8 +683,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PageLikeEntityService, $SigninEntityService, $UserEntityService, - $UserGroupEntityService, - $UserGroupInvitationEntityService, $UserListEntityService, $FlashEntityService, $FlashLikeEntityService, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 63f0319442f57a53661de88c910a90acbd67f626..a1a257fbd193737a728c3634267279ba422189f0 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -61,7 +61,7 @@ export class CustomEmojiService { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiAdded', { - emoji: await this.emojiEntityService.pack(emoji.id), + emoji: await this.emojiEntityService.packDetailed(emoji.id), }); } diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 42a430ea7542c1160c4b9521b912cc9d305d78ab..b15c967c85e96724846ecde930a66be81928aa09 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -7,7 +7,7 @@ import { DI } from '@/di-symbols.js'; import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import Logger from '@/logger.js'; -import type { IRemoteUser, User } from '@/models/entities/User.js'; +import type { RemoteUser, User } from '@/models/entities/User.js'; import { MetaService } from '@/core/MetaService.js'; import { DriveFile } from '@/models/entities/DriveFile.js'; import { IdService } from '@/core/IdService.js'; @@ -255,7 +255,7 @@ export class DriveService { return { webpublic: null, thumbnail: null, - } + }; } try { @@ -399,7 +399,7 @@ export class DriveService { } @bindThis - private async deleteOldFile(user: IRemoteUser) { + private async deleteOldFile(user: RemoteUser) { const q = this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) .andWhere('file.isLink = FALSE'); @@ -500,7 +500,7 @@ export class DriveService { throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); } else { // (ã‚¢ãƒã‚¿ãƒ¼ã¾ãŸã¯ãƒãƒŠãƒ¼ã‚’å«ã¾ãš)最もå¤ã„ファイルを削除ã™ã‚‹ - this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as IRemoteUser); + this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser); } } } diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 35f30deeb4e5e750366b4519ae735576a2be9574..bbc8b4332e87153dc56a213703bbe48330a743dd 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -2,7 +2,6 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { JSDOM } from 'jsdom'; import tinycolor from 'tinycolor2'; -import fetch from 'node-fetch'; import type { Instance } from '@/models/entities/Instance.js'; import type { InstancesRepository } from '@/models/index.js'; import { AppLockService } from '@/core/AppLockService.js'; diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index 67337b5056c804a67e6bfd967d63e78a80929eb1..e39b134b7ee1336eafc8412ffd08526fcdd80108 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -3,7 +3,7 @@ import * as crypto from 'node:crypto'; import { join } from 'node:path'; import * as stream from 'node:stream'; import * as util from 'node:util'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { FSWatcher } from 'chokidar'; import { fileTypeFromFile } from 'file-type'; import FFmpeg from 'fluent-ffmpeg'; diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 784612149d7772a7b831555a9d5b2b2a389a6033..65a69a02359f3929d55783e51f3b11b677f93e44 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -3,21 +3,15 @@ import Redis from 'ioredis'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import type { UserList } from '@/models/entities/UserList.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { Antenna } from '@/models/entities/Antenna.js'; -import type { Channel } from '@/models/entities/Channel.js'; import type { StreamChannels, AdminStreamTypes, AntennaStreamTypes, BroadcastTypes, - ChannelStreamTypes, DriveStreamTypes, - GroupMessagingStreamTypes, InternalStreamTypes, MainStreamTypes, - MessagingIndexStreamTypes, - MessagingStreamTypes, NoteStreamTypes, UserListStreamTypes, UserStreamTypes, @@ -83,11 +77,6 @@ export class GlobalEventService { }); } - @bindThis - public publishChannelStream<K extends keyof ChannelStreamTypes>(channelId: Channel['id'], type: K, value?: ChannelStreamTypes[K]): void { - this.publish(`channelStream:${channelId}`, type, typeof value === 'undefined' ? null : value); - } - @bindThis public publishUserListStream<K extends keyof UserListStreamTypes>(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void { this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); @@ -98,21 +87,6 @@ export class GlobalEventService { this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); } - @bindThis - public publishMessagingStream<K extends keyof MessagingStreamTypes>(userId: User['id'], otherpartyId: User['id'], type: K, value?: MessagingStreamTypes[K]): void { - this.publish(`messagingStream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value); - } - - @bindThis - public publishGroupMessagingStream<K extends keyof GroupMessagingStreamTypes>(groupId: UserGroup['id'], type: K, value?: GroupMessagingStreamTypes[K]): void { - this.publish(`messagingStream:${groupId}`, type, typeof value === 'undefined' ? null : value); - } - - @bindThis - public publishMessagingIndexStream<K extends keyof MessagingIndexStreamTypes>(userId: User['id'], type: K, value?: MessagingIndexStreamTypes[K]): void { - this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); - } - @bindThis public publishNotesStream(note: Packed<'Note'>): void { this.publish('notesStream', null, note); diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index e32026b04f7dcdf62aa534eeabfb3f48a94774a6..375aa846cb36ea37f431e16efe48ec163e11850f 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -99,7 +99,6 @@ export class HttpRequestService { const res = await this.send(url, { method: 'GET', headers: Object.assign({ - 'User-Agent': this.config.userAgent, Accept: accept, }, headers ?? {}), timeout: 5000, @@ -114,7 +113,6 @@ export class HttpRequestService { const res = await this.send(url, { method: 'GET', headers: Object.assign({ - 'User-Agent': this.config.userAgent, Accept: accept, }, headers ?? {}), timeout: 5000, @@ -144,7 +142,10 @@ export class HttpRequestService { const res = await fetch(url, { method: args.method ?? 'GET', - headers: args.headers, + headers: { + 'User-Agent': this.config.userAgent, + ...(args.headers ?? {}) + }, body: args.body, size: args.size ?? 10 * 1024 * 1024, agent: (url) => this.getAgentByUrl(url), diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index fbc02f504b16fa829455f22e998a73a5cae8d654..7c88f5e9a092cdccbf41a546d181f4190c143afa 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -107,7 +107,7 @@ export class ImageProcessingService { withoutEnlargement: true, }) .rotate() - .webp(options) + .webp(options); return { data, diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index 0b4a83c634c855354cc6aa42a923e18135043747..ee9ae0733fbc8e46f671d5fc0c7e820fa0f4ced8 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import type { ILocalUser } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import type { UsersRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import { DI } from '@/di-symbols.js'; @@ -11,7 +11,7 @@ const ACTOR_USERNAME = 'instance.actor' as const; @Injectable() export class InstanceActorService { - private cache: Cache<ILocalUser>; + private cache: Cache<LocalUser>; constructor( @Inject(DI.usersRepository) @@ -19,24 +19,24 @@ export class InstanceActorService { private createSystemUserService: CreateSystemUserService, ) { - this.cache = new Cache<ILocalUser>(Infinity); + this.cache = new Cache<LocalUser>(Infinity); } @bindThis - public async getInstanceActor(): Promise<ILocalUser> { + public async getInstanceActor(): Promise<LocalUser> { const cached = this.cache.get(null); if (cached) return cached; const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, - }) as ILocalUser | undefined; + }) as LocalUser | undefined; if (user) { this.cache.set(null, user); return user; } else { - const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as ILocalUser; + const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as LocalUser; this.cache.set(null, created); return created; } diff --git a/packages/backend/src/core/MessagingService.ts b/packages/backend/src/core/MessagingService.ts deleted file mode 100644 index f4a1090658c2de31be9fc1f55b4847f099e3e2a8..0000000000000000000000000000000000000000 --- a/packages/backend/src/core/MessagingService.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { In, Not } from 'typeorm'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; -import type { Note } from '@/models/entities/Note.js'; -import type { User, CacheableUser, IRemoteUser } from '@/models/entities/User.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { QueueService } from '@/core/QueueService.js'; -import { toArray } from '@/misc/prelude/array.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { MessagingMessagesRepository, MutingsRepository, UserGroupJoiningsRepository, UsersRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; -import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; -import { PushNotificationService } from '@/core/PushNotificationService.js'; -import { bindThis } from '@/decorators.js'; - -@Injectable() -export class MessagingService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - @Inject(DI.mutingsRepository) - private mutingsRepository: MutingsRepository, - - private userEntityService: UserEntityService, - private messagingMessageEntityService: MessagingMessageEntityService, - private idService: IdService, - private globalEventService: GlobalEventService, - private apRendererService: ApRendererService, - private queueService: QueueService, - private pushNotificationService: PushNotificationService, - ) { - } - - @bindThis - public async createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { - const message = { - id: this.idService.genId(), - createdAt: new Date(), - fileId: file ? file.id : null, - recipientId: recipientUser ? recipientUser.id : null, - groupId: recipientGroup ? recipientGroup.id : null, - text: text ? text.trim() : null, - userId: user.id, - isRead: false, - reads: [] as any[], - uri, - } as MessagingMessage; - - await this.messagingMessagesRepository.insert(message); - - const messageObj = await this.messagingMessageEntityService.pack(message); - - if (recipientUser) { - if (this.userEntityService.isLocalUser(user)) { - // 自分ã®ã‚¹ãƒˆãƒªãƒ¼ãƒ - this.globalEventService.publishMessagingStream(message.userId, recipientUser.id, 'message', messageObj); - this.globalEventService.publishMessagingIndexStream(message.userId, 'message', messageObj); - this.globalEventService.publishMainStream(message.userId, 'messagingMessage', messageObj); - } - - if (this.userEntityService.isLocalUser(recipientUser)) { - // 相手ã®ã‚¹ãƒˆãƒªãƒ¼ãƒ - this.globalEventService.publishMessagingStream(recipientUser.id, message.userId, 'message', messageObj); - this.globalEventService.publishMessagingIndexStream(recipientUser.id, 'message', messageObj); - this.globalEventService.publishMainStream(recipientUser.id, 'messagingMessage', messageObj); - } - } else if (recipientGroup) { - // グループã®ã‚¹ãƒˆãƒªãƒ¼ãƒ - this.globalEventService.publishGroupMessagingStream(recipientGroup.id, 'message', messageObj); - - // メンãƒãƒ¼ã®ã‚¹ãƒˆãƒªãƒ¼ãƒ - const joinings = await this.userGroupJoiningsRepository.findBy({ userGroupId: recipientGroup.id }); - for (const joining of joinings) { - this.globalEventService.publishMessagingIndexStream(joining.userId, 'message', messageObj); - this.globalEventService.publishMainStream(joining.userId, 'messagingMessage', messageObj); - } - } - - // 2秒経ã£ã¦ã‚‚(今回作æˆã—ãŸ)メッセージãŒæ—¢èªã«ãªã‚‰ãªã‹ã£ãŸã‚‰ã€Œæœªèªã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãŒã‚ã‚Šã¾ã™ã‚ˆã€ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行ã™ã‚‹ - setTimeout(async () => { - const freshMessage = await this.messagingMessagesRepository.findOneBy({ id: message.id }); - if (freshMessage == null) return; // メッセージãŒå‰Šé™¤ã•ã‚Œã¦ã„ã‚‹å ´åˆã‚‚ã‚ã‚‹ - - if (recipientUser && this.userEntityService.isLocalUser(recipientUser)) { - if (freshMessage.isRead) return; // æ—¢èª - - //#region ãŸã ã—ミュートã•ã‚Œã¦ã„ã‚‹ãªã‚‰ç™ºè¡Œã—ãªã„ - const mute = await this.mutingsRepository.findBy({ - muterId: recipientUser.id, - }); - if (mute.map(m => m.muteeId).includes(user.id)) return; - //#endregion - - this.globalEventService.publishMainStream(recipientUser.id, 'unreadMessagingMessage', messageObj); - this.pushNotificationService.pushNotification(recipientUser.id, 'unreadMessagingMessage', messageObj); - } else if (recipientGroup) { - const joinings = await this.userGroupJoiningsRepository.findBy({ userGroupId: recipientGroup.id, userId: Not(user.id) }); - for (const joining of joinings) { - if (freshMessage.reads.includes(joining.userId)) return; // æ—¢èª - this.globalEventService.publishMainStream(joining.userId, 'unreadMessagingMessage', messageObj); - this.pushNotificationService.pushNotification(joining.userId, 'unreadMessagingMessage', messageObj); - } - } - }, 2000); - - if (recipientUser && this.userEntityService.isLocalUser(user) && this.userEntityService.isRemoteUser(recipientUser)) { - const note = { - id: message.id, - createdAt: message.createdAt, - fileIds: message.fileId ? [message.fileId] : [], - text: message.text, - userId: message.userId, - visibility: 'specified', - mentions: [recipientUser].map(u => u.id), - mentionedRemoteUsers: JSON.stringify([recipientUser].map(u => ({ - uri: u.uri, - username: u.username, - host: u.host, - }))), - } as Note; - - const activity = this.apRendererService.renderActivity(this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false, true), note)); - - this.queueService.deliver(user, activity, recipientUser.inbox); - } - return messageObj; - } - - @bindThis - public async deleteMessage(message: MessagingMessage) { - await this.messagingMessagesRepository.delete(message.id); - this.postDeleteMessage(message); - } - - @bindThis - private async postDeleteMessage(message: MessagingMessage) { - if (message.recipientId) { - const user = await this.usersRepository.findOneByOrFail({ id: message.userId }); - const recipient = await this.usersRepository.findOneByOrFail({ id: message.recipientId }); - - if (this.userEntityService.isLocalUser(user)) this.globalEventService.publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id); - if (this.userEntityService.isLocalUser(recipient)) this.globalEventService.publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id); - - if (this.userEntityService.isLocalUser(user) && this.userEntityService.isRemoteUser(recipient)) { - const activity = this.apRendererService.renderActivity(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), user)); - this.queueService.deliver(user, activity, recipient.inbox); - } - } else if (message.groupId) { - this.globalEventService.publishGroupMessagingStream(message.groupId, 'deleted', message.id); - } - } - - /** - * Mark messages as read - */ - @bindThis - public async readUserMessagingMessage( - userId: User['id'], - otherpartyId: User['id'], - messageIds: MessagingMessage['id'][], - ) { - if (messageIds.length === 0) return; - - const messages = await this.messagingMessagesRepository.findBy({ - id: In(messageIds), - }); - - for (const message of messages) { - if (message.recipientId !== userId) { - throw new IdentifiableError('e140a4bf-49ce-4fb6-b67c-b78dadf6b52f', 'Access denied (user).'); - } - } - - // Update documents - await this.messagingMessagesRepository.update({ - id: In(messageIds), - userId: otherpartyId, - recipientId: userId, - isRead: false, - }, { - isRead: true, - }); - - // Publish event - this.globalEventService.publishMessagingStream(otherpartyId, userId, 'read', messageIds); - this.globalEventService.publishMessagingIndexStream(userId, 'read', messageIds); - - if (!await this.userEntityService.getHasUnreadMessagingMessage(userId)) { - // å…¨ã¦ã®(ã„ã¾ã¾ã§æœªèªã ã£ãŸ)自分宛ã¦ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’(ã“ã‚Œã§)èªã¿ã¾ã—ãŸã‚ˆã¨ã„ã†ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - this.globalEventService.publishMainStream(userId, 'readAllMessagingMessages'); - this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessages', undefined); - } else { - // ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã§æœªèªãŒãªã‘ã‚Œã°ã‚¤ãƒ™ãƒ³ãƒˆç™ºè¡Œ - const count = await this.messagingMessagesRepository.count({ - where: { - userId: otherpartyId, - recipientId: userId, - isRead: false, - }, - take: 1, - }); - - if (!count) { - this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId }); - } - } - } - - /** - * Mark messages as read - */ - @bindThis - public async readGroupMessagingMessage( - userId: User['id'], - groupId: UserGroup['id'], - messageIds: MessagingMessage['id'][], - ) { - if (messageIds.length === 0) return; - - // check joined - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userId: userId, - userGroupId: groupId, - }); - - if (joining == null) { - throw new IdentifiableError('930a270c-714a-46b2-b776-ad27276dc569', 'Access denied (group).'); - } - - const messages = await this.messagingMessagesRepository.findBy({ - id: In(messageIds), - }); - - const reads: MessagingMessage['id'][] = []; - - for (const message of messages) { - if (message.userId === userId) continue; - if (message.reads.includes(userId)) continue; - - // Update document - await this.messagingMessagesRepository.createQueryBuilder().update() - .set({ - reads: (() => `array_append("reads", '${joining.userId}')`) as any, - }) - .where('id = :id', { id: message.id }) - .execute(); - - reads.push(message.id); - } - - // Publish event - this.globalEventService.publishGroupMessagingStream(groupId, 'read', { - ids: reads, - userId: userId, - }); - this.globalEventService.publishMessagingIndexStream(userId, 'read', reads); - - if (!await this.userEntityService.getHasUnreadMessagingMessage(userId)) { - // å…¨ã¦ã®(ã„ã¾ã¾ã§æœªèªã ã£ãŸ)自分宛ã¦ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’(ã“ã‚Œã§)èªã¿ã¾ã—ãŸã‚ˆã¨ã„ã†ã‚¤ãƒ™ãƒ³ãƒˆã‚’発行 - this.globalEventService.publishMainStream(userId, 'readAllMessagingMessages'); - this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessages', undefined); - } else { - // ãã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãŠã„ã¦æœªèªãŒãªã‘ã‚Œã°ã‚¤ãƒ™ãƒ³ãƒˆç™ºè¡Œ - const unreadExist = await this.messagingMessagesRepository.createQueryBuilder('message') - .where('message.groupId = :groupId', { groupId: groupId }) - .andWhere('message.userId != :userId', { userId: userId }) - .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) - .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分ãŒåŠ å…¥ã™ã‚‹å‰ã®ä¼šè©±ã«ã¤ã„ã¦ã¯ã€æœªèªæ‰±ã„ã—ãªã„ - .getOne().then(x => x != null); - - if (!unreadExist) { - this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId }); - } - } - } - - @bindThis - public async deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) { - messages = toArray(messages).filter(x => x.uri); - const contents = messages.map(x => this.apRendererService.renderRead(user, x)); - - if (contents.length > 1) { - const collection = this.apRendererService.renderOrderedCollection(null, contents.length, undefined, undefined, contents); - this.queueService.deliver(user, this.apRendererService.renderActivity(collection), recipient.inbox); - } else { - for (const content of contents) { - this.queueService.deliver(user, this.apRendererService.renderActivity(content), recipient.inbox); - } - } - } -} diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 6c40ba25a1faaac97f97f276ea13efd52bb6928f..9b2d5dc0ff1ec7e3e364193709a6806c4f45dc39 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -1,9 +1,8 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; -import { JSDOM } from 'jsdom'; +import { Window } from 'happy-dom'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; @@ -236,7 +235,7 @@ export class MfmService { return null; } - const { window } = new JSDOM(''); + const { window } = new Window(); const doc = window.document; @@ -301,7 +300,7 @@ export class MfmService { hashtag: (node) => { const a = doc.createElement('a'); - a.href = `${this.config.url}/tags/${node.props.hashtag}`; + a.setAttribute('href', `${this.config.url}/tags/${node.props.hashtag}`); a.textContent = `#${node.props.hashtag}`; a.setAttribute('rel', 'tag'); return a; @@ -327,7 +326,7 @@ export class MfmService { link: (node) => { const a = doc.createElement('a'); - a.href = node.props.url; + a.setAttribute('href', node.props.url); appendChildren(node.children, a); return a; }, @@ -336,7 +335,7 @@ export class MfmService { const a = doc.createElement('a'); const { username, host, acct } = node.props; const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host); - a.href = remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`; + a.setAttribute('href', remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`); a.className = 'u-url mention'; a.textContent = acct; return a; @@ -361,14 +360,14 @@ export class MfmService { url: (node) => { const a = doc.createElement('a'); - a.href = node.props.url; + a.setAttribute('href', node.props.url); a.textContent = node.props.url; return a; }, search: (node) => { const a = doc.createElement('a'); - a.href = `https://www.google.com/search?q=${node.props.query}`; + a.setAttribute('href', `https://www.google.com/search?q=${node.props.query}`); a.textContent = node.props.content; return a; }, diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4a81f764dc73d43566dee9510f96d2961e691987..54c135a7c5eeca76b85961dc4a822bb30c0877a2 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -1,5 +1,5 @@ import * as mfm from 'mfm-js'; -import { Not, In, DataSource } from 'typeorm'; +import { In, DataSource } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { extractMentions } from '@/misc/extract-mentions.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; @@ -11,7 +11,7 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { App } from '@/models/entities/App.js'; import { concat } from '@/misc/prelude/array.js'; import { IdService } from '@/core/IdService.js'; -import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; +import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { IPoll } from '@/models/entities/Poll.js'; import { Poll } from '@/models/entities/Poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; @@ -52,7 +52,7 @@ class NotificationManager { private notifier: { id: User['id']; }; private note: Note; private queue: { - target: ILocalUser['id']; + target: LocalUser['id']; reason: NotificationType; }[]; @@ -68,7 +68,7 @@ class NotificationManager { } @bindThis - public push(notifiee: ILocalUser['id'], reason: NotificationType) { + public push(notifiee: LocalUser['id'], reason: NotificationType) { // 自分自身ã¸ã¯é€šçŸ¥ã—ãªã„ if (this.notifier.id === notifiee) return; @@ -605,7 +605,7 @@ export class NoteCreateService { // メンションã•ã‚ŒãŸãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã«é…é€ for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) { - dm.addDirectRecipe(u as IRemoteUser); + dm.addDirectRecipe(u as RemoteUser); } // 投稿ãŒãƒªãƒ—ライã‹ã¤æŠ•ç¨¿è€…ãŒãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã¤ãƒªãƒ—ライ先ã®æŠ•ç¨¿ã®æŠ•ç¨¿è€…ãŒãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ãªã‚‰é…é€ @@ -711,7 +711,7 @@ export class NoteCreateService { ? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note) : this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); - return this.apRendererService.renderActivity(content); + return this.apRendererService.addContext(content); } @bindThis diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 4dad825097da665cec8968a36d3361a03b8bf10d..571b62552336de22d12ee721f22373c0659c812b 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -1,6 +1,6 @@ import { Brackets, In } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; -import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; +import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js'; import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; import { RelayService } from '@/core/RelayService.js'; @@ -78,7 +78,7 @@ export class NoteDeleteService { }); } - const content = this.apRendererService.renderActivity(renote + const content = this.apRendererService.addContext(renote ? this.apRendererService.renderUndo(this.apRendererService.renderAnnounce(renote.uri ?? `${this.config.url}/notes/${renote.id}`, note), user) : this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${note.id}`), user)); @@ -90,7 +90,7 @@ export class NoteDeleteService { for (const cascadingNote of cascadingNotes) { if (!cascadingNote.user) continue; if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue; - const content = this.apRendererService.renderActivity(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); + const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); this.deliverToConcerned(cascadingNote.user, cascadingNote, content); } //#endregion @@ -159,11 +159,11 @@ export class NoteDeleteService { return await this.usersRepository.find({ where, - }) as IRemoteUser[]; + }) as RemoteUser[]; } @bindThis - private async deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { + private async deliverToConcerned(user: { id: LocalUser['id']; host: null; }, note: Note, content: any) { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); const remoteUsers = await this.getMentionedRemoteUsers(note); diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index bb6def1edb66fdf20ae498af78019621b8469991..3a9f832ac07baf7e16caebd17bc20fcea294ab6d 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -115,7 +115,7 @@ export class NotePiningService { const target = `${this.config.url}/users/${user.id}/collections/featured`; const item = `${this.config.url}/notes/${noteId}`; - const content = this.apRendererService.renderActivity(isAddition ? this.apRendererService.renderAdd(user, target, item) : this.apRendererService.renderRemove(user, target, item)); + const content = this.apRendererService.addContext(isAddition ? this.apRendererService.renderAdd(user, target, item) : this.apRendererService.renderRemove(user, target, item)); this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 9fef36dd2cfee0e227cab1bf2f1df3b34d593891..88173c230717e6dc288999521506eb3ea2ef5d55 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -2,13 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { NotificationsRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { Notification } from '@/models/entities/Notification.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import { GlobalEventService } from './GlobalEventService.js'; import { PushNotificationService } from './PushNotificationService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class NotificationService { @@ -66,7 +65,6 @@ export class NotificationService { @bindThis private postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { - this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); } } diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 042dcb3e67bb3270cff9d2838cf9d768c7518957..368753d9a7a93e325294d12ca1a6e7d3492c2c26 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -1,10 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; +import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, User } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { RelayService } from '@/core/RelayService.js'; -import type { CacheableUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; @@ -39,7 +37,7 @@ export class PollService { } @bindThis - public async vote(user: CacheableUser, note: Note, choice: number) { + public async vote(user: User, note: Note, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); if (poll == null) throw new Error('poll not found'); @@ -97,7 +95,7 @@ export class PollService { if (user == null) throw new Error('note not found'); if (this.userEntityService.isLocalUser(user)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user)); + const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user)); this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index 55b70bfc948f706f81ddc2ce86717e7973886f04..780e56ef104f50c0b63cd9ddaa987bbc49f2d72a 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository } from '@/models/index.js'; -import type { ILocalUser, User } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; @@ -16,9 +16,9 @@ export class ProxyAccountService { } @bindThis - public async fetch(): Promise<ILocalUser | null> { + public async fetch(): Promise<LocalUser | null> { const meta = await this.metaService.fetch(); if (meta.proxyAccountId == null) return null; - return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as ILocalUser; + return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as LocalUser; } } diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index b18b7bb2cdb2f4e654af1dbfea899a749be055cf..2cad1bc07ea8b2e411c738f51a8a84659f44d1bd 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -9,24 +9,21 @@ import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; // Defined also packages/sw/types.ts#L13 -type pushNotificationsTypes = { +type PushNotificationsTypes = { 'notification': Packed<'Notification'>; - 'unreadMessagingMessage': Packed<'MessagingMessage'>; 'unreadAntennaNote': { antenna: { id: string, name: string }; note: Packed<'Note'>; }; 'readNotifications': { notificationIds: string[] }; 'readAllNotifications': undefined; - 'readAllMessagingMessages': undefined; - 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string }; 'readAntenna': { antennaId: string }; 'readAllAntennas': undefined; }; // Reduce length because push message servers have character limits -function truncateBody<T extends keyof pushNotificationsTypes>(type: T, body: pushNotificationsTypes[T]): pushNotificationsTypes[T] { - if (body === undefined) return body; +function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: PushNotificationsTypes[T]): PushNotificationsTypes[T] { + if (typeof body !== 'object') return body; return { ...body, @@ -40,11 +37,9 @@ function truncateBody<T extends keyof pushNotificationsTypes>(type: T, body: pus reply: undefined, renote: undefined, user: type === 'notification' ? undefined as any : body.note.user, - } + }, } : {}), }; - - return body; } @Injectable() @@ -61,7 +56,7 @@ export class PushNotificationService { } @bindThis - public async pushNotification<T extends keyof pushNotificationsTypes>(userId: string, type: T, body: pushNotificationsTypes[T]) { + public async pushNotification<T extends keyof PushNotificationsTypes>(userId: string, type: T, body: PushNotificationsTypes[T]) { const meta = await this.metaService.fetch(); if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; @@ -81,8 +76,6 @@ export class PushNotificationService { if ([ 'readNotifications', 'readAllNotifications', - 'readAllMessagingMessages', - 'readAllMessagingMessagesOfARoom', 'readAntenna', 'readAllAntennas', ].includes(type) && !subscription.sendReadMessage) continue; diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 3806590059bd1d8dfbe96808d6ecc3aaa6741efc..9fccc14ee4298d5c5faef2ce61e89de3d8d37f3a 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -3,7 +3,7 @@ import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { EmojisRepository, BlockingsRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { IRemoteUser, User } from '@/models/entities/User.js'; +import type { RemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { IdService } from '@/core/IdService.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; @@ -85,7 +85,7 @@ export class ReactionService { } @bindThis - public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, reaction?: string) { + public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, reaction?: string | null) { // Check blocking if (note.userId !== user.id) { const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); @@ -177,11 +177,11 @@ export class ReactionService { //#region é…ä¿¡ if (this.userEntityService.isLocalUser(user) && !note.localOnly) { - const content = this.apRendererService.renderActivity(await this.apRendererService.renderLike(record, note)); + const content = this.apRendererService.addContext(await this.apRendererService.renderLike(record, note)); const dm = this.apDeliverManagerService.createDeliverManager(user, content); if (note.userHost !== null) { const reactee = await this.usersRepository.findOneBy({ id: note.userId }); - dm.addDirectRecipe(reactee as IRemoteUser); + dm.addDirectRecipe(reactee as RemoteUser); } if (['public', 'home', 'followers'].includes(note.visibility)) { @@ -189,7 +189,7 @@ export class ReactionService { } else if (note.visibility === 'specified') { const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id }))); for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) { - dm.addDirectRecipe(u as IRemoteUser); + dm.addDirectRecipe(u as RemoteUser); } } @@ -235,11 +235,11 @@ export class ReactionService { //#region é…ä¿¡ if (this.userEntityService.isLocalUser(user) && !note.localOnly) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(await this.apRendererService.renderLike(exist, note), user)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(await this.apRendererService.renderLike(exist, note), user)); const dm = this.apDeliverManagerService.createDeliverManager(user, content); if (note.userHost !== null) { const reactee = await this.usersRepository.findOneBy({ id: note.userId }); - dm.addDirectRecipe(reactee as IRemoteUser); + dm.addDirectRecipe(reactee as RemoteUser); } dm.addFollowersRecipe(); dm.execute(); diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index a7408649b88360f592bd068aae7093d8e0db1e29..2e07825e9bdf689eecea4ede9c35a84308938619 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import type { ILocalUser, User } from '@/models/entities/User.js'; +import type { LocalUser, User } from '@/models/entities/User.js'; import type { RelaysRepository, UsersRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Cache } from '@/misc/cache.js'; @@ -34,16 +34,16 @@ export class RelayService { } @bindThis - private async getRelayActor(): Promise<ILocalUser> { + private async getRelayActor(): Promise<LocalUser> { const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, }); - if (user) return user as ILocalUser; + if (user) return user as LocalUser; const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME); - return created as ILocalUser; + return created as LocalUser; } @bindThis @@ -56,7 +56,7 @@ export class RelayService { const relayActor = await this.getRelayActor(); const follow = await this.apRendererService.renderFollowRelay(relay, relayActor); - const activity = this.apRendererService.renderActivity(follow); + const activity = this.apRendererService.addContext(follow); this.queueService.deliver(relayActor, activity, relay.inbox); return relay; @@ -75,7 +75,7 @@ export class RelayService { const relayActor = await this.getRelayActor(); const follow = this.apRendererService.renderFollowRelay(relay, relayActor); const undo = this.apRendererService.renderUndo(follow, relayActor); - const activity = this.apRendererService.renderActivity(undo); + const activity = this.apRendererService.addContext(undo); this.queueService.deliver(relayActor, activity, relay.inbox); await this.relaysRepository.delete(relay.id); diff --git a/packages/backend/src/core/RemoteLoggerService.ts b/packages/backend/src/core/RemoteLoggerService.ts index 0ea5d7b42fbcdad193d8a3ded56ea3dc103f9abc..3d4560583678d9ceb960508c2521e8ba13b7eb59 100644 --- a/packages/backend/src/core/RemoteLoggerService.ts +++ b/packages/backend/src/core/RemoteLoggerService.ts @@ -1,7 +1,6 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class RemoteLoggerService { diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index dde40986243aacd7b433f1fc8f1b9deb05f7c298..b72dce5180758e76306ee6c3e325e1ef5f82f62f 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -4,7 +4,7 @@ import chalk from 'chalk'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; -import type { IRemoteUser, User } from '@/models/entities/User.js'; +import type { RemoteUser, User } from '@/models/entities/User.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -60,7 +60,7 @@ export class RemoteUserResolveService { }); } - const user = await this.usersRepository.findOneBy({ usernameLower, host }) as IRemoteUser | null; + const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null; const acctLower = `${usernameLower}@${host}`; @@ -82,7 +82,7 @@ export class RemoteUserResolveService { const self = await this.resolveSelf(acctLower); if (user.uri !== self.href) { - // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping. + // if uri mismatch, Fix (user@host <=> AP's Person id(RemoteUser.uri)) mapping. this.logger.info(`uri missmatch: ${acctLower}`); this.logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`); diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 9a782780d1ab0053b7feff54b0b8bd35b76002ca..b84d5e75853e2b71dcab88a56b5db00cafe3e74b 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -3,7 +3,7 @@ import Redis from 'ioredis'; import { In } from 'typeorm'; import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; -import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js'; +import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { MetaService } from '@/core/MetaService.js'; diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index d73432866944fa69df8785c0fe9bd49bb45ce5e3..be37bad52ee4f109107902b6bf5294beb7c59c2a 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import Redis from 'ioredis'; import { IdService } from '@/core/IdService.js'; -import type { CacheableUser, User } from '@/models/entities/User.js'; +import type { User } from '@/models/entities/User.js'; import type { Blocking } from '@/models/entities/Blocking.js'; import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; @@ -117,7 +117,7 @@ export class UserBlockingService implements OnApplicationShutdown { }); if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderBlock(blocking)); + const content = this.apRendererService.addContext(this.apRendererService.renderBlock(blocking)); this.queueService.deliver(blocker, content, blockee.inbox); } } @@ -162,13 +162,13 @@ export class UserBlockingService implements OnApplicationShutdown { // リモートã«ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ã—ã¦ã„ãŸã‚‰UndoFollowé€ä¿¡ if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } // リモートã‹ã‚‰ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’å—ã‘ã¦ã„ãŸã‚‰Rejecté€ä¿¡ if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -210,13 +210,13 @@ export class UserBlockingService implements OnApplicationShutdown { // リモートã«ãƒ•ã‚©ãƒãƒ¼ã‚’ã—ã¦ã„ãŸã‚‰UndoFollowé€ä¿¡ if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } // リモートã‹ã‚‰ãƒ•ã‚©ãƒãƒ¼ã‚’ã•ã‚Œã¦ã„ãŸã‚‰RejectFollowé€ä¿¡ if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -236,7 +236,7 @@ export class UserBlockingService implements OnApplicationShutdown { } @bindThis - public async unblock(blocker: CacheableUser, blockee: CacheableUser) { + public async unblock(blocker: User, blockee: User) { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, @@ -261,7 +261,7 @@ export class UserBlockingService implements OnApplicationShutdown { // deliver if remote bloking if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker)); this.queueService.deliver(blocker, content, blockee.inbox); } } diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index 29a64f5848d54a868f493e91929bcd0e304d142d..fc383d1c0861299927a7fc9ddd35a4cdb9ec2df3 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import type { UsersRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; -import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js'; +import type { LocalUser, User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -11,10 +11,10 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class UserCacheService implements OnApplicationShutdown { - public userByIdCache: Cache<CacheableUser>; - public localUserByNativeTokenCache: Cache<CacheableLocalUser | null>; - public localUserByIdCache: Cache<CacheableLocalUser>; - public uriPersonCache: Cache<CacheableUser | null>; + public userByIdCache: Cache<User>; + public localUserByNativeTokenCache: Cache<LocalUser | null>; + public localUserByIdCache: Cache<LocalUser>; + public uriPersonCache: Cache<User | null>; constructor( @Inject(DI.redisSubscriber) @@ -27,10 +27,10 @@ export class UserCacheService implements OnApplicationShutdown { ) { //this.onMessage = this.onMessage.bind(this); - this.userByIdCache = new Cache<CacheableUser>(Infinity); - this.localUserByNativeTokenCache = new Cache<CacheableLocalUser | null>(Infinity); - this.localUserByIdCache = new Cache<CacheableLocalUser>(Infinity); - this.uriPersonCache = new Cache<CacheableUser | null>(Infinity); + this.userByIdCache = new Cache<User>(Infinity); + this.localUserByNativeTokenCache = new Cache<LocalUser | null>(Infinity); + this.localUserByIdCache = new Cache<LocalUser>(Infinity); + this.uriPersonCache = new Cache<User | null>(Infinity); this.redisSubscriber.on('message', this.onMessage); } @@ -58,7 +58,7 @@ export class UserCacheService implements OnApplicationShutdown { break; } case 'userTokenRegenerated': { - const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as ILocalUser; + const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as LocalUser; this.localUserByNativeTokenCache.delete(body.oldToken); this.localUserByNativeTokenCache.set(body.newToken, user); break; diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 2214a4862a23b2e85cc8cb977aab22a1b4284e2b..d8426512bf47104c3aaf1ea29e46db898090d4cf 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueService } from '@/core/QueueService.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; @@ -21,16 +21,16 @@ import Logger from '../logger.js'; const logger = new Logger('following/create'); -type Local = ILocalUser | { - id: ILocalUser['id']; - host: ILocalUser['host']; - uri: ILocalUser['uri'] +type Local = LocalUser | { + id: LocalUser['id']; + host: LocalUser['host']; + uri: LocalUser['uri'] }; -type Remote = IRemoteUser | { - id: IRemoteUser['id']; - host: IRemoteUser['host']; - uri: IRemoteUser['uri']; - inbox: IRemoteUser['inbox']; +type Remote = RemoteUser | { + id: RemoteUser['id']; + host: RemoteUser['host']; + uri: RemoteUser['uri']; + inbox: RemoteUser['inbox']; }; type Both = Local | Remote; @@ -81,7 +81,7 @@ export class UserFollowingService { if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) { // リモートフォãƒãƒ¼ã‚’å—ã‘ã¦ãƒ–ãƒãƒƒã‚¯ã—ã¦ã„ãŸå ´åˆã¯ã€ã‚¨ãƒ©ãƒ¼ã«ã™ã‚‹ã®ã§ã¯ãªãRejectã‚’é€ã‚Šè¿”ã—ã¦ãŠã—ã¾ã„。 - const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee)); this.queueService.deliver(followee, content, follower.inbox); return; } else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) { @@ -130,7 +130,7 @@ export class UserFollowingService { await this.insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -293,13 +293,13 @@ export class UserFollowingService { } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) { // local user has null host - const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -388,7 +388,7 @@ export class UserFollowingService { } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)); this.queueService.deliver(follower, content, followee.inbox); } } @@ -403,7 +403,7 @@ export class UserFollowingService { }, ): Promise<void> { if (this.userEntityService.isRemoteUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); if (this.userEntityService.isLocalUser(follower)) { // 本æ¥ã“ã®ãƒã‚§ãƒƒã‚¯ã¯ä¸è¦ã ã‘ã©TSã«æ€’られるã®ã§ this.queueService.deliver(follower, content, followee.inbox); @@ -434,7 +434,7 @@ export class UserFollowingService { followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, - follower: CacheableUser, + follower: User, ): Promise<void> { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -448,7 +448,7 @@ export class UserFollowingService { await this.insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = this.apRendererService.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox); } @@ -556,7 +556,7 @@ export class UserFollowingService { followerId: follower.id, }); - const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId ?? undefined), followee)); + const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId ?? undefined), followee)); this.queueService.deliver(followee, content, follower.inbox); } diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index df1664942f88f927be03921677e3c8168a40486f..02903a059056d82eecb669705eb66e47816b6c95 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -35,7 +35,7 @@ export class UserSuspendService { if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxã«Deleteé…ä¿¡ - const content = this.apRendererService.renderActivity(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user)); + const content = this.apRendererService.addContext(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user)); const queue: string[] = []; @@ -65,7 +65,7 @@ export class UserSuspendService { if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxã«Undo Deleteé…ä¿¡ - const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user), user)); + const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user), user)); const queue: string[] = []; diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index dd6c51c217758cc11e5d6592ed86c9ebeb8660ba..eccfeb0e7d2657049c16ee6b273d0b9f2efce657 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -53,7 +53,7 @@ export class VideoProcessingService { thumbnail: '1', url, }) - ) + ); } } diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 64f01644a723ccf6d20e0fc0e3375dc2858fb9b1..8282a6324c5d24b934bb0bb3230704887dc3c86c 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -1,11 +1,9 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; -import { DI } from '@/di-symbols.js'; -import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; -import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; +import type { RemoteUser, User } from '@/models/entities/User.js'; +import { concat, unique } from '@/misc/prelude/array.js'; import { bindThis } from '@/decorators.js'; -import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import { getApIds } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { ApObject } from './type.js'; import type { Resolver } from './ApResolverService.js'; @@ -14,8 +12,8 @@ type Visibility = 'public' | 'home' | 'followers' | 'specified'; type AudienceInfo = { visibility: Visibility, - mentionedUsers: CacheableUser[], - visibleUsers: CacheableUser[], + mentionedUsers: User[], + visibleUsers: User[], }; @Injectable() @@ -26,16 +24,16 @@ export class ApAudienceService { } @bindThis - public async parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { + public async parseAudience(actor: RemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { const toGroups = this.groupingAudience(getApIds(to), actor); const ccGroups = this.groupingAudience(getApIds(cc), actor); const others = unique(concat([toGroups.other, ccGroups.other])); - const limit = promiseLimit<CacheableUser | null>(2); + const limit = promiseLimit<User | null>(2); const mentionedUsers = (await Promise.all( others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), - )).filter((x): x is CacheableUser => x != null); + )).filter((x): x is User => x != null); if (toGroups.public.length > 0) { return { @@ -69,7 +67,7 @@ export class ApAudienceService { } @bindThis - private groupingAudience(ids: string[], actor: CacheableRemoteUser) { + private groupingAudience(ids: string[], actor: RemoteUser) { const groups = { public: [] as string[], followers: [] as string[], @@ -101,7 +99,7 @@ export class ApAudienceService { } @bindThis - private isFollowers(id: string, actor: CacheableRemoteUser) { + private isFollowers(id: string, actor: RemoteUser) { return ( id === (actor.followersUri ?? `${actor.uri}/followers`) ); diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index 1d0c2d5da4ba0fa4153c91ca1399ac94ede2de2c..d0a4ad7a7581dc239d36a292a79a8aebe8e73856 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -1,15 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; import escapeRegexp from 'escape-regexp'; import { DI } from '@/di-symbols.js'; -import type { MessagingMessagesRepository, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; import { Cache } from '@/misc/cache.js'; import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import { UserCacheService } from '@/core/UserCacheService.js'; import type { Note } from '@/models/entities/Note.js'; -import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { bindThis } from '@/decorators.js'; +import { RemoteUser, User } from '@/models/entities/User.js'; import { getApId } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { IObject } from './type.js'; @@ -42,9 +41,6 @@ export class ApDbResolverService { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -101,28 +97,11 @@ export class ApDbResolverService { } } - @bindThis - public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> { - const parsed = this.parseUri(value); - - if (parsed.local) { - if (parsed.type !== 'notes') return null; - - return await this.messagingMessagesRepository.findOneBy({ - id: parsed.id, - }); - } else { - return await this.messagingMessagesRepository.findOneBy({ - uri: parsed.uri, - }); - } - } - /** * AP Person => Misskey User in DB */ @bindThis - public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> { + public async getUserFromApId(value: string | IObject): Promise<User | null> { const parsed = this.parseUri(value); if (parsed.local) { @@ -143,7 +122,7 @@ export class ApDbResolverService { */ @bindThis public async getAuthUserFromKeyId(keyId: string): Promise<{ - user: CacheableRemoteUser; + user: RemoteUser; key: UserPublickey; } | null> { const key = await this.publicKeyCache.fetch(keyId, async () => { @@ -159,7 +138,7 @@ export class ApDbResolverService { if (key == null) return null; return { - user: await this.userCacheService.findById(key.userId) as CacheableRemoteUser, + user: await this.userCacheService.findById(key.userId) as RemoteUser, key, }; } @@ -169,10 +148,10 @@ export class ApDbResolverService { */ @bindThis public async getAuthUserFromApId(uri: string): Promise<{ - user: CacheableRemoteUser; + user: RemoteUser; key: UserPublickey | null; } | null> { - const user = await this.apPersonService.resolvePerson(uri) as CacheableRemoteUser; + const user = await this.apPersonService.resolvePerson(uri) as RemoteUser; if (user == null) return null; diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 256cf12651b228b3760dff6e3086e06520f2f352..5e6ea69846053311691b182aa40c428e1425d4aa 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -3,7 +3,7 @@ import { IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import { QueueService } from '@/core/QueueService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -18,7 +18,7 @@ interface IFollowersRecipe extends IRecipe { interface IDirectRecipe extends IRecipe { type: 'Direct'; - to: IRemoteUser; + to: RemoteUser; } const isFollowers = (recipe: any): recipe is IFollowersRecipe => @@ -50,7 +50,7 @@ export class ApDeliverManagerService { * @param from Followee */ @bindThis - public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { + public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: any) { const manager = new DeliverManager( this.userEntityService, this.followingsRepository, @@ -68,7 +68,7 @@ export class ApDeliverManagerService { * @param to Target user */ @bindThis - public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { + public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: any, to: RemoteUser) { const manager = new DeliverManager( this.userEntityService, this.followingsRepository, @@ -132,7 +132,7 @@ class DeliverManager { * @param to To */ @bindThis - public addDirectRecipe(to: IRemoteUser) { + public addDirectRecipe(to: RemoteUser) { const recipe = { type: 'Direct', to, diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 76c8bf68dfd4752c25702b2faade5dce148e205b..21d2d16ed88ce772f95fd919888b696989322d4c 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { ReactionService } from '@/core/ReactionService.js'; import { RelayService } from '@/core/RelayService.js'; @@ -20,9 +19,10 @@ import { UtilityService } from '@/core/UtilityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { QueueService } from '@/core/QueueService.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, MessagingMessagesRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js'; -import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; +import type { RemoteUser } from '@/models/entities/User.js'; +import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; import { ApLoggerService } from './ApLoggerService.js'; import { ApDbResolverService } from './ApDbResolverService.js'; @@ -31,8 +31,7 @@ import { ApAudienceService } from './ApAudienceService.js'; import { ApPersonService } from './models/ApPersonService.js'; import { ApQuestionService } from './models/ApQuestionService.js'; import type { Resolver } from './ApResolverService.js'; -import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; -import { bindThis } from '@/decorators.js'; +import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate } from './type.js'; @Injectable() export class ApInboxService { @@ -51,9 +50,6 @@ export class ApInboxService { @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, @@ -81,13 +77,12 @@ export class ApInboxService { private apPersonService: ApPersonService, private apQuestionService: ApQuestionService, private queueService: QueueService, - private messagingService: MessagingService, ) { this.logger = this.apLoggerService.logger; } @bindThis - public async performActivity(actor: CacheableRemoteUser, activity: IObject) { + public async performActivity(actor: RemoteUser, activity: IObject) { if (isCollectionOrOrderedCollection(activity)) { const resolver = this.apResolverService.createResolver(); for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { @@ -115,7 +110,7 @@ export class ApInboxService { } @bindThis - public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise<void> { + public async performOneActivity(actor: RemoteUser, activity: IObject): Promise<void> { if (actor.isSuspended) return; if (isCreate(activity)) { @@ -124,8 +119,6 @@ export class ApInboxService { await this.delete(actor, activity); } else if (isUpdate(activity)) { await this.update(actor, activity); - } else if (isRead(activity)) { - await this.read(actor, activity); } else if (isFollow(activity)) { await this.follow(actor, activity); } else if (isAccept(activity)) { @@ -152,7 +145,7 @@ export class ApInboxService { } @bindThis - private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { + private async follow(actor: RemoteUser, activity: IFollow): Promise<string> { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { @@ -168,7 +161,7 @@ export class ApInboxService { } @bindThis - private async like(actor: CacheableRemoteUser, activity: ILike): Promise<string> { + private async like(actor: RemoteUser, activity: ILike): Promise<string> { const targetUri = getApId(activity.object); const note = await this.apNoteService.fetchNote(targetUri); @@ -186,30 +179,7 @@ export class ApInboxService { } @bindThis - private async read(actor: CacheableRemoteUser, activity: IRead): Promise<string> { - const id = await getApId(activity.object); - - if (!this.utilityService.isSelfHost(this.utilityService.extractDbHost(id))) { - return `skip: Read to foreign host (${id})`; - } - - const messageId = id.split('/').pop(); - - const message = await this.messagingMessagesRepository.findOneBy({ id: messageId }); - if (message == null) { - return 'skip: message not found'; - } - - if (actor.id !== message.recipientId) { - return 'skip: actor is not a message recipient'; - } - - await this.messagingService.readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); - return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; - } - - @bindThis - private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> { + private async accept(actor: RemoteUser, activity: IAccept): Promise<string> { const uri = activity.id ?? activity; this.logger.info(`Accept: ${uri}`); @@ -227,7 +197,7 @@ export class ApInboxService { } @bindThis - private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { + private async acceptFollow(actor: RemoteUser, activity: IFollow): Promise<string> { // ※ activityã¯ã“ã£ã¡ã‹ã‚‰æŠ•ã’ãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãªã®ã§ã€activity.actorã¯å˜åœ¨ã™ã‚‹ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ const follower = await this.apDbResolverService.getUserFromApId(activity.actor); @@ -251,7 +221,7 @@ export class ApInboxService { } @bindThis - private async add(actor: CacheableRemoteUser, activity: IAdd): Promise<void> { + private async add(actor: RemoteUser, activity: IAdd): Promise<void> { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -271,7 +241,7 @@ export class ApInboxService { } @bindThis - private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<void> { + private async announce(actor: RemoteUser, activity: IAnnounce): Promise<void> { const uri = getApId(activity); this.logger.info(`Announce: ${uri}`); @@ -282,7 +252,7 @@ export class ApInboxService { } @bindThis - private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { + private async announceNote(actor: RemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { const uri = getApId(activity); if (actor.isSuspended) { @@ -342,7 +312,7 @@ export class ApInboxService { } @bindThis - private async block(actor: CacheableRemoteUser, activity: IBlock): Promise<string> { + private async block(actor: RemoteUser, activity: IBlock): Promise<string> { // ※ activity.objectã«ãƒ–ãƒãƒƒã‚¯å¯¾è±¡ãŒã‚ã‚Šã€ãã‚Œã¯å˜åœ¨ã™ã‚‹ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã¯ãš const blockee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -360,7 +330,7 @@ export class ApInboxService { } @bindThis - private async create(actor: CacheableRemoteUser, activity: ICreate): Promise<void> { + private async create(actor: RemoteUser, activity: ICreate): Promise<void> { const uri = getApId(activity); this.logger.info(`Create: ${uri}`); @@ -396,7 +366,7 @@ export class ApInboxService { } @bindThis - private async createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { + private async createNote(resolver: Resolver, actor: RemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { const uri = getApId(note); if (typeof note === 'object') { @@ -431,7 +401,7 @@ export class ApInboxService { } @bindThis - private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise<string> { + private async delete(actor: RemoteUser, activity: IDelete): Promise<string> { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -473,7 +443,7 @@ export class ApInboxService { } @bindThis - private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise<string> { + private async deleteActor(actor: RemoteUser, uri: string): Promise<string> { this.logger.info(`Deleting the Actor: ${uri}`); if (actor.uri !== uri) { @@ -482,7 +452,7 @@ export class ApInboxService { const user = await this.usersRepository.findOneByOrFail({ id: actor.id }); if (user.isDeleted) { - this.logger.info('skip: already deleted'); + return 'skip: already deleted'; } const job = await this.queueService.createDeleteAccountJob(actor); @@ -495,7 +465,7 @@ export class ApInboxService { } @bindThis - private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise<string> { + private async deleteNote(actor: RemoteUser, uri: string): Promise<string> { this.logger.info(`Deleting the Note: ${uri}`); const unlock = await this.appLockService.getApLock(uri); @@ -504,16 +474,7 @@ export class ApInboxService { const note = await this.apDbResolverService.getNoteFromApId(uri); if (note == null) { - const message = await this.apDbResolverService.getMessageFromApId(uri); - if (message == null) return 'message not found'; - - if (message.userId !== actor.id) { - return '投稿を削除ã—よã†ã¨ã—ã¦ã„るユーザーã¯æŠ•ç¨¿ã®ä½œæˆè€…ã§ã¯ã‚ã‚Šã¾ã›ã‚“'; - } - - await this.messagingService.deleteMessage(message); - - return 'ok: message deleted'; + return 'message not found'; } if (note.userId !== actor.id) { @@ -528,7 +489,7 @@ export class ApInboxService { } @bindThis - private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise<string> { + private async flag(actor: RemoteUser, activity: IFlag): Promise<string> { // object㯠`(User|Note) | (User|Note)[]` ã ã‘ã©ã€å…¨ãƒ‘ターンDBスã‚ーマã¨å¯¾å¿œã•ã›ã‚‰ã‚Œãªã„ã®ã§ // 対象ユーザーã¯ä¸€ç•ªæœ€åˆã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ ã¨ã—㦠ã‚ã¨ã¯ã‚³ãƒ¡ãƒ³ãƒˆã¨ã—ã¦æ ¼ç´ã™ã‚‹ const uris = getApIds(activity.object); @@ -553,7 +514,7 @@ export class ApInboxService { } @bindThis - private async reject(actor: CacheableRemoteUser, activity: IReject): Promise<string> { + private async reject(actor: RemoteUser, activity: IReject): Promise<string> { const uri = activity.id ?? activity; this.logger.info(`Reject: ${uri}`); @@ -571,7 +532,7 @@ export class ApInboxService { } @bindThis - private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { + private async rejectFollow(actor: RemoteUser, activity: IFollow): Promise<string> { // ※ activityã¯ã“ã£ã¡ã‹ã‚‰æŠ•ã’ãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãªã®ã§ã€activity.actorã¯å˜åœ¨ã™ã‚‹ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã§ã‚ã‚‹å¿…è¦ãŒã‚ã‚‹ const follower = await this.apDbResolverService.getUserFromApId(activity.actor); @@ -595,7 +556,7 @@ export class ApInboxService { } @bindThis - private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise<void> { + private async remove(actor: RemoteUser, activity: IRemove): Promise<void> { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -615,7 +576,7 @@ export class ApInboxService { } @bindThis - private async undo(actor: CacheableRemoteUser, activity: IUndo): Promise<string> { + private async undo(actor: RemoteUser, activity: IUndo): Promise<string> { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -641,7 +602,7 @@ export class ApInboxService { } @bindThis - private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> { + private async undoAccept(actor: RemoteUser, activity: IAccept): Promise<string> { const follower = await this.apDbResolverService.getUserFromApId(activity.object); if (follower == null) { return 'skip: follower not found'; @@ -661,7 +622,7 @@ export class ApInboxService { } @bindThis - private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<string> { + private async undoAnnounce(actor: RemoteUser, activity: IAnnounce): Promise<string> { const uri = getApId(activity); const note = await this.notesRepository.findOneBy({ @@ -676,7 +637,7 @@ export class ApInboxService { } @bindThis - private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise<string> { + private async undoBlock(actor: RemoteUser, activity: IBlock): Promise<string> { const blockee = await this.apDbResolverService.getUserFromApId(activity.object); if (blockee == null) { @@ -692,7 +653,7 @@ export class ApInboxService { } @bindThis - private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { + private async undoFollow(actor: RemoteUser, activity: IFollow): Promise<string> { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { return 'skip: followee not found'; @@ -726,7 +687,7 @@ export class ApInboxService { } @bindThis - private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise<string> { + private async undoLike(actor: RemoteUser, activity: ILike): Promise<string> { const targetUri = getApId(activity.object); const note = await this.apNoteService.fetchNote(targetUri); @@ -741,7 +702,7 @@ export class ApInboxService { } @bindThis - private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise<string> { + private async update(actor: RemoteUser, activity: IUpdate): Promise<string> { if ('actor' in activity && actor.uri !== activity.actor) { return 'skip: invalid actor'; } diff --git a/packages/backend/src/core/activitypub/ApLoggerService.ts b/packages/backend/src/core/activitypub/ApLoggerService.ts index b9bf1e4054aa107427d0e84c799c6cd3e0951c91..eeffab1b6dd56e810a83f304c5794bca4755209f 100644 --- a/packages/backend/src/core/activitypub/ApLoggerService.ts +++ b/packages/backend/src/core/activitypub/ApLoggerService.ts @@ -1,7 +1,6 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ApLoggerService { diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 648f30229a383e6dd8eb4b9d2f5035683543acc4..6a1f233bd883c4e6d4a05f92174086a38733a735 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -5,7 +5,7 @@ import { v4 as uuid } from 'uuid'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js'; import type { Blocking } from '@/models/entities/Blocking.js'; import type { Relay } from '@/models/entities/Relay.js'; @@ -13,7 +13,6 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import type { Poll } from '@/models/entities/Poll.js'; -import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import type { PollVote } from '@/models/entities/PollVote.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; import { MfmService } from '@/core/MfmService.js'; @@ -24,7 +23,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil import { bindThis } from '@/decorators.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; -import type { IActivity, IObject } from './type.js'; +import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; import type { IIdentifier } from './models/identifier.js'; @Injectable() @@ -61,7 +60,7 @@ export class ApRendererService { } @bindThis - public renderAccept(object: any, user: { id: User['id']; host: null }) { + public renderAccept(object: any, user: { id: User['id']; host: null }): IAccept { return { type: 'Accept', actor: `${this.config.url}/users/${user.id}`, @@ -70,7 +69,7 @@ export class ApRendererService { } @bindThis - public renderAdd(user: ILocalUser, target: any, object: any) { + public renderAdd(user: LocalUser, target: any, object: any): IAdd { return { type: 'Add', actor: `${this.config.url}/users/${user.id}`, @@ -80,7 +79,7 @@ export class ApRendererService { } @bindThis - public renderAnnounce(object: any, note: Note) { + public renderAnnounce(object: any, note: Note): IAnnounce { const attributedTo = `${this.config.url}/users/${note.userId}`; let to: string[] = []; @@ -93,7 +92,7 @@ export class ApRendererService { to = [`${attributedTo}/followers`]; cc = ['https://www.w3.org/ns/activitystreams#Public']; } else { - return null; + throw new Error('renderAnnounce: cannot render non-public note'); } return { @@ -113,7 +112,7 @@ export class ApRendererService { * @param block The block to be rendered. The blockee relation must be loaded. */ @bindThis - public renderBlock(block: Blocking) { + public renderBlock(block: Blocking): IBlock { if (block.blockee?.uri == null) { throw new Error('renderBlock: missing blockee uri'); } @@ -127,14 +126,14 @@ export class ApRendererService { } @bindThis - public renderCreate(object: any, note: Note) { + public renderCreate(object: IObject, note: Note): ICreate { const activity = { id: `${this.config.url}/notes/${note.id}/activity`, actor: `${this.config.url}/users/${note.userId}`, type: 'Create', published: note.createdAt.toISOString(), object, - } as any; + } as ICreate; if (object.to) activity.to = object.to; if (object.cc) activity.cc = object.cc; @@ -143,7 +142,7 @@ export class ApRendererService { } @bindThis - public renderDelete(object: any, user: { id: User['id']; host: null }) { + public renderDelete(object: IObject | string, user: { id: User['id']; host: null }): IDelete { return { type: 'Delete', actor: `${this.config.url}/users/${user.id}`, @@ -153,7 +152,7 @@ export class ApRendererService { } @bindThis - public renderDocument(file: DriveFile) { + public renderDocument(file: DriveFile): IApDocument { return { type: 'Document', mediaType: file.type, @@ -163,12 +162,12 @@ export class ApRendererService { } @bindThis - public renderEmoji(emoji: Emoji) { + public renderEmoji(emoji: Emoji): IApEmoji { return { id: `${this.config.url}/emojis/${emoji.name}`, type: 'Emoji', name: `:${emoji.name}:`, - updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString, + updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString(), icon: { type: 'Image', mediaType: emoji.type ?? 'image/png', @@ -179,9 +178,8 @@ export class ApRendererService { } // to anonymise reporters, the reporting actor must be a system user - // object has to be a uri or array of uris @bindThis - public renderFlag(user: ILocalUser, object: [string], content: string) { + public renderFlag(user: LocalUser, object: IObject | string, content: string): IFlag { return { type: 'Flag', actor: `${this.config.url}/users/${user.id}`, @@ -191,15 +189,13 @@ export class ApRendererService { } @bindThis - public renderFollowRelay(relay: Relay, relayActor: ILocalUser) { - const follow = { + public renderFollowRelay(relay: Relay, relayActor: LocalUser): IFollow { + return { id: `${this.config.url}/activities/follow-relay/${relay.id}`, type: 'Follow', actor: `${this.config.url}/users/${relayActor.id}`, object: 'https://www.w3.org/ns/activitystreams#Public', }; - - return follow; } /** @@ -217,19 +213,17 @@ export class ApRendererService { follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, requestId?: string, - ) { - const follow = { + ): IFollow { + return { id: requestId ?? `${this.config.url}/follows/${follower.id}/${followee.id}`, type: 'Follow', - actor: this.userEntityService.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri, - object: this.userEntityService.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri, - } as any; - - return follow; + actor: this.userEntityService.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri!, + object: this.userEntityService.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri!, + }; } @bindThis - public renderHashtag(tag: string) { + public renderHashtag(tag: string): IApHashtag { return { type: 'Hashtag', href: `${this.config.url}/tags/${encodeURIComponent(tag)}`, @@ -238,7 +232,7 @@ export class ApRendererService { } @bindThis - public renderImage(file: DriveFile) { + public renderImage(file: DriveFile): IApImage { return { type: 'Image', url: this.driveFileEntityService.getPublicUrl(file), @@ -248,7 +242,7 @@ export class ApRendererService { } @bindThis - public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { + public renderKey(user: LocalUser, key: UserKeypair, postfix?: string): IKey { return { id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, type: 'Key', @@ -261,7 +255,7 @@ export class ApRendererService { } @bindThis - public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }) { + public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }): Promise<ILike> { const reaction = noteReaction.reaction; const object = { @@ -271,10 +265,11 @@ export class ApRendererService { object: note.uri ? note.uri : `${this.config.url}/notes/${noteReaction.noteId}`, content: reaction, _misskey_reaction: reaction, - } as any; + } as ILike; if (reaction.startsWith(':')) { const name = reaction.replaceAll(':', ''); + // TODO: cache const emoji = await this.emojisRepository.findOneBy({ name, host: IsNull(), @@ -287,16 +282,16 @@ export class ApRendererService { } @bindThis - public renderMention(mention: User) { + public renderMention(mention: User): IApMention { return { type: 'Mention', - href: this.userEntityService.isRemoteUser(mention) ? mention.uri : `${this.config.url}/users/${(mention as ILocalUser).id}`, - name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`, + href: this.userEntityService.isRemoteUser(mention) ? mention.uri! : `${this.config.url}/users/${(mention as LocalUser).id}`, + name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as LocalUser).username}`, }; } @bindThis - public async renderNote(note: Note, dive = true, isTalk = false): Promise<IObject> { + public async renderNote(note: Note, dive = true): Promise<IPost> { const getPromisedFiles = async (ids: string[]) => { if (!ids || ids.length === 0) return []; const items = await this.driveFilesRepository.findBy({ id: In(ids) }); @@ -409,12 +404,8 @@ export class ApRendererService { totalItems: poll!.votes[i], }, })), - } : {}; - - const asTalk = isTalk ? { - _misskey_talk: true, - } : {}; - + } as const : {}; + return { id: `${this.config.url}/notes/${note.id}`, type: 'Note', @@ -436,12 +427,11 @@ export class ApRendererService { sensitive: note.cw != null || files.some(file => file.isSensitive), tag, ...asPoll, - ...asTalk, }; } @bindThis - public async renderPerson(user: ILocalUser) { + public async renderPerson(user: LocalUser) { const id = `${this.config.url}/users/${user.id}`; const isSystem = !!user.username.match(/\./); @@ -518,8 +508,8 @@ export class ApRendererService { } @bindThis - public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { - const question = { + public renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll): IQuestion { + return { type: 'Question', id: `${this.config.url}/questions/${note.id}`, actor: `${this.config.url}/users/${user.id}`, @@ -533,21 +523,10 @@ export class ApRendererService { }, })), }; - - return question; } @bindThis - public renderRead(user: { id: User['id'] }, message: MessagingMessage) { - return { - type: 'Read', - actor: `${this.config.url}/users/${user.id}`, - object: message.uri, - }; - } - - @bindThis - public renderReject(object: any, user: { id: User['id'] }) { + public renderReject(object: any, user: { id: User['id'] }): IReject { return { type: 'Reject', actor: `${this.config.url}/users/${user.id}`, @@ -556,7 +535,7 @@ export class ApRendererService { } @bindThis - public renderRemove(user: { id: User['id'] }, target: any, object: any) { + public renderRemove(user: { id: User['id'] }, target: any, object: any): IRemove { return { type: 'Remove', actor: `${this.config.url}/users/${user.id}`, @@ -566,7 +545,7 @@ export class ApRendererService { } @bindThis - public renderTombstone(id: string) { + public renderTombstone(id: string): ITombstone { return { id, type: 'Tombstone', @@ -574,8 +553,7 @@ export class ApRendererService { } @bindThis - public renderUndo(object: any, user: { id: User['id'] }) { - if (object == null) return null; + public renderUndo(object: any, user: { id: User['id'] }): IUndo { const id = typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined; return { @@ -588,21 +566,19 @@ export class ApRendererService { } @bindThis - public renderUpdate(object: any, user: { id: User['id'] }) { - const activity = { + public renderUpdate(object: any, user: { id: User['id'] }): IUpdate { + return { id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, actor: `${this.config.url}/users/${user.id}`, type: 'Update', to: ['https://www.w3.org/ns/activitystreams#Public'], object, published: new Date().toISOString(), - } as any; - - return activity; + }; } @bindThis - public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser) { + public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: RemoteUser): ICreate { return { id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`, actor: `${this.config.url}/users/${user.id}`, @@ -621,9 +597,7 @@ export class ApRendererService { } @bindThis - public renderActivity(x: any): IActivity | null { - if (x == null) return null; - + public addContext<T extends IObject>(x: T): T & { '@context': any; id: string; } { if (typeof x === 'object' && x.id == null) { x.id = `${this.config.url}/${uuid()}`; } @@ -653,13 +627,12 @@ export class ApRendererService { '_misskey_quote': 'misskey:_misskey_quote', '_misskey_reaction': 'misskey:_misskey_reaction', '_misskey_votes': 'misskey:_misskey_votes', - '_misskey_talk': 'misskey:_misskey_talk', 'isCat': 'misskey:isCat', // vcard vcard: 'http://www.w3.org/2006/vcard/ns#', }, ], - }, x); + }, x as T & { id: string; }); } @bindThis diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index 7e962cb127f73c144bdfd0c4ebd886d9c73d5086..df7bb464056d4f1f20d61f66258c637b68509c88 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { ILocalUser } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -18,7 +18,7 @@ import type { IObject, ICollection, IOrderedCollection } from './type.js'; export class Resolver { private history: Set<string>; - private user?: ILocalUser; + private user?: LocalUser; private logger: Logger; constructor( @@ -38,8 +38,7 @@ export class Resolver { private recursionLimit = 100, ) { this.history = new Set(); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - this.logger = this.loggerService?.getLogger('ap-resolve'); // ãªãœã‹ TypeError: Cannot read properties of undefined (reading 'getLogger') ã¨è¨€ã‚れる + this.logger = this.loggerService.getLogger('ap-resolve'); } @bindThis @@ -124,17 +123,17 @@ export class Resolver { switch (parsed.type) { case 'notes': return this.notesRepository.findOneByOrFail({ id: parsed.id }) - .then(note => { + .then(async note => { if (parsed.rest === 'activity') { // this refers to the create activity and not the note itself - return this.apRendererService.renderActivity(this.apRendererService.renderCreate(this.apRendererService.renderNote(note), note)); + return this.apRendererService.addContext(this.apRendererService.renderCreate(await this.apRendererService.renderNote(note), note)); } else { return this.apRendererService.renderNote(note); } }); case 'users': return this.usersRepository.findOneByOrFail({ id: parsed.id }) - .then(user => this.apRendererService.renderPerson(user as ILocalUser)); + .then(user => this.apRendererService.renderPerson(user as LocalUser)); case 'questions': // Polls are indexed by the note they are attached to. return Promise.all([ @@ -143,8 +142,8 @@ export class Resolver { ]) .then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll)); case 'likes': - return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(reaction => - this.apRendererService.renderActivity(this.apRendererService.renderLike(reaction, { uri: null }))!); + return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(async reaction => + this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null }))); case 'follows': // rest should be <followee id> if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI'); @@ -152,7 +151,7 @@ export class Resolver { return Promise.all( [parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })), ) - .then(([follower, followee]) => this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee, url))); + .then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee, url))); default: throw new Error(`resolveLocal: type ${parsed.type} unhandled`); } @@ -184,6 +183,7 @@ export class ApResolverService { private httpRequestService: HttpRequestService, private apRendererService: ApRendererService, private apDbResolverService: ApDbResolverService, + private loggerService: LoggerService, ) { } @@ -202,6 +202,7 @@ export class ApResolverService { this.httpRequestService, this.apRendererService, this.apDbResolverService, + this.loggerService, ); } } diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index a29e1be5642ee8e3accaa2d5988df3320d63f3ee..2dc1a410ac2f766996018cdcf5c1959ccd79cf36 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -1,6 +1,5 @@ import * as crypto from 'node:crypto'; -import { Inject, Injectable } from '@nestjs/common'; -import jsonld from 'jsonld'; +import { Injectable } from '@nestjs/common'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { bindThis } from '@/decorators.js'; import { CONTEXTS } from './misc/contexts.js'; @@ -85,7 +84,9 @@ class LdSignature { @bindThis public async normalize(data: any) { const customLoader = this.getLoader(); - return await jsonld.normalize(data, { + // XXX: Importing jsonld dynamically since Jest frequently fails to import it statically + // https://github.com/misskey-dev/misskey/pull/9894#discussion_r1103753595 + return (await import('jsonld')).default.normalize(data, { documentLoader: customLoader, }); } diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index 928ef1ae79a7466c8bdff066641c2bc2f4047c5c..3b671af1277b4eafdaddc52733026f9f12ac4447 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { RemoteUser } from '@/models/entities/User.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { MetaService } from '@/core/MetaService.js'; import { truncate } from '@/misc/truncate.js'; @@ -36,7 +36,7 @@ export class ApImageService { * Imageを作æˆã—ã¾ã™ã€‚ */ @bindThis - public async createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { + public async createImage(actor: RemoteUser, value: any): Promise<DriveFile> { // 投稿者ãŒå‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ if (actor.isSuspended) { throw new Error('actor has been suspended'); @@ -88,7 +88,7 @@ export class ApImageService { * リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ã‚§ãƒƒãƒã—ã¦Misskeyã«ç™»éŒ²ã—ãれを返ã—ã¾ã™ã€‚ */ @bindThis - public async resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { + public async resolveImage(actor: RemoteUser, value: any): Promise<DriveFile> { // TODO // リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ã‚§ãƒƒãƒã—ã¦ãã¦ç™»éŒ² diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index 41e6c6b14f889ee42c56d6ccd0c666c4d06d0a95..c581840ca948cd491e0df98ccc75655ba0dcb707 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -1,15 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; +import type { User } from '@/models/index.js'; import type { Config } from '@/config.js'; import { toArray, unique } from '@/misc/prelude/array.js'; -import type { CacheableUser } from '@/models/entities/User.js'; +import { bindThis } from '@/decorators.js'; import { isMention } from '../type.js'; import { ApResolverService, Resolver } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; import type { IObject, IApMention } from '../type.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ApMentionService { @@ -26,10 +25,10 @@ export class ApMentionService { public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); - const limit = promiseLimit<CacheableUser | null>(2); + const limit = promiseLimit<User | null>(2); const mentionedUsers = (await Promise.all( hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), - )).filter((x): x is CacheableUser => x != null); + )).filter((x): x is User => x != null); return mentionedUsers; } diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 813415e6f6b46aa94fe2ec76aa77794b35ed4d4a..c36e8d4ed6f1e2fdb4ea8c67e88a825b882cd554 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -1,9 +1,9 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; -import type { MessagingMessagesRepository, PollsRepository, EmojisRepository, UsersRepository } from '@/models/index.js'; +import type { PollsRepository, EmojisRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { RemoteUser } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; import type { Emoji } from '@/models/entities/Emoji.js'; @@ -16,7 +16,6 @@ import { IdService } from '@/core/IdService.js'; import { PollService } from '@/core/PollService.js'; import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { MessagingService } from '@/core/MessagingService.js'; import { bindThis } from '@/decorators.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports @@ -47,9 +46,6 @@ export class ApNoteService { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - private idService: IdService, private apMfmService: ApMfmService, private apResolverService: ApResolverService, @@ -64,7 +60,6 @@ export class ApNoteService { private apImageService: ApImageService, private apQuestionService: ApQuestionService, private metaService: MetaService, - private messagingService: MessagingService, private appLockService: AppLockService, private pollService: PollService, private noteCreateService: NoteCreateService, @@ -114,7 +109,7 @@ export class ApNoteService { public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { if (resolver == null) resolver = this.apResolverService.createResolver(); - const object: any = await resolver.resolve(value); + const object = await resolver.resolve(value); const entryUri = getApId(value); const err = this.validateNote(object, entryUri); @@ -129,7 +124,7 @@ export class ApNoteService { throw new Error('invalid note'); } - const note: IPost = object; + const note: IPost = object as any; this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); @@ -146,7 +141,7 @@ export class ApNoteService { this.logger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッム- const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; + const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo!), resolver) as RemoteUser; // 投稿者ãŒå‡çµã•ã‚Œã¦ã„ãŸã‚‰ã‚¹ã‚ップ if (actor.isSuspended) { @@ -165,8 +160,6 @@ export class ApNoteService { } } - let isMessaging = note._misskey_talk && visibility === 'specified'; - const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); const apHashtags = await extractApHashtags(note.tag); @@ -193,17 +186,6 @@ export class ApNoteService { return x; } }).catch(async err => { - // トークã ã£ãŸã‚‰inReplyToã®ã‚¨ãƒ©ãƒ¼ã¯ç„¡è¦– - const uri = getApId(note.inReplyTo); - if (uri.startsWith(this.config.url + '/')) { - const id = uri.split('/').pop(); - const talk = await this.messagingMessagesRepository.findOneBy({ id }); - if (talk) { - isMessaging = true; - return null; - } - } - this.logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`); throw err; }) @@ -292,14 +274,7 @@ export class ApNoteService { const apEmojis = emojis.map(emoji => emoji.name); const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - - if (isMessaging) { - for (const recipient of visibleUsers) { - await this.messagingService.createMessage(actor, recipient, undefined, text ?? undefined, (files && files.length > 0) ? files[0] : null, object.id); - return null; - } - } - + return await this.noteCreateService.create(actor, { createdAt: note.published ? new Date(note.published) : null, files, diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 76f820cda012ec620bb11bbbd4b9f5b78e560651..a1fdd7a198fe7c8fccc9cfe8f33bd69cbeffbdb2 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -1,11 +1,11 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { CacheableUser, IRemoteUser } from '@/models/entities/User.js'; +import type { RemoteUser } from '@/models/entities/User.js'; import { User } from '@/models/entities/User.js'; import { truncate } from '@/misc/truncate.js'; import type { UserCacheService } from '@/core/UserCacheService.js'; @@ -39,7 +39,7 @@ import type { ApResolverService, Resolver } from '../ApResolverService.js'; import type { ApLoggerService } from '../ApLoggerService.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import type { ApImageService } from './ApImageService.js'; -import type { IActor, IObject, IApPropertyValue } from '../type.js'; +import type { IActor, IObject } from '../type.js'; const nameLength = 128; const summaryLength = 2048; @@ -197,7 +197,7 @@ export class ApPersonService implements OnModuleInit { * Misskeyã«å¯¾è±¡ã®PersonãŒç™»éŒ²ã•ã‚Œã¦ã„ã‚Œã°ãれを返ã—ã¾ã™ã€‚ */ @bindThis - public async fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> { + public async fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> { if (typeof uri !== 'string') throw new Error('uri is not string'); const cached = this.userCacheService.uriPersonCache.get(uri); @@ -259,7 +259,7 @@ export class ApPersonService implements OnModuleInit { } // Create user - let user: IRemoteUser; + let user: RemoteUser; try { // Start transaction await this.db.transaction(async transactionalEntityManager => { @@ -284,7 +284,7 @@ export class ApPersonService implements OnModuleInit { isBot, isCat: (person as any).isCat === true, showTimelineReplies: false, - })) as IRemoteUser; + })) as RemoteUser; await transactionalEntityManager.save(new UserProfile({ userId: user.id, @@ -313,7 +313,7 @@ export class ApPersonService implements OnModuleInit { }); if (u) { - user = u as IRemoteUser; + user = u as RemoteUser; } else { throw new Error('already registered'); } @@ -392,7 +392,7 @@ export class ApPersonService implements OnModuleInit { } //#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•ã‚Œã¦ã„ã‚‹ã‹ - const exist = await this.usersRepository.findOneBy({ uri }) as IRemoteUser; + const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser; if (exist == null) { return; @@ -500,7 +500,7 @@ export class ApPersonService implements OnModuleInit { * リモートサーãƒãƒ¼ã‹ã‚‰ãƒ•ã‚§ãƒƒãƒã—ã¦Misskeyã«ç™»éŒ²ã—ãれを返ã—ã¾ã™ã€‚ */ @bindThis - public async resolvePerson(uri: string, resolver?: Resolver): Promise<CacheableUser> { + public async resolvePerson(uri: string, resolver?: Resolver): Promise<User> { if (typeof uri !== 'string') throw new Error('uri is not string'); //#region ã“ã®ã‚µãƒ¼ãƒãƒ¼ã«æ—¢ã«ç™»éŒ²ã•ã‚Œã¦ã„ãŸã‚‰ãれを返㙠diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index dcc5110aa50f58cd3a17cb78c71788d4f5b71f5c..7f2ca9c05e644f6a0a56396f7c7e579a5d16d220 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -1,25 +1,25 @@ -export type obj = { [x: string]: any }; +export type Obj = { [x: string]: any }; export type ApObject = IObject | string | (IObject | string)[]; export interface IObject { - '@context': string | string[] | obj | obj[]; + '@context'?: string | string[] | Obj | Obj[]; type: string | string[]; id?: string; + name?: string | null; summary?: string; published?: string; cc?: ApObject; to?: ApObject; - attributedTo: ApObject; + attributedTo?: ApObject; attachment?: any[]; inReplyTo?: any; replies?: ICollection; - content?: string; - name?: string; + content?: string | null; startTime?: Date; endTime?: Date; icon?: any; image?: any; - url?: ApObject; + url?: ApObject | string; href?: string; tag?: IObject | IObject[]; sensitive?: boolean; @@ -113,11 +113,11 @@ export interface IPost extends IObject { _misskey_quote?: string; _misskey_content?: string; quoteUrl?: string; - _misskey_talk?: boolean; } export interface IQuestion extends IObject { type: 'Note' | 'Question'; + actor: string; source?: { content: string; mediaType: string; @@ -200,6 +200,7 @@ export const isPropertyValue = (object: IObject): object is IApPropertyValue => export interface IApMention extends IObject { type: 'Mention'; href: string; + name: string; } export const isMention = (object: IObject): object is IApMention => @@ -217,12 +218,30 @@ export const isHashtag = (object: IObject): object is IApHashtag => export interface IApEmoji extends IObject { type: 'Emoji'; - updated: Date; + name: string; + updated: string; } export const isEmoji = (object: IObject): object is IApEmoji => getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null; +export interface IKey extends IObject { + type: 'Key'; + owner: string; + publicKeyPem: string | Buffer; +} + +export interface IApDocument extends IObject { + type: 'Document'; + name: string | null; + mediaType: string; +} + +export interface IApImage extends IObject { + type: 'Image'; + name: string | null; +} + export interface ICreate extends IActivity { type: 'Create'; } diff --git a/packages/backend/src/core/chart/ChartLoggerService.ts b/packages/backend/src/core/chart/ChartLoggerService.ts index d392c6d595badac33b62ac567a76fc0dfff4ab58..afd3bab5a2fe488c8471808f0969825e4dc9d162 100644 --- a/packages/backend/src/core/chart/ChartLoggerService.ts +++ b/packages/backend/src/core/chart/ChartLoggerService.ts @@ -1,7 +1,6 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ChartLoggerService { diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 779a32ac5ebc81f7ef0b8981eaf8d109d5232c9b..dbde7576764645c4b9f79ed62f66b3efe4547ccf 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -1,4 +1,4 @@ -import { Injectable, Inject } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import FederationChart from './charts/federation.js'; diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts index da36b944f5d46ff738248e1b015f56da5b04256a..b63db591fbbedfc9d0bee765b81af5f853f5df8e 100644 --- a/packages/backend/src/core/chart/charts/drive.ts +++ b/packages/backend/src/core/chart/charts/drive.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull, DataSource } from 'typeorm'; +import { DataSource } from 'typeorm'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index bc79ce26aabc8f1eb0ce12bdeb2cd509823cefda..c8a98c0bea3f096aa1ca6d6b917a15b63197807b 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { AntennaNotesRepository, AntennasRepository } from '@/models/index.js'; import type { Packed } from '@/misc/schema.js'; import type { Antenna } from '@/models/entities/Antenna.js'; import { bindThis } from '@/decorators.js'; @@ -14,9 +13,6 @@ export class AntennaEntityService { @Inject(DI.antennaNotesRepository) private antennaNotesRepository: AntennaNotesRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, ) { } @@ -27,7 +23,6 @@ export class AntennaEntityService { const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src }); const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null; - const userGroupJoining = antenna.userGroupJoiningId ? await this.userGroupJoiningsRepository.findOneBy({ id: antenna.userGroupJoiningId }) : null; return { id: antenna.id, @@ -37,7 +32,6 @@ export class AntennaEntityService { excludeKeywords: antenna.excludeKeywords, src: antenna.src, userListId: antenna.userListId, - userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null, users: antenna.users, caseSensitive: antenna.caseSensitive, notify: antenna.notify, diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 781cbdcc6b0323eefbffdabd4364fb8de4a6f0ca..36cd48f3ce00327a92fc9c25f9cbdbc67a89c4a8 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -1,11 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { AccessTokensRepository, AppsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { App } from '@/models/entities/App.js'; import type { User } from '@/models/entities/User.js'; -import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; @Injectable() diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index 4a74f9c2f639487884923c47155a8b67b32c946e..b7edc8494e7699ac31cc07ff0a7f6cc6bb7da5fe 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -2,10 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { AuthSessionsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { AuthSession } from '@/models/entities/AuthSession.js'; import type { User } from '@/models/entities/User.js'; -import { UserEntityService } from './UserEntityService.js'; import { AppEntityService } from './AppEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index 6ce590aa96a30b2413c2f6b82be8763cc9e6e124..0a9bcf85c4cc9186cd8fda52252ddc971147b7d4 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index 1e794391e920dea86018589eefa6fe0f360a8893..63c50865e0799e675c553191723bdd6746ca7b8d 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -4,7 +4,6 @@ import type { ClipsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { Clip } from '@/models/entities/Clip.js'; import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index b8550cd73ed12c2e41b5b7415e773427797b3b05..158fafa9d58f1b134729d1f83155d8a7a7c99b5e 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -1,6 +1,5 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { DataSource, In } from 'typeorm'; -import * as mfm from 'mfm-js'; +import { DataSource } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { NotesRepository, DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -11,9 +10,9 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import { appendQuery, query } from '@/misc/prelude/url.js'; import { deepClone } from '@/misc/clone.js'; import { UtilityService } from '../UtilityService.js'; +import { VideoProcessingService } from '../VideoProcessingService.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFolderEntityService } from './DriveFolderEntityService.js'; -import { VideoProcessingService } from '../VideoProcessingService.js'; type PackOptions = { detail?: boolean, @@ -74,14 +73,14 @@ export class DriveFileEntityService { } @bindThis - private getProxiedUrl(url: string, mode?: 'static' | 'avatar'): string | null { + private getProxiedUrl(url: string, mode?: 'static' | 'avatar'): string { return appendQuery( `${this.config.mediaProxy}/${mode ?? 'image'}.webp`, query({ url, ...(mode ? { [mode]: '1' } : {}), - }) - ) + }), + ); } @bindThis @@ -110,7 +109,7 @@ export class DriveFileEntityService { } @bindThis - public getPublicUrl(file: DriveFile, mode?: 'avatar'): string | null { // static = thumbnail + public getPublicUrl(file: DriveFile, mode?: 'avatar'): string { // static = thumbnail // リモートã‹ã¤ãƒ¡ãƒ‡ã‚£ã‚¢ãƒ—ãƒã‚ã‚· if (file.uri != null && file.userHost != null && this.config.externalMediaProxyEnabled) { return this.getProxiedUrl(file.uri, mode); diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 0bb0f1754e94dc0301c7aa426397831d3944840d..93c52c91f2966b80932b2f927455a36d0a7b9854 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -4,9 +4,7 @@ import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/inde import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { DriveFolder } from '@/models/entities/DriveFolder.js'; -import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; @Injectable() diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 7d248e6342633996ba4882e5d4fa20324661d921..f5c8f2d4bbde56abb66c4f08bd1598bf23593803 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -1,50 +1,63 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { EmojisRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { bindThis } from '@/decorators.js'; -import { UserEntityService } from './UserEntityService.js'; @Injectable() export class EmojiEntityService { constructor( @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - - private userEntityService: UserEntityService, ) { } @bindThis - public async pack( + public async packSimple( src: Emoji['id'] | Emoji, - opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = { omitHost: true, omitId: true, withUrl: true }, - ): Promise<Packed<'Emoji'>> { - opts = { omitHost: true, omitId: true, withUrl: true, ...opts } + ): Promise<Packed<'EmojiSimple'>> { + const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); + return { + aliases: emoji.aliases, + name: emoji.name, + category: emoji.category, + // || emoji.originalUrl ã—ã¦ã‚‹ã®ã¯å¾Œæ–¹äº’æ›æ€§ã®ãŸã‚(publicUrlã¯stringãªã®ã§??ã¯ã ã‚) + url: emoji.publicUrl || emoji.originalUrl, + }; + } + + @bindThis + public packSimpleMany( + emojis: any[], + ) { + return Promise.all(emojis.map(x => this.packSimple(x))); + } + + @bindThis + public async packDetailed( + src: Emoji['id'] | Emoji, + ): Promise<Packed<'EmojiDetailed'>> { const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); return { - id: opts.omitId ? undefined : emoji.id, + id: emoji.id, aliases: emoji.aliases, name: emoji.name, category: emoji.category, - host: opts.omitHost ? undefined : emoji.host, + host: emoji.host, // || emoji.originalUrl ã—ã¦ã‚‹ã®ã¯å¾Œæ–¹äº’æ›æ€§ã®ãŸã‚(publicUrlã¯stringãªã®ã§??ã¯ã ã‚) - url: opts.withUrl ? (emoji.publicUrl || emoji.originalUrl) : undefined, + url: emoji.publicUrl || emoji.originalUrl, }; } @bindThis - public packMany( + public packDetailedMany( emojis: any[], - opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = {}, ) { - return Promise.all(emojis.map(x => this.pack(x, opts))); + return Promise.all(emojis.map(x => this.packDetailed(x))); } } diff --git a/packages/backend/src/core/entities/FlashLikeEntityService.ts b/packages/backend/src/core/entities/FlashLikeEntityService.ts index dcf12d53ea2f52670409c04426bb52bbdd9fded5..0351ec3014eee5c31ad351732887371d206d48f4 100644 --- a/packages/backend/src/core/entities/FlashLikeEntityService.ts +++ b/packages/backend/src/core/entities/FlashLikeEntityService.ts @@ -1,13 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { FlashLikesRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { FlashLike } from '@/models/entities/FlashLike.js'; import { bindThis } from '@/decorators.js'; -import { UserEntityService } from './UserEntityService.js'; import { FlashEntityService } from './FlashEntityService.js'; @Injectable() diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index 88c91d0f218971c09dc4cde8694f1613dfab4c2f..c2edc6a13a8b7fd3fdaa1198043367880feb5edc 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -1,8 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { FollowRequestsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { FollowRequest } from '@/models/entities/FollowRequest.js'; diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index 8b15ffc2bbbc60bf9bd5daa10b6ddc906fc1c0a4..db46045db3efcd2bc842e6474bf4f9e24d489780 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -1,12 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { GalleryLikesRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { GalleryLike } from '@/models/entities/GalleryLike.js'; -import { UserEntityService } from './UserEntityService.js'; import { GalleryPostEntityService } from './GalleryPostEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index f79b8212225562fb67c847bdfd9961c95f2b046f..88b1ff3a38b8d4b11ce3f7d935680f71a086aff6 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -1,10 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { HashtagsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { Hashtag } from '@/models/entities/Hashtag.js'; import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 42ea5e23f660429a6bca348dc6e522cd6c323ff3..8a9706816b3c6626c9016aca1ce41dc548992f81 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -1,10 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { InstancesRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { Instance } from '@/models/entities/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { UtilityService } from '../UtilityService.js'; diff --git a/packages/backend/src/core/entities/MessagingMessageEntityService.ts b/packages/backend/src/core/entities/MessagingMessageEntityService.ts deleted file mode 100644 index cdb752dd8197c36735cfd6b8dec8e0637eb00e9f..0000000000000000000000000000000000000000 --- a/packages/backend/src/core/entities/MessagingMessageEntityService.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { MessagingMessagesRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; -import { UserEntityService } from './UserEntityService.js'; -import { DriveFileEntityService } from './DriveFileEntityService.js'; -import { UserGroupEntityService } from './UserGroupEntityService.js'; -import { bindThis } from '@/decorators.js'; - -@Injectable() -export class MessagingMessageEntityService { - constructor( - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - private userEntityService: UserEntityService, - private userGroupEntityService: UserGroupEntityService, - private driveFileEntityService: DriveFileEntityService, - ) { - } - - @bindThis - public async pack( - src: MessagingMessage['id'] | MessagingMessage, - me?: { id: User['id'] } | null | undefined, - options?: { - populateRecipient?: boolean, - populateGroup?: boolean, - }, - ): Promise<Packed<'MessagingMessage'>> { - const opts = options ?? { - populateRecipient: true, - populateGroup: true, - }; - - const message = typeof src === 'object' ? src : await this.messagingMessagesRepository.findOneByOrFail({ id: src }); - - return { - id: message.id, - createdAt: message.createdAt.toISOString(), - text: message.text, - userId: message.userId, - user: await this.userEntityService.pack(message.user ?? message.userId, me), - recipientId: message.recipientId, - recipient: message.recipientId && opts.populateRecipient ? await this.userEntityService.pack(message.recipient ?? message.recipientId, me) : undefined, - groupId: message.groupId, - group: message.groupId && opts.populateGroup ? await this.userGroupEntityService.pack(message.group ?? message.groupId) : undefined, - fileId: message.fileId, - file: message.fileId ? await this.driveFileEntityService.pack(message.fileId) : null, - isRead: message.isRead, - reads: message.reads, - }; - } -} - diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index ab617979105f80814f01ea563c81543d1520e36a..7058e38af985f92d7607c9c994c3d892f0a3316a 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -2,9 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { ModerationLogsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { ModerationLog } from '@/models/entities/ModerationLog.js'; import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index bd6971adb345b6be5e9dd1499e4a4a8aac32f970..2ffe5f1c21795d79468bce70425dfe593e1c1b6f 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -1,9 +1,8 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index aa5c354b6d52d9a1a64b949f2104d776789b2874..8a7727b4cd8bcebbab2eb6c82f11b9bd268ae696 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -1,12 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { NoteFavoritesRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { NoteFavorite } from '@/models/entities/NoteFavorite.js'; -import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index eba6f9d9080c54440617a99ec7badb2daeae1293..97794896730a60b84667d51af67be3d7b5b3d365 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { NoteReactionsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { OnModuleInit } from '@nestjs/common'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 4140b3f35e8dd33522df19ca3bfd82f7c2ca76a5..34ff52ede84ca842d89d0d44cf521c083b8d6f80 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -13,13 +13,11 @@ import type { OnModuleInit } from '@nestjs/common'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; -import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; @Injectable() export class NotificationEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; - private userGroupInvitationEntityService: UserGroupInvitationEntityService; private customEmojiService: CustomEmojiService; constructor( @@ -36,7 +34,6 @@ export class NotificationEntityService implements OnModuleInit { //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, - //private userGroupInvitationEntityService: UserGroupInvitationEntityService, //private customEmojiService: CustomEmojiService, ) { } @@ -44,7 +41,6 @@ export class NotificationEntityService implements OnModuleInit { onModuleInit() { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); - this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); } @@ -111,9 +107,6 @@ export class NotificationEntityService implements OnModuleInit { _hint_: options._hintForEachNotes_, }), } : {}), - ...(notification.type === 'groupInvited' ? { - invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!), - } : {}), ...(notification.type === 'achievementEarned' ? { achievement: notification.achievement, } : {}), diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index d3e45783dd7527481e51168cb83af9a533d6bf3f..3460c1e4227af97105e936cfb427ed83d380d62f 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -1,12 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { PageLikesRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { PageLike } from '@/models/entities/PageLike.js'; -import { UserEntityService } from './UserEntityService.js'; import { PageEntityService } from './PageEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index dbb89ff19b87ac59ad4dd13b22836f6667dddaf9..80ef5ac1fa9aacf1fd1bd02079f3fbb2c385451f 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { User } from '@/models/entities/User.js'; import type { Role } from '@/models/entities/Role.js'; import { bindThis } from '@/decorators.js'; @@ -26,14 +25,7 @@ export class RoleEntityService { public async pack( src: Role['id'] | Role, me?: { id: User['id'] } | null | undefined, - options?: { - detail?: boolean; - }, ) { - const opts = Object.assign({ - detail: true, - }, options); - const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src }); const assigns = await this.roleAssignmentsRepository.findBy({ @@ -66,9 +58,6 @@ export class RoleEntityService { canEditMembersByModerator: role.canEditMembersByModerator, policies: policies, usersCount: assigns.length, - ...(opts.detail ? { - users: this.userEntityService.packMany(assigns.map(x => x.userId), me), - } : {}), }); } @@ -76,11 +65,8 @@ export class RoleEntityService { public packMany( roles: any[], me: { id: User['id'] }, - options?: { - detail?: boolean; - }, ) { - return Promise.all(roles.map(x => this.pack(x, me, options))); + return Promise.all(roles.map(x => this.pack(x, me))); } } diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index c402644742aede29cf213507710e05df533a49fd..51fa7543d956d4d4b5bcf3959fe4dc7cfc3e1382 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -1,10 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { SigninsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { Signin } from '@/models/entities/Signin.js'; import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index eea9d5567d16b399e7371429c8f3f1877a3565ef..8c36e47f1b59f775c589b0a7f28511af770523ab 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -1,4 +1,4 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { In, Not } from 'typeorm'; import Ajv from 'ajv'; import { ModuleRef } from '@nestjs/core'; @@ -10,9 +10,9 @@ import { awaitAll } from '@/misc/prelude/await-all.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/Instance.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; -import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js'; +import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -32,13 +32,13 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo const ajv = new Ajv(); -function isLocalUser(user: User): user is ILocalUser; +function isLocalUser(user: User): user is LocalUser; function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; }; function isLocalUser(user: User | { host: User['host'] }): boolean { return user.host == null; } -function isRemoteUser(user: User): user is IRemoteUser; +function isRemoteUser(user: User): user is RemoteUser; function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; }; function isRemoteUser(user: User | { host: User['host'] }): boolean { return !isLocalUser(user); @@ -102,12 +102,6 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.announcementReadsRepository) private announcementReadsRepository: AnnouncementReadsRepository, - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - @Inject(DI.announcementsRepository) private announcementsRepository: AnnouncementsRepository, @@ -204,36 +198,6 @@ export class UserEntityService implements OnModuleInit { }); } - @bindThis - public async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> { - const mute = await this.mutingsRepository.findBy({ - muterId: userId, - }); - - const joinings = await this.userGroupJoiningsRepository.findBy({ userId: userId }); - - const groupQs = Promise.all(joinings.map(j => this.messagingMessagesRepository.createQueryBuilder('message') - .where('message.groupId = :groupId', { groupId: j.userGroupId }) - .andWhere('message.userId != :userId', { userId: userId }) - .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) - .andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分ãŒåŠ å…¥ã™ã‚‹å‰ã®ä¼šè©±ã«ã¤ã„ã¦ã¯ã€æœªèªæ‰±ã„ã—ãªã„ - .getOne().then(x => x != null))); - - const [withUser, withGroups] = await Promise.all([ - this.messagingMessagesRepository.count({ - where: { - recipientId: userId, - isRead: false, - ...(mute.length > 0 ? { userId: Not(In(mute.map(x => x.muteeId))) } : {}), - }, - take: 1, - }).then(count => count > 0), - groupQs, - ]); - - return withUser || withGroups.some(x => x); - } - @bindThis public async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> { const reads = await this.announcementReadsRepository.findBy({ @@ -492,7 +456,6 @@ export class UserEntityService implements OnModuleInit { hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id), hasUnreadAntenna: this.getHasUnreadAntenna(user.id), hasUnreadChannel: this.getHasUnreadChannel(user.id), - hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id), hasUnreadNotification: this.getHasUnreadNotification(user.id), hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), mutedWords: profile!.mutedWords, diff --git a/packages/backend/src/core/entities/UserGroupEntityService.ts b/packages/backend/src/core/entities/UserGroupEntityService.ts deleted file mode 100644 index 0674a767232b6d0e88ed164c73de2bb8d3320005..0000000000000000000000000000000000000000 --- a/packages/backend/src/core/entities/UserGroupEntityService.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { UserGroupJoiningsRepository, UserGroupsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { UserEntityService } from './UserEntityService.js'; -import { bindThis } from '@/decorators.js'; - -@Injectable() -export class UserGroupEntityService { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private userEntityService: UserEntityService, - ) { - } - - @bindThis - public async pack( - src: UserGroup['id'] | UserGroup, - ): Promise<Packed<'UserGroup'>> { - const userGroup = typeof src === 'object' ? src : await this.userGroupsRepository.findOneByOrFail({ id: src }); - - const users = await this.userGroupJoiningsRepository.findBy({ - userGroupId: userGroup.id, - }); - - return { - id: userGroup.id, - createdAt: userGroup.createdAt.toISOString(), - name: userGroup.name, - ownerId: userGroup.userId, - userIds: users.map(x => x.userId), - }; - } -} - diff --git a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts deleted file mode 100644 index 0fba1426f444733fa2924529ab33bf69b0a2ec25..0000000000000000000000000000000000000000 --- a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { UserGroupInvitationsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; -import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; -import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; -import { UserEntityService } from './UserEntityService.js'; -import { UserGroupEntityService } from './UserGroupEntityService.js'; -import { bindThis } from '@/decorators.js'; - -@Injectable() -export class UserGroupInvitationEntityService { - constructor( - @Inject(DI.userGroupInvitationsRepository) - private userGroupInvitationsRepository: UserGroupInvitationsRepository, - - private userGroupEntityService: UserGroupEntityService, - ) { - } - - @bindThis - public async pack( - src: UserGroupInvitation['id'] | UserGroupInvitation, - ) { - const invitation = typeof src === 'object' ? src : await this.userGroupInvitationsRepository.findOneByOrFail({ id: src }); - - return { - id: invitation.id, - group: await this.userGroupEntityService.pack(invitation.userGroup ?? invitation.userGroupId), - }; - } - - @bindThis - public packMany( - invitations: any[], - ) { - return Promise.all(invitations.map(x => this.pack(x))); - } -} - diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index f2e04269287c4cd0c34d97a7a916768822a7355a..3c1087881a3b5562de4efab801318fee22f8e5d8 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -1,10 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; -import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; -import type { User } from '@/models/entities/User.js'; import type { UserList } from '@/models/entities/UserList.js'; import { UserEntityService } from './UserEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index 7b47d78a17c005ce38eee27b1c2b33ba6e0ff471..b717434e09c65ee2665b314fdb0c94eb2d333d9a 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -1,6 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import Xev from 'xev'; -import { DI } from '@/di-symbols.js'; import { QueueService } from '@/core/QueueService.js'; import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index 7971f9e810d58ae3fc378bd0054e82394e0a27a0..1688bb2ba7cc7935b2fa2ae21b2a994d4e5e56d3 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -1,8 +1,7 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import si from 'systeminformation'; import Xev from 'xev'; import * as osUtils from 'os-utils'; -import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 3fb0cd4dae003a5a4b49931b392047e297637363..05603093be6639a4f5b981cb9c8f3b0aa0313298 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -24,9 +24,6 @@ export const DI = { userPublickeysRepository: Symbol('userPublickeysRepository'), userListsRepository: Symbol('userListsRepository'), userListJoiningsRepository: Symbol('userListJoiningsRepository'), - userGroupsRepository: Symbol('userGroupsRepository'), - userGroupJoiningsRepository: Symbol('userGroupJoiningsRepository'), - userGroupInvitationsRepository: Symbol('userGroupInvitationsRepository'), userNotePiningsRepository: Symbol('userNotePiningsRepository'), userIpsRepository: Symbol('userIpsRepository'), usedUsernamesRepository: Symbol('usedUsernamesRepository'), @@ -47,7 +44,6 @@ export const DI = { authSessionsRepository: Symbol('authSessionsRepository'), accessTokensRepository: Symbol('accessTokensRepository'), signinsRepository: Symbol('signinsRepository'), - messagingMessagesRepository: Symbol('messagingMessagesRepository'), pagesRepository: Symbol('pagesRepository'), pageLikesRepository: Symbol('pageLikesRepository'), galleryPostsRepository: Symbol('galleryPostsRepository'), diff --git a/packages/backend/src/misc/emoji-regex.ts b/packages/backend/src/misc/emoji-regex.ts index ca224971c5c5782c93e682e1968b95fb14ebbf28..1c6f5776dbc50cca71881820d763207edd959a7e 100644 --- a/packages/backend/src/misc/emoji-regex.ts +++ b/packages/backend/src/misc/emoji-regex.ts @@ -1,4 +1,4 @@ -import twemoji from 'twemoji-parser/dist/lib/regex.js'; -const twemojiRegex = twemoji.default; +// taken from twemoji-parser/dist/lib/regex.js +const twemojiRegex = /(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd\udec3-\udec5\udef0-\udef6]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedd-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude74\ude78-\ude7c\ude80-\ude86\ude90-\udeac\udeb0-\udeba\udec0-\udec2\uded0-\uded9\udee0-\udee7]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g; export const emojiRegex = new RegExp(`(${twemojiRegex.source})`); diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index 7aeb65f296f62b35a1c2ea9d9c20d8c01be56e2d..7fc4a3e6544b83a5a2e67fe277aa803ddc119811 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -10,7 +10,6 @@ import { import { packedNoteSchema } from '@/models/schema/note.js'; import { packedUserListSchema } from '@/models/schema/user-list.js'; import { packedAppSchema } from '@/models/schema/app.js'; -import { packedMessagingMessageSchema } from '@/models/schema/messaging-message.js'; import { packedNotificationSchema } from '@/models/schema/notification.js'; import { packedDriveFileSchema } from '@/models/schema/drive-file.js'; import { packedDriveFolderSchema } from '@/models/schema/drive-folder.js'; @@ -20,7 +19,6 @@ import { packedBlockingSchema } from '@/models/schema/blocking.js'; import { packedNoteReactionSchema } from '@/models/schema/note-reaction.js'; import { packedHashtagSchema } from '@/models/schema/hashtag.js'; import { packedPageSchema } from '@/models/schema/page.js'; -import { packedUserGroupSchema } from '@/models/schema/user-group.js'; import { packedNoteFavoriteSchema } from '@/models/schema/note-favorite.js'; import { packedChannelSchema } from '@/models/schema/channel.js'; import { packedAntennaSchema } from '@/models/schema/antenna.js'; @@ -28,7 +26,8 @@ import { packedClipSchema } from '@/models/schema/clip.js'; import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js'; import { packedQueueCountSchema } from '@/models/schema/queue.js'; import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js'; -import { packedEmojiSchema } from '@/models/schema/emoji.js'; +import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/schema/emoji.js'; +import { packedFlashSchema } from '@/models/schema/flash.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -40,9 +39,7 @@ export const refs = { User: packedUserSchema, UserList: packedUserListSchema, - UserGroup: packedUserGroupSchema, App: packedAppSchema, - MessagingMessage: packedMessagingMessageSchema, Note: packedNoteSchema, NoteReaction: packedNoteReactionSchema, NoteFavorite: packedNoteFavoriteSchema, @@ -60,7 +57,9 @@ export const refs = { Clip: packedClipSchema, FederationInstance: packedFederationInstanceSchema, GalleryPost: packedGalleryPostSchema, - Emoji: packedEmojiSchema, + EmojiSimple: packedEmojiSimpleSchema, + EmojiDetailed: packedEmojiDetailedSchema, + Flash: packedFlashSchema, }; export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>; diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 2a235bc6fc35e4d31aa193e85ee885cd1563a3ef..311f875ba51ec7562233334e58ba004c3d5500fd 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js'; +import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -118,24 +118,6 @@ const $userListJoiningsRepository: Provider = { inject: [DI.db], }; -const $userGroupsRepository: Provider = { - provide: DI.userGroupsRepository, - useFactory: (db: DataSource) => db.getRepository(UserGroup), - inject: [DI.db], -}; - -const $userGroupJoiningsRepository: Provider = { - provide: DI.userGroupJoiningsRepository, - useFactory: (db: DataSource) => db.getRepository(UserGroupJoining), - inject: [DI.db], -}; - -const $userGroupInvitationsRepository: Provider = { - provide: DI.userGroupInvitationsRepository, - useFactory: (db: DataSource) => db.getRepository(UserGroupInvitation), - inject: [DI.db], -}; - const $userNotePiningsRepository: Provider = { provide: DI.userNotePiningsRepository, useFactory: (db: DataSource) => db.getRepository(UserNotePining), @@ -256,12 +238,6 @@ const $signinsRepository: Provider = { inject: [DI.db], }; -const $messagingMessagesRepository: Provider = { - provide: DI.messagingMessagesRepository, - useFactory: (db: DataSource) => db.getRepository(MessagingMessage), - inject: [DI.db], -}; - const $pagesRepository: Provider = { provide: DI.pagesRepository, useFactory: (db: DataSource) => db.getRepository(Page), @@ -435,9 +411,6 @@ const $roleAssignmentsRepository: Provider = { $userPublickeysRepository, $userListsRepository, $userListJoiningsRepository, - $userGroupsRepository, - $userGroupJoiningsRepository, - $userGroupInvitationsRepository, $userNotePiningsRepository, $userIpsRepository, $usedUsernamesRepository, @@ -458,7 +431,6 @@ const $roleAssignmentsRepository: Provider = { $authSessionsRepository, $accessTokensRepository, $signinsRepository, - $messagingMessagesRepository, $pagesRepository, $pageLikesRepository, $galleryPostsRepository, @@ -505,9 +477,6 @@ const $roleAssignmentsRepository: Provider = { $userPublickeysRepository, $userListsRepository, $userListJoiningsRepository, - $userGroupsRepository, - $userGroupJoiningsRepository, - $userGroupInvitationsRepository, $userNotePiningsRepository, $userIpsRepository, $usedUsernamesRepository, @@ -528,7 +497,6 @@ const $roleAssignmentsRepository: Provider = { $authSessionsRepository, $accessTokensRepository, $signinsRepository, - $messagingMessagesRepository, $pagesRepository, $pageLikesRepository, $galleryPostsRepository, diff --git a/packages/backend/src/models/entities/Ad.ts b/packages/backend/src/models/entities/Ad.ts index 36b758f205b6d5395c31b31ef31534b4043d0a54..56baf863caf1ea254131cc61b0a5ac04853ccb9b 100644 --- a/packages/backend/src/models/entities/Ad.ts +++ b/packages/backend/src/models/entities/Ad.ts @@ -18,6 +18,13 @@ export class Ad { }) public expiresAt: Date; + @Index() + @Column('timestamp with time zone', { + comment: 'The expired date of the Ad.', + default: () => 'now()', + }) + public startsAt: Date; + @Column('varchar', { length: 32, nullable: false, }) diff --git a/packages/backend/src/models/entities/Antenna.ts b/packages/backend/src/models/entities/Antenna.ts index 860fd9cf5544581df9a0639d413587bcc4b8ad16..5b2164ef17c8587d6e29b6fa25243eadbabb2186 100644 --- a/packages/backend/src/models/entities/Antenna.ts +++ b/packages/backend/src/models/entities/Antenna.ts @@ -2,7 +2,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ import { id } from '../id.js'; import { User } from './User.js'; import { UserList } from './UserList.js'; -import { UserGroupJoining } from './UserGroupJoining.js'; @Entity() export class Antenna { @@ -33,8 +32,8 @@ export class Antenna { }) public name: string; - @Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] }) - public src: 'home' | 'all' | 'users' | 'list' | 'group'; + @Column('enum', { enum: ['home', 'all', 'users', 'list'] }) + public src: 'home' | 'all' | 'users' | 'list'; @Column({ ...id(), @@ -48,18 +47,6 @@ export class Antenna { @JoinColumn() public userList: UserList | null; - @Column({ - ...id(), - nullable: true, - }) - public userGroupJoiningId: UserGroupJoining['id'] | null; - - @ManyToOne(type => UserGroupJoining, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroupJoining: UserGroupJoining | null; - @Column('varchar', { length: 1024, array: true, default: '{}', diff --git a/packages/backend/src/models/entities/Flash.ts b/packages/backend/src/models/entities/Flash.ts index 07039d4fa18027ed2389aa0189af111ce0db6d30..192f7e7bc49dc6d5a0d47cf6b39684bfe746272e 100644 --- a/packages/backend/src/models/entities/Flash.ts +++ b/packages/backend/src/models/entities/Flash.ts @@ -1,7 +1,6 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; import { id } from '../id.js'; import { User } from './User.js'; -import { DriveFile } from './DriveFile.js'; @Entity() export class Flash { diff --git a/packages/backend/src/models/entities/MessagingMessage.ts b/packages/backend/src/models/entities/MessagingMessage.ts deleted file mode 100644 index 69fc9815d40a8da5025875f1afa69fdd860ed051..0000000000000000000000000000000000000000 --- a/packages/backend/src/models/entities/MessagingMessage.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { DriveFile } from './DriveFile.js'; -import { UserGroup } from './UserGroup.js'; - -@Entity() -export class MessagingMessage { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the MessagingMessage.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The sender user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), nullable: true, - comment: 'The recipient user ID.', - }) - public recipientId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public recipient: User | null; - - @Index() - @Column({ - ...id(), nullable: true, - comment: 'The recipient group ID.', - }) - public groupId: UserGroup['id'] | null; - - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public group: UserGroup | null; - - @Column('varchar', { - length: 4096, nullable: true, - }) - public text: string | null; - - @Column('boolean', { - default: false, - }) - public isRead: boolean; - - @Column('varchar', { - length: 512, nullable: true, - }) - public uri: string | null; - - @Column({ - ...id(), - array: true, default: '{}', - }) - public reads: User['id'][]; - - @Column({ - ...id(), - nullable: true, - }) - public fileId: DriveFile['id'] | null; - - @ManyToOne(type => DriveFile, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public file: DriveFile | null; -} diff --git a/packages/backend/src/models/entities/NoteThreadMuting.ts b/packages/backend/src/models/entities/NoteThreadMuting.ts index a23176b99436ec38cc40609c911726f3c437889a..3c884fe615a715f88645437e2647195511457962 100644 --- a/packages/backend/src/models/entities/NoteThreadMuting.ts +++ b/packages/backend/src/models/entities/NoteThreadMuting.ts @@ -1,7 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; import { id } from '../id.js'; import { User } from './User.js'; -import { Note } from './Note.js'; @Entity() @Index(['userId', 'threadId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts index 66f131d1c027d5760b96d7572c070ebf88a2b624..105f0d0407bdb6c36fb00df1ee8359970d5b8d51 100644 --- a/packages/backend/src/models/entities/Notification.ts +++ b/packages/backend/src/models/entities/Notification.ts @@ -4,7 +4,6 @@ import { id } from '../id.js'; import { User } from './User.js'; import { Note } from './Note.js'; import { FollowRequest } from './FollowRequest.js'; -import { UserGroupInvitation } from './UserGroupInvitation.js'; import { AccessToken } from './AccessToken.js'; @Entity() @@ -63,7 +62,6 @@ export class Notification { * pollEnded - 自分ã®ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆã‚‚ã—ãã¯è‡ªåˆ†ãŒæŠ•ç¥¨ã—ãŸã‚¢ãƒ³ã‚±ãƒ¼ãƒˆãŒçµ‚了ã—㟠* receiveFollowRequest - フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã•ã‚ŒãŸ * followRequestAccepted - 自分ã®é€ã£ãŸãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒæ‰¿èªã•ã‚ŒãŸ - * groupInvited - グループã«æ‹›å¾…ã•ã‚ŒãŸ * achievementEarned - 実績をç²å¾— * app - アプリ通知 */ @@ -108,18 +106,6 @@ export class Notification { @JoinColumn() public followRequest: FollowRequest | null; - @Column({ - ...id(), - nullable: true, - }) - public userGroupInvitationId: UserGroupInvitation['id'] | null; - - @ManyToOne(type => UserGroupInvitation, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroupInvitation: UserGroupInvitation | null; - @Column('varchar', { length: 128, nullable: true, }) diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts index 8cf6811863b420db2c6faf741b8197ffcc377626..399e9ead05ea414c9463b4df57cd6220329012fc 100644 --- a/packages/backend/src/models/entities/Role.ts +++ b/packages/backend/src/models/entities/Role.ts @@ -1,4 +1,4 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { Entity, Column, PrimaryColumn } from 'typeorm'; import { id } from '../id.js'; type CondFormulaValueAnd = { diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/entities/User.ts index 1cfcc814ea2b5289cca9ae839412f3a2f9cb0a09..0a8b89ea06131bb75c968dae63ead83a7bb6b6e8 100644 --- a/packages/backend/src/models/entities/User.ts +++ b/packages/backend/src/models/entities/User.ts @@ -215,20 +215,16 @@ export class User { } } -export interface ILocalUser extends User { +export type LocalUser = User & { host: null; + uri: null; } -export interface IRemoteUser extends User { +export type RemoteUser = User & { host: string; + uri: string; } -export type CacheableLocalUser = ILocalUser; - -export type CacheableRemoteUser = IRemoteUser; - -export type CacheableUser = CacheableLocalUser | CacheableRemoteUser; - export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; export const passwordSchema = { type: 'string', minLength: 1 } as const; export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; diff --git a/packages/backend/src/models/entities/UserGroup.ts b/packages/backend/src/models/entities/UserGroup.ts deleted file mode 100644 index 328a1883cb9e86084c6072ce68384aba775335ef..0000000000000000000000000000000000000000 --- a/packages/backend/src/models/entities/UserGroup.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; - -@Entity() -export class UserGroup { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroup.', - }) - public createdAt: Date; - - @Column('varchar', { - length: 256, - }) - public name: string; - - @Index() - @Column({ - ...id(), - comment: 'The ID of owner.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('boolean', { - default: false, - }) - public isPrivate: boolean; - - constructor(data: Partial<UserGroup>) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/UserGroupInvitation.ts b/packages/backend/src/models/entities/UserGroupInvitation.ts deleted file mode 100644 index e4aa3ccae14bb3d5b530c9e05d775c87d7a800e9..0000000000000000000000000000000000000000 --- a/packages/backend/src/models/entities/UserGroupInvitation.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { UserGroup } from './UserGroup.js'; - -@Entity() -@Index(['userId', 'userGroupId'], { unique: true }) -export class UserGroupInvitation { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroupInvitation.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The group ID.', - }) - public userGroupId: UserGroup['id']; - - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroup: UserGroup | null; -} diff --git a/packages/backend/src/models/entities/UserGroupJoining.ts b/packages/backend/src/models/entities/UserGroupJoining.ts deleted file mode 100644 index fae7241525197644fe24a9042619790ef950413d..0000000000000000000000000000000000000000 --- a/packages/backend/src/models/entities/UserGroupJoining.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './User.js'; -import { UserGroup } from './UserGroup.js'; - -@Entity() -@Index(['userId', 'userGroupId'], { unique: true }) -export class UserGroupJoining { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroupJoining.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The group ID.', - }) - public userGroupId: UserGroup['id']; - - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroup: UserGroup | null; -} diff --git a/packages/backend/src/models/entities/UserIp.ts b/packages/backend/src/models/entities/UserIp.ts index e9afd40d499600663c40c135fc04d07f9846ac4e..628e3d0361a1acf3697af1fb711ef1dda3f4a356 100644 --- a/packages/backend/src/models/entities/UserIp.ts +++ b/packages/backend/src/models/entities/UserIp.ts @@ -1,6 +1,5 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Entity, Index, Column, PrimaryGeneratedColumn } from 'typeorm'; import { id } from '../id.js'; -import { Note } from './Note.js'; import type { User } from './User.js'; @Entity() diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index 1ff261cda31cb649e56f3def47dc066065a4bed0..3d35b4cb5ae5ff31d89db6ac3fd728f2ec5f39e0 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -71,7 +71,7 @@ export class UserProfile { public emailVerified: boolean; @Column('jsonb', { - default: ['follow', 'receiveFollowRequest', 'groupInvited'], + default: ['follow', 'receiveFollowRequest'], }) public emailNotificationTypes: string[]; @@ -202,7 +202,11 @@ export class UserProfile { public mutedInstances: string[]; @Column('enum', { - enum: notificationTypes, + enum: [ + ...notificationTypes, + // マイグレーションã§å‰Šé™¤ãŒå›°é›£ãªã®ã§å¤ã„enumã¯æ®‹ã—ã¦ãŠã + 'groupInvited', + ], array: true, default: [], }) diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 50697597ad920478b93f5c708690f893f63d48e6..25ed9b89d895af72242ccc88befee6328705b45c 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -22,7 +22,6 @@ import { GalleryLike } from '@/models/entities/GalleryLike.js'; import { GalleryPost } from '@/models/entities/GalleryPost.js'; import { Hashtag } from '@/models/entities/Hashtag.js'; import { Instance } from '@/models/entities/Instance.js'; -import { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { Meta } from '@/models/entities/Meta.js'; import { ModerationLog } from '@/models/entities/ModerationLog.js'; import { MutedNote } from '@/models/entities/MutedNote.js'; @@ -47,9 +46,6 @@ import { Signin } from '@/models/entities/Signin.js'; import { SwSubscription } from '@/models/entities/SwSubscription.js'; import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { User } from '@/models/entities/User.js'; -import { UserGroup } from '@/models/entities/UserGroup.js'; -import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; -import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { UserIp } from '@/models/entities/UserIp.js'; import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { UserList } from '@/models/entities/UserList.js'; @@ -93,7 +89,6 @@ export { GalleryPost, Hashtag, Instance, - MessagingMessage, Meta, ModerationLog, MutedNote, @@ -118,9 +113,6 @@ export { SwSubscription, UsedUsername, User, - UserGroup, - UserGroupInvitation, - UserGroupJoining, UserIp, UserKeypair, UserList, @@ -163,7 +155,6 @@ export type GalleryLikesRepository = Repository<GalleryLike>; export type GalleryPostsRepository = Repository<GalleryPost>; export type HashtagsRepository = Repository<Hashtag>; export type InstancesRepository = Repository<Instance>; -export type MessagingMessagesRepository = Repository<MessagingMessage>; export type MetasRepository = Repository<Meta>; export type ModerationLogsRepository = Repository<ModerationLog>; export type MutedNotesRepository = Repository<MutedNote>; @@ -188,9 +179,6 @@ export type SigninsRepository = Repository<Signin>; export type SwSubscriptionsRepository = Repository<SwSubscription>; export type UsedUsernamesRepository = Repository<UsedUsername>; export type UsersRepository = Repository<User>; -export type UserGroupsRepository = Repository<UserGroup>; -export type UserGroupInvitationsRepository = Repository<UserGroupInvitation>; -export type UserGroupJoiningsRepository = Repository<UserGroupJoining>; export type UserIpsRepository = Repository<UserIp>; export type UserKeypairsRepository = Repository<UserKeypair>; export type UserListsRepository = Repository<UserList>; diff --git a/packages/backend/src/models/schema/antenna.ts b/packages/backend/src/models/schema/antenna.ts index 9cf522802cf7a0e59d257d43fe43ee2a086e8df0..f0994e48f7e53556dd37276d87acb0b639594b64 100644 --- a/packages/backend/src/models/schema/antenna.ts +++ b/packages/backend/src/models/schema/antenna.ts @@ -42,18 +42,13 @@ export const packedAntennaSchema = { src: { type: 'string', optional: false, nullable: false, - enum: ['home', 'all', 'users', 'list', 'group'], + enum: ['home', 'all', 'users', 'list'], }, userListId: { type: 'string', optional: false, nullable: true, format: 'id', }, - userGroupId: { - type: 'string', - optional: false, nullable: true, - format: 'id', - }, users: { type: 'array', optional: false, nullable: false, diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts index 143f25373ccb45eff8fa27ccd88abd5521252cb2..c00c3dac1d8e4a9cc286833e86ff9505299a09c0 100644 --- a/packages/backend/src/models/schema/emoji.ts +++ b/packages/backend/src/models/schema/emoji.ts @@ -1,11 +1,37 @@ -export const packedEmojiSchema = { +export const packedEmojiSimpleSchema = { + type: 'object', + properties: { + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + url: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +export const packedEmojiDetailedSchema = { type: 'object', properties: { id: { type: 'string', - optional: true, nullable: false, + optional: false, nullable: false, format: 'id', - example: 'xxxxxxxxxx', }, aliases: { type: 'array', @@ -26,12 +52,12 @@ export const packedEmojiSchema = { }, host: { type: 'string', - optional: true, nullable: true, + optional: false, nullable: true, description: 'The local host is represented with `null`.', }, url: { type: 'string', - optional: true, nullable: false, + optional: false, nullable: false, }, }, } as const; diff --git a/packages/backend/src/models/schema/flash.ts b/packages/backend/src/models/schema/flash.ts new file mode 100644 index 0000000000000000000000000000000000000000..8471a138ece4a72943a3a62b36c9a39e795406b7 --- /dev/null +++ b/packages/backend/src/models/schema/flash.ts @@ -0,0 +1,51 @@ +export const packedFlashSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + example: 'xxxxxxxxxx', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + updatedAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + title: { + type: 'string', + optional: false, nullable: false, + }, + summary: { + type: 'string', + optional: false, nullable: false, + }, + script: { + type: 'string', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + likedCount: { + type: 'number', + optional: false, nullable: true, + }, + isLiked: { + type: 'boolean', + optional: true, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/models/schema/messaging-message.ts b/packages/backend/src/models/schema/messaging-message.ts deleted file mode 100644 index b1ffa45955e6a79dc8c76bddf15fffd32ac2c3f4..0000000000000000000000000000000000000000 --- a/packages/backend/src/models/schema/messaging-message.ts +++ /dev/null @@ -1,73 +0,0 @@ -export const packedMessagingMessageSchema = { - type: 'object', - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - example: 'xxxxxxxxxx', - }, - createdAt: { - type: 'string', - optional: false, nullable: false, - format: 'date-time', - }, - userId: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - user: { - type: 'object', - ref: 'UserLite', - optional: true, nullable: false, - }, - text: { - type: 'string', - optional: false, nullable: true, - }, - fileId: { - type: 'string', - optional: true, nullable: true, - format: 'id', - }, - file: { - type: 'object', - optional: true, nullable: true, - ref: 'DriveFile', - }, - recipientId: { - type: 'string', - optional: false, nullable: true, - format: 'id', - }, - recipient: { - type: 'object', - optional: true, nullable: true, - ref: 'UserLite', - }, - groupId: { - type: 'string', - optional: false, nullable: true, - format: 'id', - }, - group: { - type: 'object', - optional: true, nullable: true, - ref: 'UserGroup', - }, - isRead: { - type: 'boolean', - optional: true, nullable: false, - }, - reads: { - type: 'array', - optional: true, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - }, - }, -} as const; diff --git a/packages/backend/src/models/schema/user-group.ts b/packages/backend/src/models/schema/user-group.ts deleted file mode 100644 index a73bf82bb8c1ed02dd9771f8f12708366399b899..0000000000000000000000000000000000000000 --- a/packages/backend/src/models/schema/user-group.ts +++ /dev/null @@ -1,34 +0,0 @@ -export const packedUserGroupSchema = { - type: 'object', - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - example: 'xxxxxxxxxx', - }, - createdAt: { - type: 'string', - optional: false, nullable: false, - format: 'date-time', - }, - name: { - type: 'string', - optional: false, nullable: false, - }, - ownerId: { - type: 'string', - nullable: false, optional: false, - format: 'id', - }, - userIds: { - type: 'array', - nullable: false, optional: true, - items: { - type: 'string', - nullable: false, optional: false, - format: 'id', - }, - }, - }, -} as const; diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts index 1fc93525399f2f2dd1f5f1469217ec26a2173b2b..c390018b46a95dd45ae43250afc3c7266b637041 100644 --- a/packages/backend/src/models/schema/user.ts +++ b/packages/backend/src/models/schema/user.ts @@ -311,10 +311,6 @@ export const packedMeDetailedOnlySchema = { type: 'boolean', nullable: false, optional: false, }, - hasUnreadMessagingMessage: { - type: 'boolean', - nullable: false, optional: false, - }, hasUnreadNotification: { type: 'boolean', nullable: false, optional: false, diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 33b924e7765d552734ed5ca09b7e582d866a1192..c2ee14b0f443158485604ccfb8c03945121ebf34 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -30,7 +30,6 @@ import { GalleryLike } from '@/models/entities/GalleryLike.js'; import { GalleryPost } from '@/models/entities/GalleryPost.js'; import { Hashtag } from '@/models/entities/Hashtag.js'; import { Instance } from '@/models/entities/Instance.js'; -import { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { Meta } from '@/models/entities/Meta.js'; import { ModerationLog } from '@/models/entities/ModerationLog.js'; import { MutedNote } from '@/models/entities/MutedNote.js'; @@ -55,9 +54,6 @@ import { Signin } from '@/models/entities/Signin.js'; import { SwSubscription } from '@/models/entities/SwSubscription.js'; import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { User } from '@/models/entities/User.js'; -import { UserGroup } from '@/models/entities/UserGroup.js'; -import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; -import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { UserIp } from '@/models/entities/UserIp.js'; import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { UserList } from '@/models/entities/UserList.js'; @@ -78,7 +74,6 @@ import { FlashLike } from '@/models/entities/FlashLike.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; import { bindThis } from '@/decorators.js'; -import { envOption } from './env.js'; export const dbLogger = new MisskeyLogger('db'); @@ -137,9 +132,6 @@ export const entities = [ UserPublickey, UserList, UserListJoining, - UserGroup, - UserGroupJoining, - UserGroupInvitation, UserNotePining, UserSecurityKey, UsedUsername, @@ -167,7 +159,6 @@ export const entities = [ SwSubscription, AbuseUserReport, RegistrationTicket, - MessagingMessage, Signin, ModerationLog, Clip, diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index df337ad8102ab31a26c17766006dce0a1ce09a11..910e2bea4511d1a7a9f8a8ce703ccd0ce2e2e6cf 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { DbJobData } from '@/queue/types.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index c95e1c1ba6003ddb430c8937576b686595e1ad87..865e47c3f8b898c027765ac4e3be4e46b207abc8 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { ObjectStorageJobData } from '@/queue/types.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; diff --git a/packages/backend/src/queue/QueueLoggerService.ts b/packages/backend/src/queue/QueueLoggerService.ts index 3a8a734f107b22163fe3ecfa8d68773445c0b94a..648af893c2576cacd74170713a80e492e76d9030 100644 --- a/packages/backend/src/queue/QueueLoggerService.ts +++ b/packages/backend/src/queue/QueueLoggerService.ts @@ -1,7 +1,6 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class QueueLoggerService { diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 2123815c4c468430e6086cf5d3335445ba63a98f..8457747cb06878d5b18ab393263530b42646a6bd 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { ModuleRef } from '@nestjs/core'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts index da4ae88557dc594808038eb304a8975ab56e05d3..02324c6cd4b43499cfff2162dfccf6837526a227 100644 --- a/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts +++ b/packages/backend/src/queue/processors/AggregateRetentionProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In, IsNull, MoreThan } from 'typeorm'; +import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 7a1e3e71be0b76ac2875136d43504d369892badf..f4cd560fc9289fb64d4a93b7cde6167af8df300e 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In, MoreThan } from 'typeorm'; +import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { MutingsRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 5254d3c7d8d3bc1f3d8a61c5abd6769fc27dbd6e..b458167042070c520dd2e4abf0f312d27845900a 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 4aa1dc15fdf3c34dd1f3738e4032c11a5f511025..406184cbde27031ab81295fcee8802ab7cdc4cd0 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In, LessThan, MoreThan } from 'typeorm'; +import { LessThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { AntennaNotesRepository, MutedNotesRepository, NotificationsRepository, UserIpsRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 10fcb5684f347e5a6a67cdbf681365c7fec0268d..2a053a12e0ff82c3b7c5dcee509733e07effdea2 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFilesRepository, InstancesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 21d2dc9efc9bf91a716ff7f3fd8d78b3dc11b720..037dfa1a538b0c5f61aba1bb1373a5e1458e04dd 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { PollVotesRepository, NotesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 5b3c1a415bf17a4df0d633490495e43b7ab4d106..7f2c2d08b5754c9ae89c5e35d2f6604211b73df1 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, BlockingsRepository, DriveFilesRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js'; +import type { UsersRepository, BlockingsRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index df024a8f3ccc650ef274ec54f447dc5c16913d04..b50f373ef820d7a14f810bbfd94d4e0477c7e812 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -1,8 +1,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import { format as dateFormat } from 'date-fns'; -import { ulid } from 'ulid'; import mime from 'mime-types'; import archiver from 'archiver'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts index 3820705e5ce091e21c77b11ceb92c282ffa05ede..c65f0a97a0e3d4517efd65d9c5227292cf6b9ace 100644 --- a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { NoteFavorite, NoteFavoritesRepository, NotesRepository, PollsRepository, User, UsersRepository } from '@/models/index.js'; diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 8431829e9168bc025eba1af67b6b63eef0877977..3f4f16a2ec8ed8d54c8e7c6d74a730f9320d0636 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js'; diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index a8daa5e5eeaf852121b17f47c5044d1a38d19e05..6400161b8c463883b7eaff6a86128db701eb8f0a 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { In, IsNull, MoreThan } from 'typeorm'; +import { In } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { UserListJoiningsRepository, UserListsRepository, UsersRepository } from '@/models/index.js'; diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 2eed420e962d06bc025fc1316fe5c30597821f6d..b8d9b3a52d8865bdf429b2e5a4212b70a68666e5 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UsersRepository, BlockingsRepository, DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 2d43615e25797cc7402ee0d3328d6f48a4e6bcde..4ecf8daf74938e588a9134f4f09f16d636ec3798 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan, DataSource } from 'typeorm'; +import { DataSource } from 'typeorm'; import unzipper from 'unzipper'; import { DI } from '@/di-symbols.js'; import type { EmojisRepository, DriveFilesRepository, UsersRepository } from '@/models/index.js'; diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index b61846d74761d708d7d07d32e9ded470fc582a45..037a6f2456d82b7d112bd3553093b5760295029c 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 21236da2ef560901fd3ac41f8e6475bd8f166666..83d382057bb12c7d32ed5fd6be044f9d99aede55 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index a9672250c88fab1f1fdcd2af563eec9e34389f0f..c423863410febc3f28c2ffd332f8c1fc05325a70 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UsersRepository, DriveFilesRepository, UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index f814368a7acad6f10ef3ad382a4e0c8c7af27591..33d6f4eafa79472c3de21e9234d90f1ea77e06e6 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -1,6 +1,5 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; -import { MoreThan } from 'typeorm'; import httpSignature from '@peertube/http-signature'; import { DI } from '@/di-symbols.js'; import type { InstancesRepository, DriveFilesRepository } from '@/models/index.js'; @@ -10,13 +9,11 @@ import { MetaService } from '@/core/MetaService.js'; import { ApRequestService } from '@/core/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; -import { Cache } from '@/misc/cache.js'; -import type { Instance } from '@/models/entities/Instance.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import FederationChart from '@/core/chart/charts/federation.js'; import { getApId } from '@/core/activitypub/type.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { RemoteUser } from '@/models/entities/User.js'; import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { StatusError } from '@/misc/status-error.js'; @@ -27,7 +24,7 @@ import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import type { DeliverJobData, InboxJobData } from '../types.js'; +import type { InboxJobData } from '../types.js'; // ユーザーã®inboxã«ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティãŒå±Šã„ãŸæ™‚ã®å‡¦ç† @Injectable() @@ -87,7 +84,7 @@ export class InboxProcessorService { // HTTP-Signature keyIdã‚’å…ƒã«DBã‹ã‚‰å–å¾— let authUser: { - user: CacheableRemoteUser; + user: RemoteUser; key: UserPublickey | null; } | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId); diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 74e7c632d50346bde89cc33648effdaf1fc3c3db..e5840f3da8c84bc5a9f962bce18c9aa59bd10703 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 751e02dc204205e957dd19d3360285e4607db589..7ff84c15a5c07640a5e2b25c69c53bf24705d4dd 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 57210b25d2122a31a75ab274a31d08ee98d1bbca..39b1b956586b67a3420bef8d994e95b57004f668 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { WebhooksRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 5480395eeb602736283c583b262dcaaf57a18104..da8d0114e53cf7cb5cd28938c017a27ef4a193a7 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -11,7 +11,7 @@ import * as url from '@/misc/prelude/url.js'; import type { Config } from '@/config.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { QueueService } from '@/core/QueueService.js'; -import type { ILocalUser, User } from '@/models/entities/User.js'; +import type { LocalUser, User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; import type { Following } from '@/models/entities/Following.js'; import { countIf } from '@/misc/prelude/array.js'; @@ -183,13 +183,13 @@ export class ActivityPubServerService { ); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } else { // index page const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`); reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } } @@ -271,13 +271,13 @@ export class ActivityPubServerService { ); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } else { // index page const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`); reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } } @@ -312,7 +312,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } @bindThis @@ -389,7 +389,7 @@ export class ActivityPubServerService { ); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } else { // index page const rendered = this.apRendererService.renderOrderedCollection(partOf, user.notesCount, @@ -398,7 +398,7 @@ export class ActivityPubServerService { ); reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(rendered)); + return (this.apRendererService.addContext(rendered)); } } @@ -411,7 +411,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(await this.apRendererService.renderPerson(user as ILocalUser))); + return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as LocalUser))); } @bindThis @@ -481,7 +481,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(await this.apRendererService.renderNote(note, false))); + return this.apRendererService.addContext(await this.apRendererService.renderNote(note, false)); }); // note activity @@ -502,7 +502,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(await this.packActivity(note))); + return (this.apRendererService.addContext(await this.packActivity(note))); }); // outbox @@ -545,7 +545,7 @@ export class ActivityPubServerService { if (this.userEntityService.isLocalUser(user)) { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(this.apRendererService.renderKey(user, keypair))); + return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair))); } else { reply.code(400); return; @@ -589,7 +589,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(await this.apRendererService.renderEmoji(emoji))); + return (this.apRendererService.addContext(await this.apRendererService.renderEmoji(emoji))); }); // like @@ -610,7 +610,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(await this.apRendererService.renderLike(reaction, note))); + return (this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, note))); }); // follow @@ -636,7 +636,7 @@ export class ActivityPubServerService { reply.header('Cache-Control', 'public, max-age=180'); this.setResponseType(request, reply); - return (this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee))); + return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee))); }); done(); diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index f4bc568fdc5efbcc309b8f63515ec245a0f9bb41..c12ae9b82401a9542d3691625c662adfbe717f65 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -395,7 +395,7 @@ export class FileServerService { state: 'remote', mime, ext, path, cleanup, - } + }; } catch (e) { cleanup(); throw e; @@ -429,7 +429,7 @@ export class FileServerService { url: file.uri, fileRole: isThumbnail ? 'thumbnail' : isWebpublic ? 'webpublic' : 'original', file, - } + }; } const path = this.internalStorageService.resolvePath(key); @@ -452,6 +452,6 @@ export class FileServerService { mime: file.type, ext: null, path, - } + }; } } diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index a43630c0411f0c5f1ca50f51e4008f2cf8a3b413..00a0d93093fc1549f13055d9b865055740262f8b 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { NotesRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index b605f3c8abb32d1e438b329c677097b509ca9849..a5a5f9e7f93a57fe5dac30b0ecbb0340e5597f43 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -30,8 +30,6 @@ import { HashtagChannelService } from './api/stream/channels/hashtag.js'; import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js'; import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js'; import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js'; -import { MessagingIndexChannelService } from './api/stream/channels/messaging-index.js'; -import { MessagingChannelService } from './api/stream/channels/messaging.js'; import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; import { UserListChannelService } from './api/stream/channels/user-list.js'; @@ -71,8 +69,6 @@ import { UserListChannelService } from './api/stream/channels/user-list.js'; HomeTimelineChannelService, HybridTimelineChannelService, LocalTimelineChannelService, - MessagingIndexChannelService, - MessagingChannelService, QueueStatsChannelService, ServerStatsChannelService, UserListChannelService, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index f76871c60fde386aaeb100fc9f9cdec4dad03ca4..8200b24fd47f4cbe9a41e5c8a1405e7d919a128a 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -8,7 +8,6 @@ import type { Config } from '@/config.js'; import type { EmojisRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; -import { envOption } from '@/env.js'; import * as Acct from '@/misc/acct.js'; import { genIdenticon } from '@/misc/gen-identicon.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 9bfd216ccb28418fde6ff6ad2df9d3be09f23c69..e722563036668fcedb86ea0c817071df8dcf00c8 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import vary from 'vary'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 2f3e7a44a9a2890a9c727cf3ddb506af86120220..347fa59d3665c586a1a718a3c85da309f080efc9 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -1,11 +1,10 @@ -import { performance } from 'perf_hooks'; import { pipeline } from 'node:stream'; import * as fs from 'node:fs'; import { promisify } from 'node:util'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; -import type { CacheableLocalUser, ILocalUser, User } from '@/models/entities/User.js'; +import type { LocalUser, User } from '@/models/entities/User.js'; import type { AccessToken } from '@/models/entities/AccessToken.js'; import type Logger from '@/logger.js'; import type { UserIpsRepository } from '@/models/index.js'; @@ -109,9 +108,9 @@ export class ApiCallService implements OnApplicationShutdown { const [path] = await createTemp(); await pump(multipartData.file, fs.createWriteStream(path)); - const fields = {} as Record<string, string | undefined>; + const fields = {} as Record<string, unknown>; for (const [k, v] of Object.entries(multipartData.fields)) { - fields[k] = v.value; + fields[k] = typeof v === 'object' && 'value' in v ? v.value : undefined; } const token = fields['i']; @@ -168,7 +167,7 @@ export class ApiCallService implements OnApplicationShutdown { } @bindThis - private async logIp(request: FastifyRequest, user: ILocalUser) { + private async logIp(request: FastifyRequest, user: LocalUser) { const meta = await this.metaService.fetch(); if (!meta.enableIpLogging) return; const ip = request.ip; @@ -194,7 +193,7 @@ export class ApiCallService implements OnApplicationShutdown { @bindThis private async call( ep: IEndpoint & { exec: any }, - user: CacheableLocalUser | null | undefined, + user: LocalUser | null | undefined, token: AccessToken | null | undefined, data: any, file: { @@ -220,8 +219,8 @@ export class ApiCallService implements OnApplicationShutdown { const limit = Object.assign({}, ep.meta.limit); - if (!limit.key) { - limit.key = ep.name; + if (limit.key == null) { + (limit as any).key = ep.name; } // TODO: 毎リクエスト計算ã™ã‚‹ã®ã‚‚ã‚ã‚Œã ã—ã‚ャッシュã—ãŸã„ diff --git a/packages/backend/src/server/api/ApiLoggerService.ts b/packages/backend/src/server/api/ApiLoggerService.ts index cabd65fd3eb27e43c9785b803e2fa65e87807c76..7f534b1efdbe4a2ea292abeee14626cf96ade4c6 100644 --- a/packages/backend/src/server/api/ApiLoggerService.ts +++ b/packages/backend/src/server/api/ApiLoggerService.ts @@ -1,7 +1,6 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ApiLoggerService { diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index e406949cd400d2782172465d8092bf87cbe4fed8..2b99da01b677a0284eabe01e2260c9bbd570b9df 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -2,13 +2,13 @@ import { Inject, Injectable } from '@nestjs/common'; import cors from '@fastify/cors'; import multipart from '@fastify/multipart'; import fastifyCookie from '@fastify/cookie'; -import { ModuleRef, repl } from '@nestjs/core'; +import { ModuleRef } from '@nestjs/core'; import type { Config } from '@/config.js'; import type { UsersRepository, InstancesRepository, AccessTokensRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import endpoints, { IEndpoint } from './endpoints.js'; +import endpoints from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; import { SigninApiService } from './SigninApiService.js'; diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index 8b39f6c92428c74ce468909c7525111e0e8c9add..87438c348da90c653e52abb034d1b9c7b30a9302 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { AccessTokensRepository, AppsRepository, UsersRepository } from '@/models/index.js'; -import type { CacheableLocalUser, ILocalUser } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import type { AccessToken } from '@/models/entities/AccessToken.js'; import { Cache } from '@/misc/cache.js'; import type { App } from '@/models/entities/App.js'; @@ -36,14 +36,14 @@ export class AuthenticateService { } @bindThis - public async authenticate(token: string | null | undefined): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> { + public async authenticate(token: string | null | undefined): Promise<[LocalUser | null | undefined, AccessToken | null | undefined]> { if (token == null) { return [null, null]; } if (isNativeToken(token)) { const user = await this.userCacheService.localUserByNativeTokenCache.fetch(token, - () => this.usersRepository.findOneBy({ token }) as Promise<ILocalUser | null>); + () => this.usersRepository.findOneBy({ token }) as Promise<LocalUser | null>); if (user == null) { throw new AuthenticationError('user not found'); @@ -70,7 +70,7 @@ export class AuthenticateService { const user = await this.userCacheService.localUserByIdCache.fetch(accessToken.userId, () => this.usersRepository.findOneBy({ id: accessToken.userId, - }) as Promise<ILocalUser>); + }) as Promise<LocalUser>); if (accessToken.appId) { const app = await this.appCache.fetch(accessToken.appId, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 4a55c6cbe34fbcc01938a57abb5a66fe64412421..d3e2219bd5546d6148414b0a54cc2e88751621a0 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -66,6 +66,7 @@ import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; +import * as ep___admin_roles_users from './endpoints/admin/roles/users.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -170,6 +171,7 @@ import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_updateKey from './endpoints/i/2fa/update-key.js'; import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; import * as ep___i_apps from './endpoints/i/apps.js'; @@ -195,7 +197,6 @@ import * as ep___i_notifications from './endpoints/i/notifications.js'; import * as ep___i_pageLikes from './endpoints/i/page-likes.js'; import * as ep___i_pages from './endpoints/i/pages.js'; import * as ep___i_pin from './endpoints/i/pin.js'; -import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js'; import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js'; import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js'; import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js'; @@ -212,17 +213,11 @@ import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; import * as ep___i_unpin from './endpoints/i/unpin.js'; import * as ep___i_updateEmail from './endpoints/i/update-email.js'; import * as ep___i_update from './endpoints/i/update.js'; -import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js'; import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js'; import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; -import * as ep___messaging_history from './endpoints/messaging/history.js'; -import * as ep___messaging_messages from './endpoints/messaging/messages.js'; -import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js'; -import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; -import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; @@ -283,6 +278,9 @@ import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___roles_list from './endpoints/roles/list.js'; +import * as ep___roles_show from './endpoints/roles/show.js'; +import * as ep___roles_users from './endpoints/roles/users.js'; import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; import * as ep___resetDb from './endpoints/reset-db.js'; import * as ep___resetPassword from './endpoints/reset-password.js'; @@ -300,18 +298,6 @@ import * as ep___users_followers from './endpoints/users/followers.js'; import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; -import * as ep___users_groups_create from './endpoints/users/groups/create.js'; -import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; -import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; -import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js'; -import * as ep___users_groups_invite from './endpoints/users/groups/invite.js'; -import * as ep___users_groups_joined from './endpoints/users/groups/joined.js'; -import * as ep___users_groups_leave from './endpoints/users/groups/leave.js'; -import * as ep___users_groups_owned from './endpoints/users/groups/owned.js'; -import * as ep___users_groups_pull from './endpoints/users/groups/pull.js'; -import * as ep___users_groups_show from './endpoints/users/groups/show.js'; -import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js'; -import * as ep___users_groups_update from './endpoints/users/groups/update.js'; import * as ep___users_lists_create from './endpoints/users/lists/create.js'; import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; import * as ep___users_lists_list from './endpoints/users/lists/list.js'; @@ -401,6 +387,7 @@ const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useCla const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default }; const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default }; +const $admin_roles_users: Provider = { provide: 'ep:admin/roles/users', useClass: ep___admin_roles_users.default }; const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }; @@ -505,6 +492,7 @@ const $i_2fa_keyDone: Provider = { provide: 'ep:i/2fa/key-done', useClass: ep___ const $i_2fa_passwordLess: Provider = { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }; const $i_2fa_registerKey: Provider = { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }; const $i_2fa_register: Provider = { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }; +const $i_2fa_updateKey: Provider = { provide: 'ep:i/2fa/update-key', useClass: ep___i_2fa_updateKey.default }; const $i_2fa_removeKey: Provider = { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }; const $i_2fa_unregister: Provider = { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }; const $i_apps: Provider = { provide: 'ep:i/apps', useClass: ep___i_apps.default }; @@ -530,7 +518,6 @@ const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }; const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default }; const $i_pin: Provider = { provide: 'ep:i/pin', useClass: ep___i_pin.default }; -const $i_readAllMessagingMessages: Provider = { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }; const $i_readAllUnreadNotes: Provider = { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }; const $i_readAnnouncement: Provider = { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }; const $i_regenerateToken: Provider = { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }; @@ -547,17 +534,11 @@ const $i_signinHistory: Provider = { provide: 'ep:i/signin-history', useClass: e const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }; const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }; const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default }; -const $i_userGroupInvites: Provider = { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }; const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }; const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }; const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }; const $i_webhooks_update: Provider = { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }; const $i_webhooks_delete: Provider = { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }; -const $messaging_history: Provider = { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }; -const $messaging_messages: Provider = { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }; -const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }; -const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }; -const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; @@ -618,6 +599,9 @@ const $flash_myLikes: Provider = { provide: 'ep:flash/my-likes', useClass: ep___ const $ping: Provider = { provide: 'ep:ping', useClass: ep___ping.default }; const $pinnedUsers: Provider = { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }; const $promo_read: Provider = { provide: 'ep:promo/read', useClass: ep___promo_read.default }; +const $roles_list: Provider = { provide: 'ep:roles/list', useClass: ep___roles_list.default }; +const $roles_show: Provider = { provide: 'ep:roles/show', useClass: ep___roles_show.default }; +const $roles_users: Provider = { provide: 'ep:roles/users', useClass: ep___roles_users.default }; const $requestResetPassword: Provider = { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }; const $resetDb: Provider = { provide: 'ep:reset-db', useClass: ep___resetDb.default }; const $resetPassword: Provider = { provide: 'ep:reset-password', useClass: ep___resetPassword.default }; @@ -635,18 +619,6 @@ const $users_followers: Provider = { provide: 'ep:users/followers', useClass: ep const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default }; const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }; const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }; -const $users_groups_create: Provider = { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }; -const $users_groups_delete: Provider = { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }; -const $users_groups_invitations_accept: Provider = { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }; -const $users_groups_invitations_reject: Provider = { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }; -const $users_groups_invite: Provider = { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }; -const $users_groups_joined: Provider = { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }; -const $users_groups_leave: Provider = { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }; -const $users_groups_owned: Provider = { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }; -const $users_groups_pull: Provider = { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }; -const $users_groups_show: Provider = { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }; -const $users_groups_transfer: Provider = { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }; -const $users_groups_update: Provider = { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }; const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }; const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }; const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }; @@ -740,6 +712,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_assign, $admin_roles_unassign, $admin_roles_updateDefaultPolicies, + $admin_roles_users, $announcements, $antennas_create, $antennas_delete, @@ -844,6 +817,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_2fa_passwordLess, $i_2fa_registerKey, $i_2fa_register, + $i_2fa_updateKey, $i_2fa_removeKey, $i_2fa_unregister, $i_apps, @@ -869,7 +843,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_pageLikes, $i_pages, $i_pin, - $i_readAllMessagingMessages, $i_readAllUnreadNotes, $i_readAnnouncement, $i_regenerateToken, @@ -886,17 +859,11 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_unpin, $i_updateEmail, $i_update, - $i_userGroupInvites, $i_webhooks_create, $i_webhooks_list, $i_webhooks_show, $i_webhooks_update, $i_webhooks_delete, - $messaging_history, - $messaging_messages, - $messaging_messages_create, - $messaging_messages_delete, - $messaging_messages_read, $meta, $emojis, $miauth_genToken, @@ -957,6 +924,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $ping, $pinnedUsers, $promo_read, + $roles_list, + $roles_show, + $roles_users, $requestResetPassword, $resetDb, $resetPassword, @@ -974,18 +944,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_following, $users_gallery_posts, $users_getFrequentlyRepliedUsers, - $users_groups_create, - $users_groups_delete, - $users_groups_invitations_accept, - $users_groups_invitations_reject, - $users_groups_invite, - $users_groups_joined, - $users_groups_leave, - $users_groups_owned, - $users_groups_pull, - $users_groups_show, - $users_groups_transfer, - $users_groups_update, $users_lists_create, $users_lists_delete, $users_lists_list, @@ -1073,6 +1031,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_assign, $admin_roles_unassign, $admin_roles_updateDefaultPolicies, + $admin_roles_users, $announcements, $antennas_create, $antennas_delete, @@ -1177,6 +1136,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_2fa_passwordLess, $i_2fa_registerKey, $i_2fa_register, + $i_2fa_updateKey, $i_2fa_removeKey, $i_2fa_unregister, $i_apps, @@ -1202,7 +1162,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_pageLikes, $i_pages, $i_pin, - $i_readAllMessagingMessages, $i_readAllUnreadNotes, $i_readAnnouncement, $i_regenerateToken, @@ -1219,17 +1178,11 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_unpin, $i_updateEmail, $i_update, - $i_userGroupInvites, $i_webhooks_create, $i_webhooks_list, $i_webhooks_show, $i_webhooks_update, $i_webhooks_delete, - $messaging_history, - $messaging_messages, - $messaging_messages_create, - $messaging_messages_delete, - $messaging_messages_read, $meta, $emojis, $miauth_genToken, @@ -1290,6 +1243,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $ping, $pinnedUsers, $promo_read, + $roles_list, + $roles_show, + $roles_users, $requestResetPassword, $resetDb, $resetPassword, @@ -1305,18 +1261,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_following, $users_gallery_posts, $users_getFrequentlyRepliedUsers, - $users_groups_create, - $users_groups_delete, - $users_groups_invitations_accept, - $users_groups_invitations_reject, - $users_groups_invite, - $users_groups_joined, - $users_groups_leave, - $users_groups_owned, - $users_groups_pull, - $users_groups_show, - $users_groups_transfer, - $users_groups_update, $users_lists_create, $users_lists_delete, $users_lists_list, diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index c7f9916f974eeec0761cd570241703b7bba12ba5..c94884a78c216f517d3d2f501fff2386b95dbeb4 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { NotesRepository, UsersRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { User } from '@/models/entities/User.js'; +import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -45,7 +45,7 @@ export class GetterService { throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); } - return user; + return user as LocalUser | RemoteUser; } /** diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index d490097deae3fd791bc280824f9bfbdf7d85a0c0..bd3d8a28dab34157897fb62797d0c8e913a938ff 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -1,13 +1,13 @@ import { randomBytes } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; -import * as speakeasy from 'speakeasy'; +import * as OTPAuth from 'otpauth'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; -import type { ILocalUser } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; import { bindThis } from '@/decorators.js'; @@ -105,7 +105,7 @@ export class SigninApiService { const user = await this.usersRepository.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull(), - }) as ILocalUser; + }) as LocalUser; if (user == null) { return error(404, { @@ -155,19 +155,19 @@ export class SigninApiService { }); } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorSecret, - encoding: 'base32', - token: token, - window: 2, + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!), + digits: 6, + token, + window: 1, }); - if (verified) { - return this.signinService.signin(request, reply, user); - } else { + if (delta === null) { return await fail(403, { id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f', }); + } else { + return this.signinService.signin(request, reply, user); } } else if (body.credentialId && body.clientDataJSON && body.authenticatorData && body.signature) { if (!same && !profile.usePasswordLessLogin) { diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index c78d9f85cd03b8661ed74c1322640777ce078604..aaf1d10b42a39739f5dede7437369108938613b4 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { SigninsRepository, UsersRepository } from '@/models/index.js'; +import type { SigninsRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { IdService } from '@/core/IdService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { SigninEntityService } from '@/core/entities/SigninEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -25,7 +25,7 @@ export class SigninService { } @bindThis - public signin(request: FastifyRequest, reply: FastifyReply, user: ILocalUser) { + public signin(request: FastifyRequest, reply: FastifyReply, user: LocalUser) { setImmediate(async () => { // Append signin history const record = await this.signinsRepository.insert({ diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index ffd7e203eae4061ac5bb3288ebe30acf3cc5c48a..41e8365d08ec68dd1183cc6047682e93c08b67b2 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -10,7 +10,7 @@ import { IdService } from '@/core/IdService.js'; import { SignupService } from '@/core/SignupService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { EmailService } from '@/core/EmailService.js'; -import { ILocalUser } from '@/models/entities/User.js'; +import { LocalUser } from '@/models/entities/User.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { bindThis } from '@/decorators.js'; import { SigninService } from './SigninService.js'; @@ -194,7 +194,7 @@ export class SignupApiService { emailVerifyCode: null, }); - return this.signinService.signin(request, reply, account as ILocalUser); + return this.signinService.signin(request, reply, account as LocalUser); } catch (err) { throw new FastifyReplyError(400, typeof err === 'string' ? err : (err as Error).toString()); } diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index b27329b9a96b14692f94de57b46c24dff23d73e0..ed283eb834ad43011b058b987552a1a4ab1ad690 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -1,7 +1,7 @@ import * as fs from 'node:fs'; import Ajv from 'ajv'; import type { Schema, SchemaType } from '@/misc/schema.js'; -import type { CacheableLocalUser } from '@/models/entities/User.js'; +import type { LocalUser } from '@/models/entities/User.js'; import type { AccessToken } from '@/models/entities/AccessToken.js'; import { ApiError } from './error.js'; import type { IEndpointMeta } from './endpoints.js'; @@ -20,17 +20,17 @@ type File = { }; // TODO: paramsã®åž‹ã‚’T['params']ã®ã‚¹ã‚ーマ定義ã‹ã‚‰æŽ¨è«–ã™ã‚‹ -type executor<T extends IEndpointMeta, Ps extends Schema> = - (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => +type Executor<T extends IEndpointMeta, Ps extends Schema> = + (params: SchemaType<Ps>, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, cleanup?: () => any, ip?: string | null, headers?: Record<string, string> | null) => Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; export abstract class Endpoint<T extends IEndpointMeta, Ps extends Schema> { - public exec: (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; + public exec: (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => Promise<any>; - constructor(meta: T, paramDef: Ps, cb: executor<T, Ps>) { + constructor(meta: T, paramDef: Ps, cb: Executor<T, Ps>) { const validate = ajv.compile(paramDef); - this.exec = (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { + this.exec = (params: any, user: T['requireCredential'] extends true ? LocalUser : LocalUser | null, token: AccessToken | null, file?: File, ip?: string | null, headers?: Record<string, string> | null) => { let cleanup: undefined | (() => void) = undefined; if (meta.requireFile) { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 55e1900d5178ba629fba15fb91cf4a856748111c..4d5ed9fb62905e09f860c48ae95cc2492bb87de2 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -1,4 +1,5 @@ import type { Schema } from '@/misc/schema.js'; +import { RolePolicies } from '@/core/RoleService.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; @@ -65,6 +66,7 @@ import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js'; +import * as ep___admin_roles_users from './endpoints/admin/roles/users.js'; import * as ep___announcements from './endpoints/announcements.js'; import * as ep___antennas_create from './endpoints/antennas/create.js'; import * as ep___antennas_delete from './endpoints/antennas/delete.js'; @@ -169,6 +171,7 @@ import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_updateKey from './endpoints/i/2fa/update-key.js'; import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; import * as ep___i_apps from './endpoints/i/apps.js'; @@ -194,7 +197,6 @@ import * as ep___i_notifications from './endpoints/i/notifications.js'; import * as ep___i_pageLikes from './endpoints/i/page-likes.js'; import * as ep___i_pages from './endpoints/i/pages.js'; import * as ep___i_pin from './endpoints/i/pin.js'; -import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js'; import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js'; import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js'; import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js'; @@ -211,17 +213,11 @@ import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; import * as ep___i_unpin from './endpoints/i/unpin.js'; import * as ep___i_updateEmail from './endpoints/i/update-email.js'; import * as ep___i_update from './endpoints/i/update.js'; -import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js'; import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js'; import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; -import * as ep___messaging_history from './endpoints/messaging/history.js'; -import * as ep___messaging_messages from './endpoints/messaging/messages.js'; -import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js'; -import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; -import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; @@ -282,6 +278,9 @@ import * as ep___flash_myLikes from './endpoints/flash/my-likes.js'; import * as ep___ping from './endpoints/ping.js'; import * as ep___pinnedUsers from './endpoints/pinned-users.js'; import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___roles_list from './endpoints/roles/list.js'; +import * as ep___roles_show from './endpoints/roles/show.js'; +import * as ep___roles_users from './endpoints/roles/users.js'; import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; import * as ep___resetDb from './endpoints/reset-db.js'; import * as ep___resetPassword from './endpoints/reset-password.js'; @@ -299,18 +298,6 @@ import * as ep___users_followers from './endpoints/users/followers.js'; import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; -import * as ep___users_groups_create from './endpoints/users/groups/create.js'; -import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; -import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; -import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js'; -import * as ep___users_groups_invite from './endpoints/users/groups/invite.js'; -import * as ep___users_groups_joined from './endpoints/users/groups/joined.js'; -import * as ep___users_groups_leave from './endpoints/users/groups/leave.js'; -import * as ep___users_groups_owned from './endpoints/users/groups/owned.js'; -import * as ep___users_groups_pull from './endpoints/users/groups/pull.js'; -import * as ep___users_groups_show from './endpoints/users/groups/show.js'; -import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js'; -import * as ep___users_groups_update from './endpoints/users/groups/update.js'; import * as ep___users_lists_create from './endpoints/users/lists/create.js'; import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; import * as ep___users_lists_list from './endpoints/users/lists/list.js'; @@ -398,6 +385,7 @@ const eps = [ ['admin/roles/assign', ep___admin_roles_assign], ['admin/roles/unassign', ep___admin_roles_unassign], ['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies], + ['admin/roles/users', ep___admin_roles_users], ['announcements', ep___announcements], ['antennas/create', ep___antennas_create], ['antennas/delete', ep___antennas_delete], @@ -502,6 +490,7 @@ const eps = [ ['i/2fa/password-less', ep___i_2fa_passwordLess], ['i/2fa/register-key', ep___i_2fa_registerKey], ['i/2fa/register', ep___i_2fa_register], + ['i/2fa/update-key', ep___i_2fa_updateKey], ['i/2fa/remove-key', ep___i_2fa_removeKey], ['i/2fa/unregister', ep___i_2fa_unregister], ['i/apps', ep___i_apps], @@ -527,7 +516,6 @@ const eps = [ ['i/page-likes', ep___i_pageLikes], ['i/pages', ep___i_pages], ['i/pin', ep___i_pin], - ['i/read-all-messaging-messages', ep___i_readAllMessagingMessages], ['i/read-all-unread-notes', ep___i_readAllUnreadNotes], ['i/read-announcement', ep___i_readAnnouncement], ['i/regenerate-token', ep___i_regenerateToken], @@ -544,17 +532,11 @@ const eps = [ ['i/unpin', ep___i_unpin], ['i/update-email', ep___i_updateEmail], ['i/update', ep___i_update], - ['i/user-group-invites', ep___i_userGroupInvites], ['i/webhooks/create', ep___i_webhooks_create], ['i/webhooks/list', ep___i_webhooks_list], ['i/webhooks/show', ep___i_webhooks_show], ['i/webhooks/update', ep___i_webhooks_update], ['i/webhooks/delete', ep___i_webhooks_delete], - ['messaging/history', ep___messaging_history], - ['messaging/messages', ep___messaging_messages], - ['messaging/messages/create', ep___messaging_messages_create], - ['messaging/messages/delete', ep___messaging_messages_delete], - ['messaging/messages/read', ep___messaging_messages_read], ['meta', ep___meta], ['emojis', ep___emojis], ['miauth/gen-token', ep___miauth_genToken], @@ -615,6 +597,9 @@ const eps = [ ['ping', ep___ping], ['pinned-users', ep___pinnedUsers], ['promo/read', ep___promo_read], + ['roles/list', ep___roles_list], + ['roles/show', ep___roles_show], + ['roles/users', ep___roles_users], ['request-reset-password', ep___requestResetPassword], ['reset-db', ep___resetDb], ['reset-password', ep___resetPassword], @@ -632,18 +617,6 @@ const eps = [ ['users/following', ep___users_following], ['users/gallery/posts', ep___users_gallery_posts], ['users/get-frequently-replied-users', ep___users_getFrequentlyRepliedUsers], - ['users/groups/create', ep___users_groups_create], - ['users/groups/delete', ep___users_groups_delete], - ['users/groups/invitations/accept', ep___users_groups_invitations_accept], - ['users/groups/invitations/reject', ep___users_groups_invitations_reject], - ['users/groups/invite', ep___users_groups_invite], - ['users/groups/joined', ep___users_groups_joined], - ['users/groups/leave', ep___users_groups_leave], - ['users/groups/owned', ep___users_groups_owned], - ['users/groups/pull', ep___users_groups_pull], - ['users/groups/show', ep___users_groups_show], - ['users/groups/transfer', ep___users_groups_transfer], - ['users/groups/update', ep___users_groups_update], ['users/lists/create', ep___users_lists_create], ['users/lists/delete', ep___users_lists_delete], ['users/lists/list', ep___users_lists_list], @@ -697,7 +670,7 @@ export interface IEndpointMeta { */ readonly requireAdmin?: boolean; - readonly requireRolePolicy?: string; + readonly requireRolePolicy?: keyof RolePolicies; /** * エンドãƒã‚¤ãƒ³ãƒˆã®ãƒªãƒŸãƒ†ãƒ¼ã‚·ãƒ§ãƒ³ã«é–¢ã™ã‚‹ã‚„㤠diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 8fcbde591b185a2202415a07dab4c49c90abd9fd..917242db3fd8935afcaa7b24ab07743f305c3ad6 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -20,9 +20,10 @@ export const paramDef = { priority: { type: 'string' }, ratio: { type: 'integer' }, expiresAt: { type: 'integer' }, + startsAt: { type: 'integer' }, imageUrl: { type: 'string', minLength: 1 }, }, - required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'imageUrl'], + required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'imageUrl'], } as const; // eslint-disable-next-line import/no-default-export @@ -39,6 +40,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { id: this.idService.genId(), createdAt: new Date(), expiresAt: new Date(ps.expiresAt), + startsAt: new Date(ps.startsAt), url: ps.url, imageUrl: ps.imageUrl, priority: ps.priority, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 29e245ab95f50f164a71e0ac7e76bc5e8a10e032..0b6d006052575d8d858ff4bddaf4eb62f7d4ae81 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -31,9 +31,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId) - .andWhere('ad.expiresAt > :now', { now: new Date() }); - + const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId); const ads = await query.take(ps.limit).getMany(); return ads; diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 08e3c96ca960d0c9345d9cbf4642abbc331c28bb..dbab7e9d4f14f01d483d168e319d050a12e6370e 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -30,8 +30,9 @@ export const paramDef = { priority: { type: 'string' }, ratio: { type: 'integer' }, expiresAt: { type: 'integer' }, + startsAt: { type: 'integer' }, }, - required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt'], + required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt'], } as const; // eslint-disable-next-line import/no-default-export @@ -54,6 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { memo: ps.memo, imageUrl: ps.imageUrl, expiresAt: new Date(ps.expiresAt), + startsAt: new Date(ps.startsAt), }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index 2cc4e70e551488ab7d7094dbfbab75b230228621..a8964af44946d27e11a566cca0b3ec6559728f3d 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 6376cb153c6ae2c138899500b316185b843d4d16..85b566aabe575a2381858205c8c1b48d557ac184 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; @@ -161,6 +161,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { @@ -178,7 +181,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new ApiError(meta.errors.noSuchFile); } - const isModerator = await this.roleService.isModerator(me); + const owner = file.userId ? await this.usersRepository.findOneByOrFail({ + id: file.userId, + }) : null; + + const iAmModerator = await this.roleService.isModerator(me); + const ownerIsModerator = owner ? await this.roleService.isModerator(owner) : false; return { id: file.id, @@ -207,8 +215,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { name: file.name, md5: file.md5, createdAt: file.createdAt.toISOString(), - requestIp: isModerator ? file.requestIp : null, - requestHeaders: isModerator ? file.requestHeaders : null, + requestIp: iAmModerator ? file.requestIp : null, + requestHeaders: iAmModerator && !ownerIsModerator ? file.requestHeaders : null, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index c683cd24c122613ed430eb7988bb77ab3f6ae5e6..0cc60e9191434f76581feea70a75f8869f975a0a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -56,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiUpdated', { - emojis: await this.emojiEntityService.packMany(ps.ids), + emojis: await this.emojiEntityService.packDetailedMany(ps.ids), }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 1bb05c15c24458b4eb53e02058cc2d200591a091..8889f30269021242c7529d0e8e71e43026bcaf1a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; -import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index b4fc7fd6f53b185d085161aa9c5ee1b9023d9e0c..8885a40fd93cb720d1e4ad980f3c8e552a9c9c62 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiAdded', { - emoji: await this.emojiEntityService.pack(copied.id), + emoji: await this.emojiEntityService.packDetailed(copied.id), }); return { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 0c337237d369191d2c17a4a756c580524376468e..f298baaedf69e6ab95f73d7a4cc9a0402000736a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } this.globalEventService.publishBroadcastStream('emojiDeleted', { - emojis: await this.emojiEntityService.packMany(emojis), + emojis: await this.emojiEntityService.packDetailedMany(emojis), }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index c1a60a27737c22847ca8514c51b4c0fb31bf5590..a5fbe3f4ea6037b4dbff0526b11583a48eb803ec 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -4,9 +4,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { ApiError } from '../../../error.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -57,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiDeleted', { - emojis: [ await this.emojiEntityService.pack(emoji) ], + emojis: [await this.emojiEntityService.packDetailed(emoji)], }); this.moderationLogService.insertModerationLog(me, 'deleteEmoji', { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index b4a07324bbb2e1eef2adec84b6a5ffc2d579bb95..e26f0506ce8c9c72bf20fa7fd9fbf63ca1dd8200 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 8e0ea2e1175476c4282356ae20ce36f112b7d81e..df3c28deff0d924210c4edaaadc62a150bf05850 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { .take(ps.limit) .getMany(); - return this.emojiEntityService.packMany(emojis, { omitHost: false, omitId: false, withUrl: false }); + return this.emojiEntityService.packDetailedMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 1b1931f8e63a5d8a80bdd86c122a277428fc775a..814668294f58036a045c3248c88b1f2ad6b8949c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { emojis = await q.take(ps.limit).getMany(); } - return this.emojiEntityService.packMany(emojis, { omitHost: false, omitId: false, withUrl: false }); + return this.emojiEntityService.packDetailedMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 065965f64a3a57911ce748f6e0eeb90164da3eb0..66547024f7467b0a591e7d95b8266fb85409431e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -56,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiUpdated', { - emojis: await this.emojiEntityService.packMany(ps.ids), + emojis: await this.emojiEntityService.packDetailedMany(ps.ids), }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 51c0f329ac750b891768ffe490729177a7dbc795..c8992eeb04add367bfac43adb1bc76129da02cb5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiUpdated', { - emojis: await this.emojiEntityService.packMany(ps.ids), + emojis: await this.emojiEntityService.packDetailedMany(ps.ids), }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index 3329cab7b9d583152da1c0191cc48daceb1fb584..8a538c100395bdf98af4600ea242a22a9fa29d36 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiUpdated', { - emojis: await this.emojiEntityService.packMany(ps.ids), + emojis: await this.emojiEntityService.packDetailedMany(ps.ids), }); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 22bedc71005a0555c4c55af7c1274cfee88e94f3..809bf77d6b4d10034d00a13545029b90a9d82422 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -3,9 +3,9 @@ import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -68,15 +68,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.db.queryResultCache!.remove(['meta_emojis']); - const updated = await this.emojiEntityService.pack(emoji.id); + const updated = await this.emojiEntityService.packDetailed(emoji.id); if (emoji.name === ps.name) { this.globalEventService.publishBroadcastStream('emojiUpdated', { - emojis: [ updated ], + emojis: [updated], }); } else { this.globalEventService.publishBroadcastStream('emojiDeleted', { - emojis: [ await this.emojiEntityService.pack(emoji) ], + emojis: [await this.emojiEntityService.packDetailed(emoji)], }); this.globalEventService.publishBroadcastStream('emojiAdded', { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 2b19104ea7b2b83ec27298a90f836df3b9eae875..9eef1b29c57f9d653fc153247fed11206d6e7791 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -54,86 +54,22 @@ export const meta = { }, mascotImageUrl: { type: 'string', - optional: false, nullable: false, + optional: false, nullable: true, default: '/assets/ai.png', }, bannerUrl: { type: 'string', - optional: false, nullable: false, + optional: false, nullable: true, }, errorImageUrl: { type: 'string', - optional: false, nullable: false, + optional: false, nullable: true, default: 'https://xn--931a.moe/aiart/yubitun.png', }, iconUrl: { type: 'string', optional: false, nullable: true, }, - maxNoteTextLength: { - type: 'number', - optional: false, nullable: false, - }, - emojis: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - aliases: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - category: { - type: 'string', - optional: false, nullable: true, - }, - host: { - type: 'string', - optional: false, nullable: true, - }, - url: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - }, - }, - }, - ads: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - place: { - type: 'string', - optional: false, nullable: false, - }, - url: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - imageUrl: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - }, - }, - }, enableEmail: { type: 'boolean', optional: false, nullable: false, @@ -146,10 +82,6 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, - proxyAccountName: { - type: 'string', - optional: false, nullable: true, - }, userStarForReactionFallback: { type: 'boolean', optional: true, nullable: false, @@ -228,7 +160,7 @@ export const meta = { optional: true, nullable: true, }, smtpPort: { - type: 'string', + type: 'number', optional: true, nullable: true, }, smtpUser: { @@ -299,6 +231,10 @@ export const meta = { type: 'boolean', optional: true, nullable: false, }, + policies: { + type: 'object', + optional: false, nullable: false, + }, }, }, } as const; @@ -349,7 +285,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { iconUrl: instance.iconUrl, backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, - maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互æ›æ€§ã®ãŸã‚ defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, enableEmail: instance.enableEmail, diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 9129f53f06c8caa37d70a0b18956f43971341ffb..099e2ff220b9fafe4a0195e93637bfecebad310b 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index 32ad79918f5329f0992817f6b26e72ee539a2532..f12738bd3a2d17f722dd6f5d8cd2027baf775283 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -1,5 +1,5 @@ import { URL } from 'node:url'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index 079b351add2a57ec414ef599654c0c590762fc7e..910c90e78e08e88d1926cf174258a4e621e3aa1d 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 9dc4105d14baca766584b637db35d3294d061cd2..5e26f61fa778ecb52478b9c93003b72e81725c58 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RelayService } from '@/core/RelayService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index cdaec13a3f2339d1e0178f660d000487169dcc7e..d0d52089e60036a7c24792fc649a3fee3012f205 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const actor = await this.instanceActorService.getInstanceActor(); const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId }); - this.queueService.deliver(actor, this.apRendererService.renderActivity(this.apRendererService.renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); + this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox); } await this.abuseUserReportsRepository.update(report.id, { diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts index 458a8d535b1adef1e4f652f9b65520edca94cc82..edaf638ea971de7bd2461f5037dae7cf1ebbe81a 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RolesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '@/server/api/error.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; export const meta = { @@ -33,7 +32,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const roles = await this.rolesRepository.find({ order: { lastUsedAt: 'DESC' }, }); - return await this.roleEntityService.packMany(roles, me, { detail: false }); + return await this.roleEntityService.packMany(roles, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts index c83f96191d96c245b11a9c485a9b3e97c6ee5651..01028a086f5b6abbdc44d8b711477948bfadd29a 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts @@ -39,12 +39,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private roleEntityService: RoleEntityService, ) { - super(meta, paramDef, async (ps) => { + super(meta, paramDef, async (ps, me) => { const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); if (role == null) { throw new ApiError(meta.errors.noSuchRole); } - return await this.roleEntityService.pack(role); + return await this.roleEntityService.pack(role, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts index 6006816bcb611a7dc9a12eb1f0356f3a95c56dff..5a34eee96cc2d54298f479d2390200faaadd121f 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts @@ -1,9 +1,6 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '@/server/api/error.js'; import { MetaService } from '@/core/MetaService.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb016a8425d771b935c0b580e6dfc6cc61c9a8e6 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -0,0 +1,71 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin', 'role', 'users'], + + requireCredential: false, + requireAdmin: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '224eff5e-2488-4b18-b3e7-f50d94421648', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['roleId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private queryService: QueryService, + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ + id: ps.roleId, + }); + + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) + .andWhere('assign.roleId = :roleId', { roleId: role.id }) + .innerJoinAndSelect('assign.user', 'user'); + + const assigns = await query + .take(ps.limit) + .getMany(); + + return await Promise.all(assigns.map(async assign => ({ + id: assign.id, + user: await this.userEntityService.pack(assign.user!, me, { detail: true }), + }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index 7434bf4c9132e54272ea7023083859cf2ecd30b4..5ddc62f476a74c06c5dff0edc3a869b1c4726bbf 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmailService } from '@/core/EmailService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 823af6d8bec1662489d13067ed278113cb429932..9d19efbbcfefc8ca5dc9b505744b7499c7c012b1 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -59,12 +59,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new Error('cannot show info of admin'); } - if (!await this.roleService.isAdministrator(_me)) { - return { - isSuspended: user.isSuspended, - }; - } - const signins = await this.signinsRepository.findBy({ userId: user.id }); const roles = await this.roleService.getUserRoles(user.id); @@ -89,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { moderationNote: profile.moderationNote, signins, policies: await this.roleService.getUserPolicies(user.id), - roles: await this.roleEntityService.packMany(roles, me, { detail: false }), + roles: await this.roleEntityService.packMany(roles, me), }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 354ef22aa78cfb44b923bb60fda20a0d103e9d83..a7531aae898f8d61ed9d2962c7f15d092d003da8 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -2,10 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import type { Meta } from '@/models/entities/Meta.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MetaService } from '@/core/MetaService.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index a1553b6a80db6bee83ed7416c30a37d6a88a24ac..bc5d249ae56eb6aa5f2611d3ac07389d2427055e 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import type { UserListsRepository, UserGroupJoiningsRepository, AntennasRepository } from '@/models/index.js'; +import type { UserListsRepository, AntennasRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -22,12 +22,6 @@ export const meta = { id: '95063e93-a283-4b8b-9aa5-bcdb8df69a7f', }, - noSuchUserGroup: { - message: 'No such user group.', - code: 'NO_SUCH_USER_GROUP', - id: 'aa3c0b9a-8cae-47c0-92ac-202ce5906682', - }, - tooManyAntennas: { message: 'You cannot create antenna any more.', code: 'TOO_MANY_ANTENNAS', @@ -46,9 +40,8 @@ export const paramDef = { type: 'object', properties: { name: { type: 'string', minLength: 1, maxLength: 100 }, - src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'group'] }, + src: { type: 'string', enum: ['home', 'all', 'users', 'list'] }, userListId: { type: 'string', format: 'misskey:id', nullable: true }, - userGroupId: { type: 'string', format: 'misskey:id', nullable: true }, keywords: { type: 'array', items: { type: 'array', items: { type: 'string', @@ -80,9 +73,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - private antennaEntityService: AntennaEntityService, private roleService: RoleService, private idService: IdService, @@ -97,7 +87,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } let userList; - let userGroupJoining; if (ps.src === 'list' && ps.userListId) { userList = await this.userListsRepository.findOneBy({ @@ -108,15 +97,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (userList == null) { throw new ApiError(meta.errors.noSuchUserList); } - } else if (ps.src === 'group' && ps.userGroupId) { - userGroupJoining = await this.userGroupJoiningsRepository.findOneBy({ - userGroupId: ps.userGroupId, - userId: me.id, - }); - - if (userGroupJoining == null) { - throw new ApiError(meta.errors.noSuchUserGroup); - } } const antenna = await this.antennasRepository.insert({ @@ -126,7 +106,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { name: ps.name, src: ps.src, userListId: userList ? userList.id : null, - userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, keywords: ps.keywords, excludeKeywords: ps.excludeKeywords, users: ps.users, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 1955eac949cfb2f2bfa97b64facc5c706b13e0eb..3f85442131b692f71ba57bc3eb299ba8be7441c3 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AntennasRepository, UserListsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { AntennasRepository, UserListsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -25,12 +25,6 @@ export const meta = { code: 'NO_SUCH_USER_LIST', id: '1c6b35c9-943e-48c2-81e4-2844989407f7', }, - - noSuchUserGroup: { - message: 'No such user group.', - code: 'NO_SUCH_USER_GROUP', - id: '109ed789-b6eb-456e-b8a9-6059d567d385', - }, }, res: { @@ -45,9 +39,8 @@ export const paramDef = { properties: { antennaId: { type: 'string', format: 'misskey:id' }, name: { type: 'string', minLength: 1, maxLength: 100 }, - src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'group'] }, + src: { type: 'string', enum: ['home', 'all', 'users', 'list'] }, userListId: { type: 'string', format: 'misskey:id', nullable: true }, - userGroupId: { type: 'string', format: 'misskey:id', nullable: true }, keywords: { type: 'array', items: { type: 'array', items: { type: 'string', @@ -78,9 +71,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, private antennaEntityService: AntennaEntityService, private globalEventService: GlobalEventService, @@ -97,7 +87,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } let userList; - let userGroupJoining; if (ps.src === 'list' && ps.userListId) { userList = await this.userListsRepository.findOneBy({ @@ -108,22 +97,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (userList == null) { throw new ApiError(meta.errors.noSuchUserList); } - } else if (ps.src === 'group' && ps.userGroupId) { - userGroupJoining = await this.userGroupJoiningsRepository.findOneBy({ - userGroupId: ps.userGroupId, - userId: me.id, - }); - - if (userGroupJoining == null) { - throw new ApiError(meta.errors.noSuchUserGroup); - } } await this.antennasRepository.update(antenna.id, { name: ps.name, src: ps.src, userListId: userList ? userList.id : null, - userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, keywords: ps.keywords, excludeKeywords: ps.excludeKeywords, users: ps.users, diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 8bafb3b1226a23ce6d46ed83bc9ff500e16c8a69..c45a86761c59250faafde876d99394318957032a 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,8 +1,7 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApResolverService } from '@/core/activitypub/ApResolverService.js'; -import { ApiError } from '../../error.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 9470dd3cbb8aaae6fc3525037c57caaf87f1c351..61e05531e62c89835342d8725487b5d34ebf8129 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -3,7 +3,7 @@ import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, NotesRepository } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; -import type { CacheableLocalUser, User } from '@/models/entities/User.js'; +import type { LocalUser, User } from '@/models/entities/User.js'; import { isActor, isPost, getApId } from '@/core/activitypub/type.js'; import type { SchemaType } from '@/misc/schema.js'; import { ApResolverService } from '@/core/activitypub/ApResolverService.js'; @@ -114,7 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { * URIã‹ã‚‰Userã‹Noteを解決ã™ã‚‹ */ @bindThis - private async fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { + private async fetchAny(uri: string, me: LocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { // ブãƒãƒƒã‚¯ã—ã¦ãŸã‚‰ä¸æ– const fetchedMeta = await this.metaService.fetch(); if (this.utilityService.isBlockedHost(fetchedMeta.blockedHosts, this.utilityService.extractDbHost(uri))) return null; @@ -147,7 +147,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } @bindThis - private async mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise<SchemaType<typeof meta.res> | null> { + private async mergePack(me: LocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise<SchemaType<typeof meta.res> | null> { if (user != null) { return { type: 'User', diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index 862ef892684470587102bee4827e1f26f1900bc6..2ab58e4309a2e2a6d763a9197fa8e82fecb81c55 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 1d5b8f05f89d3d8bae8b00b252fab3c9ae74bed9..e40a53d82ef85a72756be88f36633c0e9a46b245 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index ec28fa75de1a915e298e1725b4624c02ec0e0df5..9a5aff4af97113e3e89ada70466c40ed04ffb2c5 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import DriveChart from '@/core/chart/charts/drive.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 6c24cbbb77463c2f18c620e7f2da82f1043816d2..ed3a9686812089820f63549932b6ce96f73df256 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import FederationChart from '@/core/chart/charts/federation.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index a6a538ea5c8a5b720f514c620193a3f85996a391..c992d525c961e5f9e227e397024e5f9dc09a313f 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import InstanceChart from '@/core/chart/charts/instance.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 8d03f2eaf1d040a70facab2639ea52ae95e21f94..5750cd5b78645dfbe67f5c9bd54869eb3c87df13 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import NotesChart from '@/core/chart/charts/notes.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index 87d56f38b76e95fb30cf42138bda456745477f7d..5e372294b7846a45adbdbc513320ecc88f7511a7 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index 7a61544aea46d629d339ae367ccf0e0cb3ad9e10..3f50918fa794df2a36a4680c6984bb0d361bcc46 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getJsonSchema } from '@/core/chart/core.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index fdc385191f9c9526c11ef1652b33eb8d41ce2df3..0517b3283f7f10187e9f16f7d21af55822e1c1b8 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/user/pv.ts b/packages/backend/src/server/api/endpoints/charts/user/pv.ts index 33652c3adf2a3dbea1c70855232e5b10a0d81c34..8d1a9aee10462d0423efac89245b06fbcedee2f5 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/pv.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/pv.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index f0f3e520dac16d57541236fe0060a93641bf8227..f2ff41319595bdd03f1888525e8a4001f4704659 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index d09f2512e51042e3d5d98c5d2f521b838d0a1f17..1374f02046708a6dae82c195d05c844f67be1b48 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/core/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import UsersChart from '@/core/chart/charts/users.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 55778c7ecb8a96066e5380bb007556acb279bfbc..5d88870ed27f57246c6eb1f977d0428bad758726 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -1,72 +1,72 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; -import { GetterService } from '@/server/api/GetterService.js'; - -export const meta = { - tags: ['account', 'notes', 'clips'], - - requireCredential: true, - - kind: 'write:account', - - errors: { - noSuchClip: { - message: 'No such clip.', - code: 'NO_SUCH_CLIP', - id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52', - }, - - noSuchNote: { - message: 'No such note.', - code: 'NO_SUCH_NOTE', - id: 'aff017de-190e-434b-893e-33a9ff5049d8', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - clipId: { type: 'string', format: 'misskey:id' }, - noteId: { type: 'string', format: 'misskey:id' }, - }, - required: ['clipId', 'noteId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.clipsRepository) - private clipsRepository: ClipsRepository, - - @Inject(DI.clipNotesRepository) - private clipNotesRepository: ClipNotesRepository, - - private getterService: GetterService, - ) { - super(meta, paramDef, async (ps, me) => { - const clip = await this.clipsRepository.findOneBy({ - id: ps.clipId, - userId: me.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); - } - - const note = await this.getterService.getNote(ps.noteId).catch(err => { - if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw err; - }); - - await this.clipNotesRepository.delete({ - noteId: note.id, - clipId: clip.id, - }); - }); - } -} +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../error.js'; +import { GetterService } from '@/server/api/GetterService.js'; + +export const meta = { + tags: ['account', 'notes', 'clips'], + + requireCredential: true, + + kind: 'write:account', + + errors: { + noSuchClip: { + message: 'No such clip.', + code: 'NO_SUCH_CLIP', + id: 'b80525c6-97f7-49d7-a42d-ebccd49cfd52', + }, + + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'aff017de-190e-434b-893e-33a9ff5049d8', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + clipId: { type: 'string', format: 'misskey:id' }, + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['clipId', 'noteId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.clipsRepository) + private clipsRepository: ClipsRepository, + + @Inject(DI.clipNotesRepository) + private clipNotesRepository: ClipNotesRepository, + + private getterService: GetterService, + ) { + super(meta, paramDef, async (ps, me) => { + const clip = await this.clipsRepository.findOneBy({ + id: ps.clipId, + userId: me.id, + }); + + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } + + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; + }); + + await this.clipNotesRepository.delete({ + noteId: note.id, + clipId: clip.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index e5bbfecbcf4cf176aa85cc8eb798372a61d4dd77..a6ece0311bc2c084bac64653e60724174c8fe6e1 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index 0fe57de6a88bbf866f5465813662d8dc1022934a..3141e0fc01b8ff651f864817f38461bd39fc835e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; -import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index a17bca5abad1fc00fef8defd72dcaacc138dd872..cfef7938318f67f346ad31f765cb891bab0985cf 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -1,7 +1,6 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import type { DriveFilesRepository } from '@/models/index.js'; -import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 8a497a514ecb4055e6488ab0802211dc52fc2573..0f13b14d0197a207dbf9ad420c12d2342474b3d2 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { EmailService } from '@/core/EmailService.js'; diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts index 77854afb331066f49b8a4da4408516c7b90a34e2..325b7583581baf4a4c8ba3119997cb4c5a40bca7 100644 --- a/packages/backend/src/server/api/endpoints/emojis.ts +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -1,4 +1,4 @@ -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { EmojisRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -82,11 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { }); return { - emojis: await this.emojiEntityService.packMany(emojis, { - omitId: true, - omitHost: true, - withUrl: true, - }), + emojis: await this.emojiEntityService.packSimpleMany(emojis), }; }); } diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index 13b91685a44c4846cb83624acb03bbe57ebba8bc..b38c97f60a0ed8b439e656895623e9de9377c1c7 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import endpoints from '../endpoints.js'; diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index 91fc3ec98d3f81816511c2db1344bc9b6a0e3c54..9e706db747222f5245efdca5e7f4fb43966872cf 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import endpoints from '../endpoints.js'; diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index ead6b037cc7bdfa8fc5c3190b150a6f9afd989f4..6b6079ad51f05227bcb7045ee06b18db144e3855 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,5 +1,5 @@ import ms from 'ms'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index e5d1df0018a14391e2833d5ddfd937974841d836..60b24e9585986344d778f957bdd89c624c7b45a6 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -10,6 +10,8 @@ export const meta = { tags: ['federation'], requireCredential: false, + allowGet: true, + cacheSec: 3600, res: { type: 'array', diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index c19252f1986932a325fbe9c105237c136cf4073f..4596e0c0b5394debdd7b9773a801322305aa41b9 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { GetterService } from '@/server/api/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts index a652047d9819a9fc52f44797cbde9b42abaf3f73..f21d9d5c336347a14e7c261a355f5c355431cdfe 100644 --- a/packages/backend/src/server/api/endpoints/flash/create.ts +++ b/packages/backend/src/server/api/endpoints/flash/create.ts @@ -1,13 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository, FlashsRepository, PagesRepository } from '@/models/index.js'; +import type { FlashsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; -import { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; -import { ApiError } from '../../error.js'; export const meta = { tags: ['flash'], diff --git a/packages/backend/src/server/api/endpoints/flash/show.ts b/packages/backend/src/server/api/endpoints/flash/show.ts index 48114c5a607f557dbccf6b363617975b79c2a4e0..14720a8c8dc41e16fb1041013c7fcd444093273b 100644 --- a/packages/backend/src/server/api/endpoints/flash/show.ts +++ b/packages/backend/src/server/api/endpoints/flash/show.ts @@ -1,7 +1,5 @@ -import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, FlashsRepository } from '@/models/index.js'; -import type { Flash } from '@/models/entities/Flash.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts index 9ab17a61e8959c9014a35bd293051a7f099fcc6c..cd4e413a403b5bfe2197963778943289ec982f9b 100644 --- a/packages/backend/src/server/api/endpoints/flash/update.ts +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -1,5 +1,4 @@ import ms from 'ms'; -import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { FlashsRepository, DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index dcb98485da3cd515b96d1bae57c163502f36dc60..cca3e606146fe3331b70439016cd4583e2eb501a 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index f39c4e3767f078de31f7a45ede8ce8eecbef6ae4..7325e73cac2ecd64f435d17b980bf04245a453d4 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GetterService } from '@/server/api/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index ab5706e8efd7e8a2790f1722f89f77b3c24c5a68..a8fdc448769e9dd1f8ff7f6be0686e885c35c510 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 3d9d4715025b239fb4ac7a755c3ac671d53ed4b8..cb8b6a2e3eb1d74555e0688bf0c23a9ec8cce74a 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -7,7 +7,6 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import { IdService } from '@/core/IdService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; export const meta = { tags: ['gallery'], diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index d261aaa96642f4936b1e55781b82b7bfe4598f53..f14d644a3aff4b49e3d8cd63d364f758aa1f1ee5 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -2,11 +2,9 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; -import { GalleryPost } from '@/models/entities/GalleryPost.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; export const meta = { tags: ['gallery'], diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index ec9ac1ef90dad2ab2a504af999a1fe924aac7507..6c31075e05d2f87a27c925a7cc9c07e57b8bbabd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,7 +1,10 @@ -import * as speakeasy from 'speakeasy'; +import * as OTPAuth from 'otpauth'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -22,8 +25,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const token = ps.token.replace(/\s/g, ''); @@ -34,13 +43,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { throw new Error('二段階èªè¨¼ã®è¨å®šãŒé–‹å§‹ã•ã‚Œã¦ã„ã¾ã›ã‚“'); } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorTempSecret, - encoding: 'base32', - token: token, + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret), + digits: 6, + token, + window: 1, }); - if (!verified) { + if (delta === null) { throw new Error('not verified'); } @@ -48,6 +58,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { twoFactorSecret: profile.twoFactorTempSecret, twoFactorEnabled: true, }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + includeSecrets: true, + })); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 6e0849f2b26208afbe79454d7153511868d60c91..ad33398da60fc8331b902ccd053d82e0bb254a32 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -25,7 +25,7 @@ export const paramDef = { attestationObject: { type: 'string' }, password: { type: 'string' }, challengeId: { type: 'string' }, - name: { type: 'string' }, + name: { type: 'string', minLength: 1, maxLength: 30 }, }, required: ['clientDataJSON', 'attestationObject', 'password', 'challengeId', 'name'], } as const; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 0655a86350b9c7221a2c1bc2b07454f3b2106212..0ee9f556a87acd90458c731bbed499a3de32fa04 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,12 +1,23 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfilesRepository } from '@/models/index.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../../error.js'; export const meta = { requireCredential: true, secure: true, + + errors: { + noKey: { + message: 'No security key.', + code: 'NO_SECURITY_KEY', + id: 'f9c54d7f-d4c2-4d3c-9a8g-a70daac86512', + }, + }, } as const; export const paramDef = { @@ -23,11 +34,45 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + @Inject(DI.userSecurityKeysRepository) + private userSecurityKeysRepository: UserSecurityKeysRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + if (ps.value === true) { + // ã‚»ã‚ュリティã‚ーãŒãªã‘ã‚Œã°ãƒ‘スワードレスを有効ã«ã¯ã§ããªã„ + const keyCount = await this.userSecurityKeysRepository.count({ + where: { + userId: me.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }); + + if (keyCount === 0) { + await this.userProfilesRepository.update(me.id, { + usePasswordLessLogin: false, + }); + + throw new ApiError(meta.errors.noKey); + } + } + await this.userProfilesRepository.update(me.id, { usePasswordLessLogin: ps.value, }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + includeSecrets: true, + })); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index a539c5c2211576088562858aa50b1414598ac0c3..eb4d7f9c14b127445bdcdcb427bfd8c7adf8c3f6 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -1,5 +1,5 @@ import bcrypt from 'bcryptjs'; -import * as speakeasy from 'speakeasy'; +import * as OTPAuth from 'otpauth'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository } from '@/models/index.js'; @@ -42,25 +42,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { } // Generate user's secret key - const secret = speakeasy.generateSecret({ - length: 32, - }); + const secret = new OTPAuth.Secret(); await this.userProfilesRepository.update(me.id, { twoFactorTempSecret: secret.base32, }); // Get the data URL of the authenticator URL - const url = speakeasy.otpauthURL({ - secret: secret.base32, - encoding: 'base32', + const totp = new OTPAuth.TOTP({ + secret, + digits: 6, label: me.username, issuer: this.config.host, }); - const dataUrl = await QRCode.toDataURL(url); + const url = totp.toString(); + const qr = await QRCode.toDataURL(url); return { - qr: dataUrl, + qr, url, secret: secret.base32, label: me.username, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index f40ec9797da39eab1781fb8d1208f67c9e9512ca..4b726aed80bfcbc5777ddd013a76200e2cfe8498 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -2,7 +2,6 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -51,6 +50,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { id: ps.credentialId, }); + // 使ã‚ã‚Œã¦ã„ã‚‹ã‚ーãŒãªããªã£ãŸã‚‰ãƒ‘スワードレスãƒã‚°ã‚¤ãƒ³ã‚’ã‚„ã‚ã‚‹ + const keyCount = await this.userSecurityKeysRepository.count({ + where: { + userId: me.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }); + + if (keyCount === 0) { + await this.userProfilesRepository.update(me.id, { + usePasswordLessLogin: false, + }); + } + // Publish meUpdated event this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { detail: true, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 4c5b151f78fd5237be3df0ed631d8cc2d89b45df..e0e7ba665831f62fe26480b840b0235e560ebd36 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,7 +1,9 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserProfilesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -24,6 +26,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); @@ -38,7 +43,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { await this.userProfilesRepository.update(me.id, { twoFactorSecret: null, twoFactorEnabled: false, + usePasswordLessLogin: false, }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + includeSecrets: true, + })); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts new file mode 100644 index 0000000000000000000000000000000000000000..d98f60fa5f82f7a8f84d0a40858d2a3d5133898b --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -0,0 +1,78 @@ +import bcrypt from 'bcryptjs'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + requireCredential: true, + + secure: true, + + errors: { + noSuchKey: { + message: 'No such key.', + code: 'NO_SUCH_KEY', + id: 'f9c5467f-d492-4d3c-9a8g-a70dacc86512', + }, + + accessDenied: { + message: 'You do not have edit privilege of the channel.', + code: 'ACCESS_DENIED', + id: '1fb7cb09-d46a-4fff-b8df-057708cce513', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 30 }, + credentialId: { type: 'string' }, + }, + required: ['name', 'credentialId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.userSecurityKeysRepository) + private userSecurityKeysRepository: UserSecurityKeysRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps, me) => { + const key = await this.userSecurityKeysRepository.findOneBy({ + id: ps.credentialId, + }); + + if (key == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + if (key.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.userSecurityKeysRepository.update(key.id, { + name: ps.name, + }); + + // Publish meUpdated event + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + detail: true, + includeSecrets: true, + })); + + return {}; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts index d7109c6953f063c2b6d0755758219911c555018f..102dae4fb765b4a1f4b827834836b363cb0b9d47 100644 --- a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts +++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts @@ -1,6 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index 770708e68515fe1a2a8224de66f977d6176580c6..4be88cbc2b4a6e76066fc217b035705974fb8374 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/export-favorites.ts b/packages/backend/src/server/api/endpoints/i/export-favorites.ts index b32f39d3e5257d48141a490e43ae566f74cb553c..f522d4c40993614881806bac43748e9a180ceaa0 100644 --- a/packages/backend/src/server/api/endpoints/i/export-favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/export-favorites.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index fcaa59b12dbbb2885efe8eae4bee282f541e0849..1741781c0fd1927876fe8192ff7224a63db88f2c 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 37bef0a117d1d2f08a6351ecd6f1e9021c812286..8e8042b1f9978f4a5af79c768f939dc598cbb856 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 9d2505e403349856b56e2ff3499ab6d8ea4321dd..ed54c9991cb0454de7a60f30d0deaa8ec4aac8eb 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index 0f8e4bca76b785dfb9490dbc16976b9e166e3dcf..5c2be38b71e8fdad8b23daae2f420c4af68869fd 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index f31b0dc35e2f02438aca7e1b178ef2160020de37..d4af00027e09968aadd182c287fc91c0176b8366 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,6 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NotePiningService } from '@/core/NotePiningService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts deleted file mode 100644 index 109d6d1068071afb6b689bcc528073ded3eecf45..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['account', 'messaging'], - - requireCredential: true, - - kind: 'write:account', -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps, me) => { - // Update documents - await this.messagingMessagesRepository.update({ - recipientId: me.id, - isRead: false, - }, { - isRead: true, - }); - - const joinings = await this.userGroupJoiningsRepository.findBy({ userId: me.id }); - - await Promise.all(joinings.map(j => this.messagingMessagesRepository.createQueryBuilder().update() - .set({ - reads: (() => `array_append("reads", '${me.id}')`) as any, - }) - .where('groupId = :groupId', { groupId: j.userGroupId }) - .andWhere('userId != :userId', { userId: me.id }) - .andWhere('NOT (:userId = ANY(reads))', { userId: me.id }) - .execute())); - - this.globalEventService.publishMainStream(me.id, 'readAllMessagingMessages'); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index cb5b4b0a60d513b8c4f6d5ffb5550fef22dfb05c..b8922b91e5d4df872a0cb2b6dc9110cc6a2b415e 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 9a735e116876929ccd5582160eb2f12d9304bc9e..db239dc284fc716d550a4675e8c6af924b4c22fe 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,6 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NotePiningService } from '@/core/NotePiningService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts deleted file mode 100644 index 1ad2f7d68fdba272b4bdb69c31ca8198854295fd..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserGroupInvitationsRepository } from '@/models/index.js'; -import { QueryService } from '@/core/QueryService.js'; -import { UserGroupInvitationEntityService } from '@/core/entities/UserGroupInvitationEntityService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['account', 'groups'], - - requireCredential: true, - - kind: 'read:user-groups', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - group: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, - }, - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - }, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupInvitationsRepository) - private userGroupInvitationsRepository: UserGroupInvitationsRepository, - - private userGroupInvitationEntityService: UserGroupInvitationEntityService, - private queryService: QueryService, - ) { - super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(this.userGroupInvitationsRepository.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) - .andWhere('invitation.userId = :meId', { meId: me.id }) - .leftJoinAndSelect('invitation.userGroup', 'user_group'); - - const invitations = await query - .take(ps.limit) - .getMany(); - - return await this.userGroupInvitationEntityService.packMany(invitations); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts deleted file mode 100644 index 0b6099d4acf8b97f6f1ed28218a7c3349db61145..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Brackets } from 'typeorm'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; -import type { MutingsRepository, UserGroupJoiningsRepository, MessagingMessagesRepository } from '@/models/index.js'; -import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['messaging'], - - requireCredential: true, - - kind: 'read:messaging', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'MessagingMessage', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - group: { type: 'boolean', default: false }, - }, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.mutingsRepository) - private mutingsRepository: MutingsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private messagingMessageEntityService: MessagingMessageEntityService, - ) { - super(meta, paramDef, async (ps, me) => { - const mute = await this.mutingsRepository.findBy({ - muterId: me.id, - }); - - const groups = ps.group ? await this.userGroupJoiningsRepository.findBy({ - userId: me.id, - }).then(xs => xs.map(x => x.userGroupId)) : []; - - if (ps.group && groups.length === 0) { - return []; - } - - const history: MessagingMessage[] = []; - - for (let i = 0; i < ps.limit; i++) { - const found = ps.group - ? history.map(m => m.groupId!) - : history.map(m => (m.userId === me.id) ? m.recipientId! : m.userId!); - - const query = this.messagingMessagesRepository.createQueryBuilder('message') - .orderBy('message.createdAt', 'DESC'); - - if (ps.group) { - query.where('message.groupId IN (:...groups)', { groups: groups }); - - if (found.length > 0) { - query.andWhere('message.groupId NOT IN (:...found)', { found: found }); - } - } else { - query.where(new Brackets(qb => { qb - .where('message.userId = :userId', { userId: me.id }) - .orWhere('message.recipientId = :userId', { userId: me.id }); - })); - query.andWhere('message.groupId IS NULL'); - - if (found.length > 0) { - query.andWhere('message.userId NOT IN (:...found)', { found: found }); - query.andWhere('message.recipientId NOT IN (:...found)', { found: found }); - } - - if (mute.length > 0) { - query.andWhere('message.userId NOT IN (:...mute)', { mute: mute.map(m => m.muteeId) }); - query.andWhere('message.recipientId NOT IN (:...mute)', { mute: mute.map(m => m.muteeId) }); - } - } - - const message = await query.getOne(); - - if (message) { - history.push(message); - } else { - break; - } - } - - return await Promise.all(history.map(h => this.messagingMessageEntityService.pack(h.id, me))); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts deleted file mode 100644 index 3673e252ae43437840c21bf096f85394d64eadd1..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Brackets } from 'typeorm'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository, UserGroupsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { QueryService } from '@/core/QueryService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; -import { GetterService } from '@/server/api/GetterService.js'; - -export const meta = { - tags: ['messaging'], - - requireCredential: true, - - kind: 'read:messaging', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'MessagingMessage', - }, - }, - - errors: { - noSuchUser: { - message: 'No such user.', - code: 'NO_SUCH_USER', - id: '11795c64-40ea-4198-b06e-3c873ed9039d', - }, - - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: 'c4d9f88c-9270-4632-b032-6ed8cee36f7f', - }, - - groupAccessDenied: { - message: 'You can not read messages of groups that you have not joined.', - code: 'GROUP_ACCESS_DENIED', - id: 'a053a8dd-a491-4718-8f87-50775aad9284', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - markAsRead: { type: 'boolean', default: true }, - }, - anyOf: [ - { - properties: { - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], - }, - { - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId'], - }, - ], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.userGroupsRepository) - private userGroupRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private messagingMessageEntityService: MessagingMessageEntityService, - private messagingService: MessagingService, - private userEntityService: UserEntityService, - private queryService: QueryService, - private getterService: GetterService, - ) { - super(meta, paramDef, async (ps, me) => { - if (ps.userId != null) { - // Fetch recipient (user) - const recipient = await this.getterService.getUser(ps.userId).catch(err => { - if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw err; - }); - - const query = this.queryService.makePaginationQuery(this.messagingMessagesRepository.createQueryBuilder('message'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where(new Brackets(qb => { qb - .where('message.userId = :meId') - .andWhere('message.recipientId = :recipientId'); - })) - .orWhere(new Brackets(qb => { qb - .where('message.userId = :recipientId') - .andWhere('message.recipientId = :meId'); - })); - })) - .setParameter('meId', me.id) - .setParameter('recipientId', recipient.id); - - const messages = await query.take(ps.limit).getMany(); - - // Mark all as read - if (ps.markAsRead) { - this.messagingService.readUserMessagingMessage(me.id, recipient.id, messages.filter(m => m.recipientId === me.id).map(x => x.id)); - - // リモートユーザーã¨ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã ã£ãŸã‚‰æ—¢èªé…ä¿¡ - if (this.userEntityService.isLocalUser(me) && this.userEntityService.isRemoteUser(recipient)) { - this.messagingService.deliverReadActivity(me, recipient, messages); - } - } - - return await Promise.all(messages.map(message => this.messagingMessageEntityService.pack(message, me, { - populateRecipient: false, - }))); - } else if (ps.groupId != null) { - // Fetch recipient (group) - const recipientGroup = await this.userGroupRepository.findOneBy({ id: ps.groupId }); - - if (recipientGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // check joined - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userId: me.id, - userGroupId: recipientGroup.id, - }); - - if (joining == null) { - throw new ApiError(meta.errors.groupAccessDenied); - } - - const query = this.queryService.makePaginationQuery(this.messagingMessagesRepository.createQueryBuilder('message'), ps.sinceId, ps.untilId) - .andWhere('message.groupId = :groupId', { groupId: recipientGroup.id }); - - const messages = await query.take(ps.limit).getMany(); - - // Mark all as read - if (ps.markAsRead) { - this.messagingService.readGroupMessagingMessage(me.id, recipientGroup.id, messages.map(x => x.id)); - } - - return await Promise.all(messages.map(message => this.messagingMessageEntityService.pack(message, me, { - populateGroup: false, - }))); - } - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts deleted file mode 100644 index e9ffc7a9eb402279eb2f36d2d4f078c634f95200..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import ms from 'ms'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { BlockingsRepository, UserGroupJoiningsRepository, DriveFilesRepository, UserGroupsRepository } from '@/models/index.js'; -import type { User } from '@/models/entities/User.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['messaging'], - - requireCredential: true, - - kind: 'write:messaging', - - limit: { - duration: ms('1hour'), - max: 120, - }, - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'MessagingMessage', - }, - - errors: { - recipientIsYourself: { - message: 'You can not send a message to yourself.', - code: 'RECIPIENT_IS_YOURSELF', - id: '17e2ba79-e22a-4cbc-bf91-d327643f4a7e', - }, - - noSuchUser: { - message: 'No such user.', - code: 'NO_SUCH_USER', - id: '11795c64-40ea-4198-b06e-3c873ed9039d', - }, - - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: 'c94e2a5d-06aa-4914-8fa6-6a42e73d6537', - }, - - groupAccessDenied: { - message: 'You can not send messages to groups that you have not joined.', - code: 'GROUP_ACCESS_DENIED', - id: 'd96b3cca-5ad1-438b-ad8b-02f931308fbd', - }, - - noSuchFile: { - message: 'No such file.', - code: 'NO_SUCH_FILE', - id: '4372b8e2-185d-4146-8749-2f68864a3e5f', - }, - - contentRequired: { - message: 'Content required. You need to set text or fileId.', - code: 'CONTENT_REQUIRED', - id: '25587321-b0e6-449c-9239-f8925092942c', - }, - - youHaveBeenBlocked: { - message: 'You cannot send a message because you have been blocked by this user.', - code: 'YOU_HAVE_BEEN_BLOCKED', - id: 'c15a5199-7422-4968-941a-2a462c478f7d', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - text: { type: 'string', nullable: true, maxLength: 3000 }, - fileId: { type: 'string', format: 'misskey:id' }, - }, - anyOf: [ - { - properties: { - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], - }, - { - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId'], - }, - ], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - @Inject(DI.blockingsRepository) - private blockingsRepository: BlockingsRepository, - - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - - private getterService: GetterService, - private messagingService: MessagingService, - ) { - super(meta, paramDef, async (ps, me) => { - let recipientUser: User | null; - let recipientGroup: UserGroup | null; - - if (ps.userId != null) { - // Myself - if (ps.userId === me.id) { - throw new ApiError(meta.errors.recipientIsYourself); - } - - // Fetch recipient (user) - recipientUser = await this.getterService.getUser(ps.userId).catch(err => { - if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw err; - }); - - // Check blocking - const block = await this.blockingsRepository.findOneBy({ - blockerId: recipientUser.id, - blockeeId: me.id, - }); - if (block) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } else if (ps.groupId != null) { - // Fetch recipient (group) - recipientGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId! }); - - if (recipientGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // check joined - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userId: me.id, - userGroupId: recipientGroup.id, - }); - - if (joining == null) { - throw new ApiError(meta.errors.groupAccessDenied); - } - } - - let file = null; - if (ps.fileId != null) { - file = await this.driveFilesRepository.findOneBy({ - id: ps.fileId, - userId: me.id, - }); - - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } - } - - // テã‚ストãŒç„¡ã„ã‹ã¤æ·»ä»˜ãƒ•ã‚¡ã‚¤ãƒ«ã‚‚ç„¡ã‹ã£ãŸã‚‰ã‚¨ãƒ©ãƒ¼ - if (ps.text == null && file == null) { - throw new ApiError(meta.errors.contentRequired); - } - - return await this.messagingService.createMessage(me, recipientUser, recipientGroup, ps.text, file); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts deleted file mode 100644 index cd74f5f197d71feff4836ea27be275bd4a5d6f21..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import ms from 'ms'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MessagingMessagesRepository } from '@/models/index.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['messaging'], - - requireCredential: true, - - kind: 'write:messaging', - - limit: { - duration: ms('1hour'), - max: 300, - minInterval: ms('1sec'), - }, - - errors: { - noSuchMessage: { - message: 'No such message.', - code: 'NO_SUCH_MESSAGE', - id: '54b5b326-7925-42cf-8019-130fda8b56af', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - messageId: { type: 'string', format: 'misskey:id' }, - }, - required: ['messageId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - private messagingService: MessagingService, - ) { - super(meta, paramDef, async (ps, me) => { - const message = await this.messagingMessagesRepository.findOneBy({ - id: ps.messageId, - userId: me.id, - }); - - if (message == null) { - throw new ApiError(meta.errors.noSuchMessage); - } - - await this.messagingService.deleteMessage(message); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts deleted file mode 100644 index bddb6d932d4f82584346848bc9123ac34cfa288d..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MessagingMessagesRepository } from '@/models/index.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['messaging'], - - requireCredential: true, - - kind: 'write:messaging', - - errors: { - noSuchMessage: { - message: 'No such message.', - code: 'NO_SUCH_MESSAGE', - id: '86d56a2f-a9c3-4afb-b13c-3e9bfef9aa14', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - messageId: { type: 'string', format: 'misskey:id' }, - }, - required: ['messageId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - private messagingService: MessagingService, - ) { - super(meta, paramDef, async (ps, me) => { - const message = await this.messagingMessagesRepository.findOneBy({ id: ps.messageId }); - - if (message == null) { - throw new ApiError(meta.errors.noSuchMessage); - } - - if (message.recipientId) { - await this.messagingService.readUserMessagingMessage(me.id, message.userId, [message.id]).catch(err => { - if (err.id === 'e140a4bf-49ce-4fb6-b67c-b78dadf6b52f') throw new ApiError(meta.errors.noSuchMessage); - throw err; - }); - } else if (message.groupId) { - await this.messagingService.readGroupMessagingMessage(me.id, message.groupId, [message.id]).catch(err => { - if (err.id === '930a270c-714a-46b2-b776-ad27276dc569') throw new ApiError(meta.errors.noSuchMessage); - throw err; - }); - } - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 2fa7a09d49af741d141da521b4a0d2c2587d40ef..cdb314a873053223f308a7501dc2fe5813d656b8 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,7 +1,7 @@ -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull, LessThanOrEqual, MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { AdsRepository, EmojisRepository, UsersRepository } from '@/models/index.js'; -import { MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import type { AdsRepository, UsersRepository } from '@/models/index.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -262,6 +262,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const ads = await this.adsRepository.find({ where: { expiresAt: MoreThan(new Date()), + startsAt: LessThanOrEqual(new Date()), }, }); @@ -294,7 +295,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { iconUrl: instance.iconUrl, backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, - maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互æ›æ€§ã®ãŸã‚ + maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, ads: ads.map(ad => ({ diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index a709ab2f7a281808ebfd3cc2dfc298766fc4b82a..593444968efba259522cf90f6e22494e11b32772 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -11,7 +11,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; -import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -281,7 +280,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { files: files, poll: ps.poll ? { choices: ps.poll.choices, - multiple: ps.poll.multiple || false, + multiple: ps.poll.multiple ?? false, expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, } : undefined, text: ps.text ?? undefined, diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 76834cfde9067c97fba7944509a9e18b260b837d..26f69373d1d94bf890374e0bcacb60edac6d11f6 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -9,6 +9,8 @@ export const meta = { tags: ['notes'], requireCredential: false, + allowGet: true, + cacheSec: 3600, res: { type: 'array', @@ -41,7 +43,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const max = 30; const day = 1000 * 60 * 60 * 24 * 3; // 3æ—¥å‰ã¾ã§ const query = this.notesRepository.createQueryBuilder('note') @@ -67,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { let notes = await query .orderBy('note.score', 'DESC') - .take(max) + .take(50) .getMany(); notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index f396f7e584ac72e103b4704e8fc38c360e9a4fc1..18ed6d4e2191498a4b3655b98e289666401ecec9 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 92b82eb5de2e8295689463d67b961465cab10989..dcb0d0adcbd26f373b11734868b24ebc77b6a336 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -4,7 +4,6 @@ import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index befaea46642f54b34b3662158ac6cb871ceb1977..b9e06a7834ee34a9fda88f9cb22e68f96e4d610c 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,7 +1,6 @@ -import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; -import type { IRemoteUser } from '@/models/entities/User.js'; +import type { RemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; @@ -160,9 +159,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // リモート投票ã®å ´åˆãƒªãƒ—ライé€ä¿¡ if (note.userHost != null) { - const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as IRemoteUser; + const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as RemoteUser; - this.queueService.deliver(me, this.apRendererService.renderActivity(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox); + this.queueService.deliver(me, this.apRendererService.addContext(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox); } // リモートフォãƒãƒ¯ãƒ¼ã«Updateé…ä¿¡ diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 02ae212a3021fb6e2327259a7b33b901f4d76127..f758bfe9b16880c2b2dee8b9a7501999e3db8c6b 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,11 +1,9 @@ -import { DeepPartial } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { NoteReactionsRepository } from '@/models/index.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; import type { FindOptionsWhere } from 'typeorm'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 839f893db2783a10e8b5a97fa3bd62b410112f9c..04e374d1aed37211a40cc34f5adc01e0b1a2109e 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ReactionService } from '@/core/ReactionService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index cf90d7b5f68ade1ae87683127ae3c021af11260d..207f0b4cf2b7ae2ab7ee49bacca9fc9e5a5d8ae8 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,5 +1,5 @@ import ms from 'ms'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ReactionService } from '@/core/ReactionService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 8eb031dfe31d5ef570846a9226db0ef16a81ccf0..ef47a3004d739f0fca30ac797e191337581b4c87 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,4 +1,3 @@ -import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 145d3f5c83a4dd28f93ba29695ca15f3c3a472a0..d1c35e36e238dfda0a9761f1e5bd291b4dca88ee 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -5,7 +5,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 3427a3eb5c46d8174821b3d039bf0915b03f9f4b..2e63eee263272a05f6c7124d170523fbdd553dbc 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index cdf8d09f9e488a3661c03c153e160ed1d06d8db3..6262c47fd04514af0ef432f3de8feaf7b4671795 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NotificationService } from '@/core/NotificationService.js'; diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 1841a845394bde604e4e22f66d9d4feca852ab64..1d6fb567f0f264315ffd2450c675018e2f223878 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; import type { PagesRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index 4bb62b298efdd855af4bf3e4f6487090900eb21f..5807bf101e371202d3e88421747b38f8074b6506 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 42b10a4fb3ee24f23325f6ca431eb49bed6ad7d7..3b6ebfe281bace554d07de528aeb1e23637c6949 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -8,7 +8,6 @@ import { IdService } from '@/core/IdService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { EmailService } from '@/core/EmailService.js'; -import { ApiError } from '../error.js'; export const meta = { tags: ['reset password'], diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 526efbc2f6c5bf027cc1dd27e42c79452ee137b3..655dd7cd83d8332696b3574f0e2cd4be1fc4fc2c 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -4,7 +4,6 @@ import Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { resetDb } from '@/misc/reset-db.js'; -import { ApiError } from '../error.js'; export const meta = { tags: ['non-productive'], diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index cf7fcb7afdfc24e767ee6a8e5fa01dfa797b9fda..e6f1af7b220f1bbd0c58a5b67e49b4ad355cf0ed 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,10 +1,8 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../error.js'; export const meta = { tags: ['reset password'], diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts index e3c2249cdd4a6a7ef280577ca75122596cb30e7b..e9c0fd4dcd5ff6c2671685b011a907f90e5167e8 100644 --- a/packages/backend/src/server/api/endpoints/retention.ts +++ b/packages/backend/src/server/api/endpoints/retention.ts @@ -1,4 +1,3 @@ -import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { RetentionAggregationsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/roles/list.ts b/packages/backend/src/server/api/endpoints/roles/list.ts new file mode 100644 index 0000000000000000000000000000000000000000..d61c6b8dc6a522413afdb7f9c740d56290940b4e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/roles/list.ts @@ -0,0 +1,37 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; + +export const meta = { + tags: ['role'], + + requireCredential: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [ + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const roles = await this.rolesRepository.findBy({ + isPublic: true, + }); + return await this.roleEntityService.packMany(roles, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/roles/show.ts b/packages/backend/src/server/api/endpoints/roles/show.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc755dcc769f97b81b3d689486b0c983bf71a94a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/roles/show.ts @@ -0,0 +1,52 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { RolesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['role', 'users'], + + requireCredential: false, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: 'de5502bf-009a-4639-86c1-fec349e46dcb', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + }, + required: ['roleId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ + id: ps.roleId, + isPublic: true, + }); + + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + return await this.roleEntityService.pack(role, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts new file mode 100644 index 0000000000000000000000000000000000000000..6e221b6c67628e92f1f97f823dd1bcc34613935c --- /dev/null +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -0,0 +1,71 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['role', 'users'], + + requireCredential: false, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '30aaaee3-4792-48dc-ab0d-cf501a575ac5', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['roleId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private queryService: QueryService, + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ + id: ps.roleId, + isPublic: true, + }); + + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) + .andWhere('assign.roleId = :roleId', { roleId: role.id }) + .innerJoinAndSelect('assign.user', 'user'); + + const assigns = await query + .take(ps.limit) + .getMany(); + + return await Promise.all(assigns.map(async assign => ({ + id: assign.id, + user: await this.userEntityService.pack(assign.user!, me, { detail: true }), + }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 8989a3073d9c3f217c433188af798d74617c7652..1620e8ae52b51c00ce81426995f6148de3143a39 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -1,6 +1,6 @@ import * as os from 'node:os'; import si from 'systeminformation'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 8bd0311dce9656b96fe7a645bdf718a78220c055..48a85758a0fc4730cc4b319c3acdaf7a7e2bf4d2 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { IsNull } from 'typeorm'; import type { InstancesRepository, NoteReactionsRepository, NotesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index 39ea1f21717de1c93822ddffebc0665d459d7111..c88f7f2daf790a7b06e4b81b9e5beedf0dd48ca1 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts deleted file mode 100644 index 24dbf5ca3c545fd0bba1649b51812332e39c89bc..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import ms from 'ms'; -import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; -import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['groups'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Create a new group.', - - limit: { - duration: ms('1hour'), - max: 10, - }, - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - name: { type: 'string', minLength: 1, maxLength: 100 }, - }, - required: ['name'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private userGroupEntityService: UserGroupEntityService, - private idService: IdService, - ) { - super(meta, paramDef, async (ps, me) => { - const userGroup = await this.userGroupsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: me.id, - name: ps.name, - } as UserGroup).then(x => this.userGroupsRepository.findOneByOrFail(x.identifiers[0])); - - // Push the owner - await this.userGroupJoiningsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: me.id, - userGroupId: userGroup.id, - } as UserGroupJoining); - - return await this.userGroupEntityService.pack(userGroup); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts deleted file mode 100644 index d238ae9f166b150e03d840883745df4d22189db1..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Delete an existing group.', - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: '63dbd64c-cd77-413f-8e08-61781e210b38', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - ) { - super(meta, paramDef, async (ps, me) => { - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - await this.userGroupsRepository.delete(userGroup.id); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts deleted file mode 100644 index f154a57f61ebd78b03e3274d51199d8922bc55f3..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupInvitationsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../../error.js'; - -export const meta = { - tags: ['groups', 'users'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Join a group the authenticated user has been invited to.', - - errors: { - noSuchInvitation: { - message: 'No such invitation.', - code: 'NO_SUCH_INVITATION', - id: '98c11eca-c890-4f42-9806-c8c8303ebb5e', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - invitationId: { type: 'string', format: 'misskey:id' }, - }, - required: ['invitationId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupInvitationsRepository) - private userGroupInvitationsRepository: UserGroupInvitationsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private idService: IdService, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the invitation - const invitation = await this.userGroupInvitationsRepository.findOneBy({ - id: ps.invitationId, - }); - - if (invitation == null) { - throw new ApiError(meta.errors.noSuchInvitation); - } - - if (invitation.userId !== me.id) { - throw new ApiError(meta.errors.noSuchInvitation); - } - - // Push the user - await this.userGroupJoiningsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: me.id, - userGroupId: invitation.userGroupId, - } as UserGroupJoining); - - this.userGroupInvitationsRepository.delete(invitation.id); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts deleted file mode 100644 index 1fd3b2f4b30d96eb099bb57122fc7299dec53c1c..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupInvitationsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../../error.js'; - -export const meta = { - tags: ['groups', 'users'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Delete an existing group invitation for the authenticated user without joining the group.', - - errors: { - noSuchInvitation: { - message: 'No such invitation.', - code: 'NO_SUCH_INVITATION', - id: 'ad7471d4-2cd9-44b4-ac68-e7136b4ce656', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - invitationId: { type: 'string', format: 'misskey:id' }, - }, - required: ['invitationId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupInvitationsRepository) - private userGroupInvitationsRepository: UserGroupInvitationsRepository, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the invitation - const invitation = await this.userGroupInvitationsRepository.findOneBy({ - id: ps.invitationId, - }); - - if (invitation == null) { - throw new ApiError(meta.errors.noSuchInvitation); - } - - if (invitation.userId !== me.id) { - throw new ApiError(meta.errors.noSuchInvitation); - } - - await this.userGroupInvitationsRepository.delete(invitation.id); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts deleted file mode 100644 index 2e040c06010d74d2fdd455595d158e8862cef66a..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository, UserGroupJoiningsRepository, UserGroupInvitationsRepository } from '@/models/index.js'; -import { IdService } from '@/core/IdService.js'; -import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { CreateNotificationService } from '@/core/CreateNotificationService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups', 'users'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Invite a user to an existing group.', - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: '583f8bc0-8eee-4b78-9299-1e14fc91e409', - }, - - noSuchUser: { - message: 'No such user.', - code: 'NO_SUCH_USER', - id: 'da52de61-002c-475b-90e1-ba64f9cf13a8', - }, - - alreadyAdded: { - message: 'That user has already been added to that group.', - code: 'ALREADY_ADDED', - id: '7e35c6a0-39b2-4488-aea6-6ee20bd5da2c', - }, - - alreadyInvited: { - message: 'That user has already been invited to that group.', - code: 'ALREADY_INVITED', - id: 'ee0f58b4-b529-4d13-b761-b9a3e69f97e6', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId', 'userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupInvitationsRepository) - private userGroupInvitationsRepository: UserGroupInvitationsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private idService: IdService, - private getterService: GetterService, - private createNotificationService: CreateNotificationService, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // Fetch the user - const user = await this.getterService.getUser(ps.userId).catch(err => { - if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw err; - }); - - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userGroupId: userGroup.id, - userId: user.id, - }); - - if (joining) { - throw new ApiError(meta.errors.alreadyAdded); - } - - const existInvitation = await this.userGroupInvitationsRepository.findOneBy({ - userGroupId: userGroup.id, - userId: user.id, - }); - - if (existInvitation) { - throw new ApiError(meta.errors.alreadyInvited); - } - - const invitation = await this.userGroupInvitationsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: user.id, - userGroupId: userGroup.id, - } as UserGroupInvitation).then(x => this.userGroupInvitationsRepository.findOneByOrFail(x.identifiers[0])); - - // é€šçŸ¥ã‚’ä½œæˆ - this.createNotificationService.createNotification(user.id, 'groupInvited', { - notifierId: me.id, - userGroupInvitationId: invitation.id, - }); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts deleted file mode 100644 index 8daee3a6f51424715f5bd27febad63b47d55ca4e..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Not, In } from 'typeorm'; -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['groups', 'account'], - - requireCredential: true, - - kind: 'read:user-groups', - - description: 'List the groups that the authenticated user is a member of.', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private userGroupEntityService: UserGroupEntityService, - ) { - super(meta, paramDef, async (ps, me) => { - const ownedGroups = await this.userGroupsRepository.findBy({ - userId: me.id, - }); - - const joinings = await this.userGroupJoiningsRepository.findBy({ - userId: me.id, - ...(ownedGroups.length > 0 ? { - userGroupId: Not(In(ownedGroups.map(x => x.id))), - } : {}), - }); - - return await Promise.all(joinings.map(x => this.userGroupEntityService.pack(x.userGroupId))); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts deleted file mode 100644 index 846f80e64d567a43d8d94845a4e8ab69d1ddfa79..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups', 'users'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.', - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: '62780270-1f67-5dc0-daca-3eb510612e31', - }, - - youAreOwner: { - message: 'Your are the owner.', - code: 'YOU_ARE_OWNER', - id: 'b6d6e0c2-ef8a-9bb8-653d-79f4a3107c69', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - if (me.id === userGroup.userId) { - throw new ApiError(meta.errors.youAreOwner); - } - - await this.userGroupJoiningsRepository.delete({ userGroupId: userGroup.id, userId: me.id }); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts deleted file mode 100644 index 0bc6e8b3fc71b2f2ceb605edf498d209ffc056c1..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['groups', 'account'], - - requireCredential: true, - - kind: 'read:user-groups', - - description: 'List the groups that the authenticated user is the owner of.', - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: {}, - required: [], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - private userGroupEntityService: UserGroupEntityService, - ) { - super(meta, paramDef, async (ps, me) => { - const userGroups = await this.userGroupsRepository.findBy({ - userId: me.id, - }); - - return await Promise.all(userGroups.map(x => this.userGroupEntityService.pack(x))); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts deleted file mode 100644 index 409006b0b0a205724ff3313532fb5c452b87ebc9..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups', 'users'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Removes a specified user from a group. The owner can not be removed.', - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: '4662487c-05b1-4b78-86e5-fd46998aba74', - }, - - noSuchUser: { - message: 'No such user.', - code: 'NO_SUCH_USER', - id: '0b5cc374-3681-41da-861e-8bc1146f7a55', - }, - - isOwner: { - message: 'The user is the owner.', - code: 'IS_OWNER', - id: '1546eed5-4414-4dea-81c1-b0aec4f6d2af', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId', 'userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private getterService: GetterService, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // Fetch the user - const user = await this.getterService.getUser(ps.userId).catch(err => { - if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw err; - }); - - if (user.id === userGroup.userId) { - throw new ApiError(meta.errors.isOwner); - } - - // Pull the user - await this.userGroupJoiningsRepository.delete({ userGroupId: userGroup.id, userId: user.id }); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts deleted file mode 100644 index 2b0f403f337bde837b50c320ef494dcf93e74c4b..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups', 'account'], - - requireCredential: true, - - kind: 'read:user-groups', - - description: 'Show the properties of a group.', - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: 'ea04751e-9b7e-487b-a509-330fb6bd6b9b', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private userGroupEntityService: UserGroupEntityService, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userId: me.id, - userGroupId: userGroup.id, - }); - - if (joining == null && userGroup.userId !== me.id) { - throw new ApiError(meta.errors.noSuchGroup); - } - - return await this.userGroupEntityService.pack(userGroup); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts deleted file mode 100644 index 3130d98ed140736154a44b3d6f0d1716e372a0a4..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { GetterService } from '@/server/api/GetterService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups', 'users'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Transfer ownership of a group from the authenticated user to another user.', - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: '8e31d36b-2f88-4ccd-a438-e2d78a9162db', - }, - - noSuchUser: { - message: 'No such user.', - code: 'NO_SUCH_USER', - id: '711f7ebb-bbb9-4dfa-b540-b27809fed5e9', - }, - - noSuchGroupMember: { - message: 'No such group member.', - code: 'NO_SUCH_GROUP_MEMBER', - id: 'd31bebee-196d-42c2-9a3e-9474d4be6cc4', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['groupId', 'userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - private userGroupEntityService: UserGroupEntityService, - private getterService: GetterService, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // Fetch the user - const user = await this.getterService.getUser(ps.userId).catch(err => { - if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw err; - }); - - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userGroupId: userGroup.id, - userId: user.id, - }); - - if (joining == null) { - throw new ApiError(meta.errors.noSuchGroupMember); - } - - await this.userGroupsRepository.update(userGroup.id, { - userId: ps.userId, - }); - - return await this.userGroupEntityService.pack(userGroup.id); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts deleted file mode 100644 index 5af849de14e97a3658d16108432a2515500e5457..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupsRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['groups'], - - requireCredential: true, - - kind: 'write:user-groups', - - description: 'Update the properties of a group.', - - res: { - type: 'object', - optional: false, nullable: false, - ref: 'UserGroup', - }, - - errors: { - noSuchGroup: { - message: 'No such group.', - code: 'NO_SUCH_GROUP', - id: '9081cda3-7a9e-4fac-a6ce-908d70f282f6', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - groupId: { type: 'string', format: 'misskey:id' }, - name: { type: 'string', minLength: 1, maxLength: 100 }, - }, - required: ['groupId', 'name'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - constructor( - @Inject(DI.userGroupsRepository) - private userGroupsRepository: UserGroupsRepository, - - private userGroupEntityService: UserGroupEntityService, - ) { - super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await this.userGroupsRepository.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - await this.userGroupsRepository.update(userGroup.id, { - name: ps.name, - }); - - return await this.userGroupEntityService.pack(userGroup.id); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index b176e6c65dcb0e4da9ac98f015078efcca32acee..1cefcf2707ea9c5b90cee152119497e39c90fb6e 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; -import { USER_ACTIVE_THRESHOLD } from '@/const.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -37,13 +37,13 @@ export const paramDef = { properties: { username: { type: 'string', nullable: true }, }, - required: ['username'] + required: ['username'], }, { properties: { host: { type: 'string', nullable: true }, }, - required: ['host'] + required: ['host'], }, ], } as const; @@ -54,6 +54,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( + @Inject(DI.config) + private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -63,79 +66,76 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30æ—¥ - - if (ps.host) { - const q = this.usersRepository.createQueryBuilder('user') - .where('user.isSuspended = FALSE') - .andWhere('user.host LIKE :host', { host: sqlLikeEscape(ps.host.toLowerCase()) + '%' }); - + const setUsernameAndHostQuery = (query = this.usersRepository.createQueryBuilder('user')) => { if (ps.username) { - q.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }); + query.andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }); } - q.andWhere('user.updatedAt IS NOT NULL'); - q.orderBy('user.updatedAt', 'DESC'); + if (ps.host) { + if (ps.host === this.config.hostname || ps.host === '.') { + query.andWhere('user.host IS NULL'); + } else { + query.andWhere('user.host LIKE :host', { + host: sqlLikeEscape(ps.host.toLowerCase()) + '%', + }); + } + } - const users = await q.take(ps.limit).getMany(); + return query; + }; - return await this.userEntityService.packMany(users, me, { detail: ps.detail }); - } else if (ps.username) { - let users: User[] = []; + const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30æ—¥ - if (me) { - const followingQuery = this.followingsRepository.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); + let users: User[] = []; - const query = this.usersRepository.createQueryBuilder('user') - .where(`user.id IN (${ followingQuery.getQuery() })`) - .andWhere('user.id != :meId', { meId: me.id }) - .andWhere('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })); + if (me) { + const followingQuery = this.followingsRepository.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: me.id }); - query.setParameters(followingQuery.getParameters()); + const query = setUsernameAndHostQuery() + .andWhere(`user.id IN (${ followingQuery.getQuery() })`) + .andWhere('user.id != :meId', { meId: me.id }) + .andWhere('user.isSuspended = FALSE') + .andWhere(new Brackets(qb => { qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })); - users = await query - .orderBy('user.usernameLower', 'ASC') - .take(ps.limit) - .getMany(); + query.setParameters(followingQuery.getParameters()); - if (users.length < ps.limit) { - const otherQuery = await this.usersRepository.createQueryBuilder('user') - .where(`user.id NOT IN (${ followingQuery.getQuery() })`) - .andWhere('user.id != :meId', { meId: me.id }) - .andWhere('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }) - .andWhere('user.updatedAt IS NOT NULL'); + users = await query + .orderBy('user.usernameLower', 'ASC') + .take(ps.limit) + .getMany(); - otherQuery.setParameters(followingQuery.getParameters()); + if (users.length < ps.limit) { + const otherQuery = setUsernameAndHostQuery() + .andWhere(`user.id NOT IN (${ followingQuery.getQuery() })`) + .andWhere('user.isSuspended = FALSE') + .andWhere('user.updatedAt IS NOT NULL'); - const otherUsers = await otherQuery - .orderBy('user.updatedAt', 'DESC') - .take(ps.limit - users.length) - .getMany(); + otherQuery.setParameters(followingQuery.getParameters()); - users = users.concat(otherUsers); - } - } else { - users = await this.usersRepository.createQueryBuilder('user') - .where('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.username.toLowerCase()) + '%' }) - .andWhere('user.updatedAt IS NOT NULL') + const otherUsers = await otherQuery .orderBy('user.updatedAt', 'DESC') .take(ps.limit - users.length) .getMany(); - } - return await this.userEntityService.packMany(users, me, { detail: !!ps.detail }); + users = users.concat(otherUsers); + } + } else { + const query = setUsernameAndHostQuery() + .andWhere('user.isSuspended = FALSE') + .andWhere('user.updatedAt IS NOT NULL'); + + users = await query + .orderBy('user.updatedAt', 'DESC') + .take(ps.limit - users.length) + .getMany(); } - return []; + return await this.userEntityService.packMany(users, me, { detail: !!ps.detail }); }); } } diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 198fc190d4e670a40315211f703fdcb4cd69b81a..f9ef8218c140ceea224f6ab1cb10135145f0abce 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -1,5 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; +import { Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; import { LocalTimelineChannelService } from './channels/local-timeline.js'; import { HomeTimelineChannelService } from './channels/home-timeline.js'; @@ -11,11 +11,8 @@ import { ServerStatsChannelService } from './channels/server-stats.js'; import { QueueStatsChannelService } from './channels/queue-stats.js'; import { UserListChannelService } from './channels/user-list.js'; import { AntennaChannelService } from './channels/antenna.js'; -import { MessagingChannelService } from './channels/messaging.js'; -import { MessagingIndexChannelService } from './channels/messaging-index.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ChannelsService { @@ -29,8 +26,6 @@ export class ChannelsService { private hashtagChannelService: HashtagChannelService, private antennaChannelService: AntennaChannelService, private channelChannelService: ChannelChannelService, - private messagingChannelService: MessagingChannelService, - private messagingIndexChannelService: MessagingIndexChannelService, private driveChannelService: DriveChannelService, private serverStatsChannelService: ServerStatsChannelService, private queueStatsChannelService: QueueStatsChannelService, @@ -50,8 +45,6 @@ export class ChannelsService { case 'hashtag': return this.hashtagChannelService; case 'antenna': return this.antennaChannelService; case 'channel': return this.channelChannelService; - case 'messaging': return this.messagingChannelService; - case 'messagingIndex': return this.messagingIndexChannelService; case 'drive': return this.driveChannelService; case 'serverStats': return this.serverStatsChannelService; case 'queueStats': return this.queueStatsChannelService; diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 210e016a7e46f5891f735c65fc2e75395cd422bd..157fcd6aa3c10f23a8fbab6dc018d6320d605f45 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 44beef2da25ad2d4142acd890d66aaa3b948bf1f..18604d94f09c75fa59f54fea3694d51c4209f8ac 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -1,5 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { isUserRelated } from '@/misc/is-user-related.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 5ba84e43c45440bf40640d6f611c67c815b6bad6..f5ef1d110207d07f94e055c6911c6895799586e8 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -1,32 +1,24 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, UsersRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { isUserRelated } from '@/misc/is-user-related.js'; -import type { User } from '@/models/entities/User.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; -import type { StreamMessages } from '../types.js'; class ChannelChannel extends Channel { public readonly chName = 'channel'; public static shouldShare = false; public static requireCredential = false; private channelId: string; - private typers: Record<User['id'], Date> = {}; - private emitTypersIntervalId: ReturnType<typeof setInterval>; constructor( private noteEntityService: NoteEntityService, - private userEntityService: UserEntityService, id: string, connection: Channel['connection'], ) { super(id, connection); //this.onNote = this.onNote.bind(this); - //this.emitTypers = this.emitTypers.bind(this); } @bindThis @@ -35,8 +27,6 @@ class ChannelChannel extends Channel { // Subscribe stream this.subscriber.on('notesStream', this.onNote); - this.subscriber.on(`channelStream:${this.channelId}`, this.onEvent); - this.emitTypersIntervalId = setInterval(this.emitTypers, 5000); } @bindThis @@ -66,42 +56,10 @@ class ChannelChannel extends Channel { this.send('note', note); } - @bindThis - private onEvent(data: StreamMessages['channel']['payload']) { - if (data.type === 'typing') { - const id = data.body; - const begin = this.typers[id] == null; - this.typers[id] = new Date(); - if (begin) { - this.emitTypers(); - } - } - } - - @bindThis - private async emitTypers() { - const now = new Date(); - - // Remove not typing users - for (const [userId, date] of Object.entries(this.typers)) { - if (now.getTime() - date.getTime() > 5000) delete this.typers[userId]; - } - - const users = await this.userEntityService.packMany(Object.keys(this.typers), null, { detail: false }); - - this.send({ - type: 'typers', - body: users, - }); - } - @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); - this.subscriber.off(`channelStream:${this.channelId}`, this.onEvent); - - clearInterval(this.emitTypersIntervalId); } } @@ -112,7 +70,6 @@ export class ChannelChannelService { constructor( private noteEntityService: NoteEntityService, - private userEntityService: UserEntityService, ) { } @@ -120,7 +77,6 @@ export class ChannelChannelService { public create(id: string, connection: Channel['connection']): ChannelChannel { return new ChannelChannel( this.noteEntityService, - this.userEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index cfcb125b6bef6e4bd70941d189c6e77f492f9e63..52bb29fabeda6cf6fb014b74566e76d7b485e5a5 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 43d8907fc9d7ab28472eb45402d0f2a57c11af03..b8c0076ed9606c82de807b16450d26bb55425654 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -1,5 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import { isUserRelated } from '@/misc/is-user-related.js'; diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 073b737079dd45a1e0b44aed6ab5aebcac6c5061..00f8d8ecd2da704efdd579166080a057bd9fb506 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -1,5 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 5707ddd8216ef6701170c16477c7fa86617f6d06..04a9f296869f5c882fa0625b8d1a074a5de0af00 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -1,5 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 340f677815e907ae902674ef367e5f0522df6864..ab52aabb30b1f707ad82f76a91c530fdbe42db6b 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,10 +1,8 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import type { Packed } from '@/misc/schema.js'; -import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index ea29e30d63c2f9e5f0bef0882401aa67fcd75051..d8532c477b09fba9cab32c5a5536abd81ec3b132 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -1,5 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 42f255b8fe77a7274e3e4e93f267d39de50663d9..4dd16b530aabfcb41a6adb0e8ddbf1ccfe4d61d5 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -1,5 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; +import { Injectable } from '@nestjs/common'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; diff --git a/packages/backend/src/server/api/stream/channels/messaging-index.ts b/packages/backend/src/server/api/stream/channels/messaging-index.ts deleted file mode 100644 index 66cb79f7a768a7ed00e5a43b1d0e93ea79851adf..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/stream/channels/messaging-index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { bindThis } from '@/decorators.js'; -import Channel from '../channel.js'; - -class MessagingIndexChannel extends Channel { - public readonly chName = 'messagingIndex'; - public static shouldShare = true; - public static requireCredential = true; - - @bindThis - public async init(params: any) { - // Subscribe messaging index stream - this.subscriber.on(`messagingIndexStream:${this.user!.id}`, data => { - this.send(data); - }); - } -} - -@Injectable() -export class MessagingIndexChannelService { - public readonly shouldShare = MessagingIndexChannel.shouldShare; - public readonly requireCredential = MessagingIndexChannel.requireCredential; - - constructor( - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): MessagingIndexChannel { - return new MessagingIndexChannel( - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts deleted file mode 100644 index 92af6b591caa7e2a76f31191b1ac2892870de382..0000000000000000000000000000000000000000 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupJoiningsRepository, UsersRepository, MessagingMessagesRepository } from '@/models/index.js'; -import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { bindThis } from '@/decorators.js'; -import Channel from '../channel.js'; -import type { StreamMessages } from '../types.js'; - -class MessagingChannel extends Channel { - public readonly chName = 'messaging'; - public static shouldShare = false; - public static requireCredential = true; - - private otherpartyId: string | null; - private otherparty: User | null; - private groupId: string | null; - private subCh: `messagingStream:${User['id']}-${User['id']}` | `messagingStream:${UserGroup['id']}`; - private typers: Record<User['id'], Date> = {}; - private emitTypersIntervalId: ReturnType<typeof setInterval>; - - constructor( - private usersRepository: UsersRepository, - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - private messagingMessagesRepository: MessagingMessagesRepository, - private userEntityService: UserEntityService, - private messagingService: MessagingService, - - id: string, - connection: Channel['connection'], - ) { - super(id, connection); - //this.onEvent = this.onEvent.bind(this); - //this.onMessage = this.onMessage.bind(this); - //this.emitTypers = this.emitTypers.bind(this); - } - - @bindThis - public async init(params: any) { - this.otherpartyId = params.otherparty; - this.otherparty = this.otherpartyId ? await this.usersRepository.findOneByOrFail({ id: this.otherpartyId }) : null; - this.groupId = params.group; - - // Check joining - if (this.groupId) { - const joining = await this.userGroupJoiningsRepository.findOneBy({ - userId: this.user!.id, - userGroupId: this.groupId, - }); - - if (joining == null) { - return; - } - } - - this.emitTypersIntervalId = setInterval(this.emitTypers, 5000); - - this.subCh = this.otherpartyId - ? `messagingStream:${this.user!.id}-${this.otherpartyId}` - : `messagingStream:${this.groupId}`; - - // Subscribe messaging stream - this.subscriber.on(this.subCh, this.onEvent); - } - - @bindThis - private onEvent(data: StreamMessages['messaging']['payload'] | StreamMessages['groupMessaging']['payload']) { - if (data.type === 'typing') { - const id = data.body; - const begin = this.typers[id] == null; - this.typers[id] = new Date(); - if (begin) { - this.emitTypers(); - } - } else { - this.send(data); - } - } - - @bindThis - public onMessage(type: string, body: any) { - switch (type) { - case 'read': - if (this.otherpartyId) { - this.messagingService.readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]); - - // リモートユーザーã‹ã‚‰ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã ã£ãŸã‚‰æ—¢èªé…ä¿¡ - if (this.userEntityService.isLocalUser(this.user!) && this.userEntityService.isRemoteUser(this.otherparty!)) { - this.messagingMessagesRepository.findOneBy({ id: body.id }).then(message => { - if (message) this.messagingService.deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message); - }); - } - } else if (this.groupId) { - this.messagingService.readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]); - } - break; - } - } - - @bindThis - private async emitTypers() { - const now = new Date(); - - // Remove not typing users - for (const [userId, date] of Object.entries(this.typers)) { - if (now.getTime() - date.getTime() > 5000) delete this.typers[userId]; - } - - const users = await this.userEntityService.packMany(Object.keys(this.typers), null, { detail: false }); - - this.send({ - type: 'typers', - body: users, - }); - } - - @bindThis - public dispose() { - this.subscriber.off(this.subCh, this.onEvent); - - clearInterval(this.emitTypersIntervalId); - } -} - -@Injectable() -export class MessagingChannelService { - public readonly shouldShare = MessagingChannel.shouldShare; - public readonly requireCredential = MessagingChannel.requireCredential; - - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userGroupJoiningsRepository) - private userGroupJoiningsRepository: UserGroupJoiningsRepository, - - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - private userEntityService: UserEntityService, - private messagingService: MessagingService, - ) { - } - - @bindThis - public create(id: string, connection: Channel['connection']): MessagingChannel { - return new MessagingChannel( - this.usersRepository, - this.userGroupJoiningsRepository, - this.messagingMessagesRepository, - this.userEntityService, - this.messagingService, - id, - connection, - ); - } -} diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts index c77391610343c241f83b55c4c6e0f0e1f3bf5dd7..7f48c54999647289f2d7fdbe68c8362505c48895 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -1,5 +1,5 @@ import Xev from 'xev'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts index 492912dbe65f1bf3d0c15fdba57aacd1a74f082e..9eae0cf2d360d1c4465df84e1e20c4a71ed7bc7d 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -1,5 +1,5 @@ import Xev from 'xev'; -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 16af32868cb84b13a3936c971e451486e2ac3a41..7254d0a6d47f3e2ae7ae575d6a5891377b31d348 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoiningsRepository, UserListsRepository, NotesRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 6763953f9d0ce59052390f3143ea5808e516b9e4..d3056aca57e96ad896c85b63d1b69fa11451c946 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -3,7 +3,6 @@ import type { Channel as ChannelModel } from '@/models/entities/Channel.js'; import type { FollowingsRepository, MutingsRepository, UserProfilesRepository, ChannelFollowingsRepository, BlockingsRepository } from '@/models/index.js'; import type { AccessToken } from '@/models/entities/AccessToken.js'; import type { UserProfile } from '@/models/entities/UserProfile.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { Packed } from '@/misc/schema.js'; import type { GlobalEventService } from '@/core/GlobalEventService.js'; import type { NoteReadService } from '@/core/NoteReadService.js'; @@ -147,12 +146,6 @@ export default class Connection { case 'disconnect': this.onChannelDisconnectRequested(body); break; case 'channel': this.onChannelMessageRequested(body); break; case 'ch': this.onChannelMessageRequested(body); break; // alias - - // 個々ã®ãƒãƒ£ãƒ³ãƒãƒ«ã§ã¯ãªãルートレベルã§ã“れらã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’å—ã‘å–ã‚‹ç†ç”±ã¯ã€ - // クライアントã®äº‹æƒ…を考慮ã—ãŸã¨ãã€å…¥åŠ›ãƒ•ã‚©ãƒ¼ãƒ ã¯ãƒŽãƒ¼ãƒˆãƒãƒ£ãƒ³ãƒãƒ«ã‚„メッセージã®ãƒ¡ã‚¤ãƒ³ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã¨ã¯åˆ¥ - // ãªã“ã¨ã‚‚ã‚ã‚‹ãŸã‚ã€ãれらã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆãŒãã‚Œãžã‚Œå„ãƒãƒ£ãƒ³ãƒãƒ«ã«æŽ¥ç¶šã™ã‚‹ã‚ˆã†ã«ã™ã‚‹ã®ã¯é¢å€’ãªãŸã‚。 - case 'typingOnChannel': this.typingOnChannel(body.channel); break; - case 'typingOnMessaging': this.typingOnMessaging(body); break; } } @@ -325,24 +318,6 @@ export default class Connection { } } - @bindThis - private typingOnChannel(channel: ChannelModel['id']) { - if (this.user) { - this.globalEventService.publishChannelStream(channel, 'typing', this.user.id); - } - } - - @bindThis - private typingOnMessaging(param: { partner?: User['id']; group?: UserGroup['id']; }) { - if (this.user) { - if (param.partner) { - this.globalEventService.publishMessagingStream(param.partner, this.user.id, 'typing', this.user.id); - } else if (param.group) { - this.globalEventService.publishGroupMessagingStream(param.group, 'typing', this.user.id); - } - } - } - @bindThis private async updateFollowing() { const followings = await this.followingsRepository.find({ diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 8bb4147b43e1acf8c94d44fbd56125efeaf0bc8e..9287952cb6d9340356322c1595530e52337f7f5b 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -6,15 +6,13 @@ import type { Antenna } from '@/models/entities/Antenna.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import type { UserList } from '@/models/entities/UserList.js'; -import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; -import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; import type { Signin } from '@/models/entities/Signin.js'; import type { Page } from '@/models/entities/Page.js'; import type { Packed } from '@/misc/schema.js'; import type { Webhook } from '@/models/entities/Webhook.js'; import type { Meta } from '@/models/entities/Meta.js'; -import { Following, Role, RoleAssignment } from '@/models'; +import { Role, RoleAssignment } from '@/models'; import type Emitter from 'strict-event-emitter-types'; import type { EventEmitter } from 'events'; @@ -44,10 +42,10 @@ export interface InternalStreamTypes { export interface BroadcastTypes { emojiAdded: { - emoji: Packed<'Emoji'>; + emoji: Packed<'EmojiDetailed'>; }; emojiUpdated: { - emojis: Packed<'Emoji'>[]; + emojis: Packed<'EmojiDetailed'>[]; }; emojiDeleted: { emojis: { @@ -96,9 +94,6 @@ export interface MainStreamTypes { readAllUnreadMentions: undefined; unreadSpecifiedNote: Note['id']; readAllUnreadSpecifiedNotes: undefined; - readAllMessagingMessages: undefined; - messagingMessage: Packed<'MessagingMessage'>; - unreadMessagingMessage: Packed<'MessagingMessage'>; readAllAntennas: undefined; unreadAntenna: Antenna; readAllAnnouncements: undefined; @@ -153,10 +148,6 @@ type NoteStreamEventTypes = { }; }; -export interface ChannelStreamTypes { - typing: User['id']; -} - export interface UserListStreamTypes { userAdded: Packed<'User'>; userRemoved: Packed<'User'>; @@ -166,28 +157,6 @@ export interface AntennaStreamTypes { note: Note; } -export interface MessagingStreamTypes { - read: MessagingMessage['id'][]; - typing: User['id']; - message: Packed<'MessagingMessage'>; - deleted: MessagingMessage['id']; -} - -export interface GroupMessagingStreamTypes { - read: { - ids: MessagingMessage['id'][]; - userId: User['id']; - }; - typing: User['id']; - message: Packed<'MessagingMessage'>; - deleted: MessagingMessage['id']; -} - -export interface MessagingIndexStreamTypes { - read: MessagingMessage['id'][]; - message: Packed<'MessagingMessage'>; -} - export interface AdminStreamTypes { newAbuseUserReport: { id: AbuseUserReport['id']; @@ -242,10 +211,6 @@ export type StreamMessages = { name: `noteStream:${Note['id']}`; payload: EventUnionFromDictionary<SerializedAll<NoteStreamEventTypes>>; }; - channel: { - name: `channelStream:${Channel['id']}`; - payload: EventUnionFromDictionary<SerializedAll<ChannelStreamTypes>>; - }; userList: { name: `userListStream:${UserList['id']}`; payload: EventUnionFromDictionary<SerializedAll<UserListStreamTypes>>; @@ -254,18 +219,6 @@ export type StreamMessages = { name: `antennaStream:${Antenna['id']}`; payload: EventUnionFromDictionary<SerializedAll<AntennaStreamTypes>>; }; - messaging: { - name: `messagingStream:${User['id']}-${User['id']}`; - payload: EventUnionFromDictionary<SerializedAll<MessagingStreamTypes>>; - }; - groupMessaging: { - name: `messagingStream:${UserGroup['id']}`; - payload: EventUnionFromDictionary<SerializedAll<GroupMessagingStreamTypes>>; - }; - messagingIndex: { - name: `messagingIndexStream:${User['id']}`; - payload: EventUnionFromDictionary<SerializedAll<MessagingIndexStreamTypes>>; - }; admin: { name: `adminStream:${User['id']}`; payload: EventUnionFromDictionary<SerializedAll<AdminStreamTypes>>; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index c69ee33ea36905d8c78d2b7e0381317574859742..98cdd31206414962bc4375b456d8d4deb6ef8a20 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -25,7 +25,7 @@ import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; -import type { ChannelsRepository, ClipsRepository, EmojisRepository, FlashsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 57461b7a33623a7c574dd15b7ee1078fd3d63706..2ce7293a529a2883decdbddcf09631d838d17b37 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import summaly from 'summaly'; +import { summaly } from 'summaly'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -30,7 +30,7 @@ export class UrlPreviewService { } @bindThis - private wrap(url?: string): string | null { + private wrap(url?: string | null): string | null { return url != null ? url.match(/^https?:\/\//) ? `${this.config.mediaProxy}/preview.webp?${query({ @@ -64,14 +64,21 @@ export class UrlPreviewService { ? `(Proxy) Getting preview of ${url}@${lang} ...` : `Getting preview of ${url}@${lang} ...`); try { - const summary = meta.summalyProxy ? await this.httpRequestService.getJson<ReturnType<typeof summaly.default>>(`${meta.summalyProxy}?${query({ - url: url, - lang: lang ?? 'ja-JP', - })}`) : await summaly.default(url, { - followRedirects: false, - lang: lang ?? 'ja-JP', - }); - + const summary = meta.summalyProxy ? + await this.httpRequestService.getJson<ReturnType<typeof summaly>>(`${meta.summalyProxy}?${query({ + url: url, + lang: lang ?? 'ja-JP', + })}`) + : + await summaly(url, { + followRedirects: false, + lang: lang ?? 'ja-JP', + agent: { + http: this.httpRequestService.httpAgent, + https: this.httpRequestService.httpsAgent, + }, + }); + this.logger.succ(`Got preview of ${url}: ${summary.title}`); if (summary.url && !(summary.url.startsWith('http://') || summary.url.startsWith('https://'))) { diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 7e9e1933624c32e28126c2c3f0c73a2b94f0a74e..8ac65a021cc2bcb0d8d183dc79fa5394e7d41c5b 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app'] as const; +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/backend/test/_e2e/endpoints.ts b/packages/backend/test/_e2e/endpoints.ts index ea8433dfa2386904878a37ab9cef5dbca7474ab9..aed980d6c8b909d99ad0ec13309c02669e654c88 100644 --- a/packages/backend/test/_e2e/endpoints.ts +++ b/packages/backend/test/_e2e/endpoints.ts @@ -778,63 +778,6 @@ describe('API: Endpoints', () => { })); }); - describe('messaging/messages/create', () => { - test('メッセージをé€ä¿¡ã§ãã‚‹', async () => { - const res = await api('/messaging/messages/create', { - userId: bob.id, - text: 'test' - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.text, 'test'); - })); - - test('自分自身ã«ã¯ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’é€ä¿¡ã§ããªã„', async () => { - const res = await api('/messaging/messages/create', { - userId: alice.id, - text: 'Yo' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - test('å˜åœ¨ã—ãªã„ユーザーã«ã¯ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’é€ä¿¡ã§ããªã„', async () => { - const res = await api('/messaging/messages/create', { - userId: '000000000000000000000000', - text: 'test' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - test('ä¸æ£ãªãƒ¦ãƒ¼ã‚¶ãƒ¼IDã§æ€’られる', async () => { - const res = await api('/messaging/messages/create', { - userId: 'foo', - text: 'test' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - test('テã‚ストãŒç„¡ãã¦æ€’られる', async () => { - const res = await api('/messaging/messages/create', { - userId: bob.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - test('æ–‡å—数オーãƒãƒ¼ã§æ€’られる', async () => { - const res = await api('/messaging/messages/create', { - userId: bob.id, - text: '!'.repeat(1001) - }, alice); - - assert.strictEqual(res.status, 400); - })); - }); - describe('notes/replies', () => { test('自分ã«é–²è¦§æ¨©é™ã®ãªã„投稿ã¯å«ã¾ã‚Œãªã„', async () => { const alicePost = await post(alice, { diff --git a/packages/backend/test/_e2e/fetch-resource.ts b/packages/backend/test/_e2e/fetch-resource.ts index 7ae133496adcb992e7db3cc7ff42d67840621137..b8ba3f247784a36f415198147d86d501cb1394d1 100644 --- a/packages/backend/test/_e2e/fetch-resource.ts +++ b/packages/backend/test/_e2e/fetch-resource.ts @@ -11,7 +11,7 @@ const PREFER_AP = 'application/activity+json, */*'; const PREFER_HTML = 'text/html, */*'; const UNSPECIFIED = '*/*'; -// Response Contet-Type +// Response Content-Type const AP = 'application/activity+json; charset=utf-8'; const JSON = 'application/json; charset=utf-8'; const HTML = 'text/html; charset=utf-8'; diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index 9efed267eaff6a0c52a16209584d6ddf144337ab..6b31e68616afb861204bcdade8ff2a9485014149 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -1,5 +1,16 @@ -import Resolver from '../../src/activitypub/resolver.js'; -import { IObject } from '../../src/activitypub/type.js'; +import type { Config } from '@/config.js'; +import type { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; +import type { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import type { ApRequestService } from '@/core/activitypub/ApRequestService.js'; +import { Resolver } from '@/core/activitypub/ApResolverService.js'; +import type { IObject } from '@/core/activitypub/type.js'; +import type { HttpRequestService } from '@/core/HttpRequestService.js'; +import type { InstanceActorService } from '@/core/InstanceActorService.js'; +import type { LoggerService } from '@/core/LoggerService.js'; +import type { MetaService } from '@/core/MetaService.js'; +import type { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; +import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js'; type MockResponse = { type: string; @@ -8,6 +19,25 @@ type MockResponse = { export class MockResolver extends Resolver { private _rs = new Map<string, MockResponse>(); + + constructor(loggerService: LoggerService) { + super( + {} as Config, + {} as UsersRepository, + {} as NotesRepository, + {} as PollsRepository, + {} as NoteReactionsRepository, + {} as UtilityService, + {} as InstanceActorService, + {} as MetaService, + {} as ApRequestService, + {} as HttpRequestService, + {} as ApRendererService, + {} as ApDbResolverService, + loggerService, + ); + } + public async _register(uri: string, content: string | Record<string, any>, type = 'application/activity+json') { this._rs.set(uri, { type, diff --git a/packages/backend/test/tests/mfm.ts b/packages/backend/test/tests/mfm.ts deleted file mode 100644 index 884f39d7fb04bc357de5d03ab7e36763da959ace..0000000000000000000000000000000000000000 --- a/packages/backend/test/tests/mfm.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as assert from 'assert'; -import * as mfm from 'mfm-js'; - -import { toHtml } from '../../src/mfm/to-html.js'; -import { fromHtml } from '../../src/mfm/from-html.js'; - -describe('toHtml', () => { - test('br', () => { - const input = 'foo\nbar\nbaz'; - const output = '<p><span>foo<br>bar<br>baz</span></p>'; - assert.equal(toHtml(mfm.parse(input)), output); - }); - - test('br alt', () => { - const input = 'foo\r\nbar\rbaz'; - const output = '<p><span>foo<br>bar<br>baz</span></p>'; - assert.equal(toHtml(mfm.parse(input)), output); - }); -}); - -describe('fromHtml', () => { - test('p', () => { - assert.deepStrictEqual(fromHtml('<p>a</p><p>b</p>'), 'a\n\nb'); - }); - - test('block element', () => { - assert.deepStrictEqual(fromHtml('<div>a</div><div>b</div>'), 'a\nb'); - }); - - test('inline element', () => { - assert.deepStrictEqual(fromHtml('<ul><li>a</li><li>b</li></ul>'), 'a\nb'); - }); - - test('block code', () => { - assert.deepStrictEqual(fromHtml('<pre><code>a\nb</code></pre>'), '```\na\nb\n```'); - }); - - test('inline code', () => { - assert.deepStrictEqual(fromHtml('<code>a</code>'), '`a`'); - }); - - test('quote', () => { - assert.deepStrictEqual(fromHtml('<blockquote>a\nb</blockquote>'), '> a\n> b'); - }); - - test('br', () => { - assert.deepStrictEqual(fromHtml('<p>abc<br><br/>d</p>'), 'abc\n\nd'); - }); - - test('link with different text', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/b">c</a> d</p>'), 'a [c](https://example.com/b) d'); - }); - - test('link with different text, but not encoded', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/ä">c</a> d</p>'), 'a [c](<https://example.com/ä>) d'); - }); - - test('link with same text', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/b">https://example.com/b</a> d</p>'), 'a https://example.com/b d'); - }); - - test('link with same text, but not encoded', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/ä">https://example.com/ä</a> d</p>'), 'a <https://example.com/ä> d'); - }); - - test('link with no url', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="b">c</a> d</p>'), 'a [c](b) d'); - }); - - test('link without href', () => { - assert.deepStrictEqual(fromHtml('<p>a <a>c</a> d</p>'), 'a c d'); - }); - - test('link without text', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/b"></a> d</p>'), 'a https://example.com/b d'); - }); - - test('link without both', () => { - assert.deepStrictEqual(fromHtml('<p>a <a></a> d</p>'), 'a d'); - }); - - test('mention', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/@user" class="u-url mention">@user</a> d</p>'), 'a @user@example.com d'); - }); - - test('hashtag', () => { - assert.deepStrictEqual(fromHtml('<p>a <a href="https://example.com/tags/a">#a</a> d</p>', ['#a']), 'a #a d'); - }); -}); diff --git a/packages/backend/test/tests/reaction-lib.ts b/packages/backend/test/tests/reaction-lib.ts deleted file mode 100644 index 2e767f7697877291246c83958c36ca737f6424ef..0000000000000000000000000000000000000000 --- a/packages/backend/test/tests/reaction-lib.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* -import * as assert from 'assert'; - -import { toDbReaction } from '../src/misc/reaction-lib.js'; - -describe('toDbReaction', async () => { - test('æ—¢å˜ã®æ–‡å—列リアクションã¯ãã®ã¾ã¾', async () => { - assert.strictEqual(await toDbReaction('like'), 'like'); - }); - - test('Unicodeプリンã¯å¯¿å¸åŒ–ä¸èƒ½ã¨ã™ã‚‹ãŸã‚æ–‡å—列化ã—ãªã„', async () => { - assert.strictEqual(await toDbReaction('ðŸ®'), 'ðŸ®'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ like', async () => { - assert.strictEqual(await toDbReaction('ðŸ‘'), 'like'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ love', async () => { - assert.strictEqual(await toDbReaction('â¤ï¸'), 'love'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ love 異体å—セレクタãªã—', async () => { - assert.strictEqual(await toDbReaction('â¤'), 'love'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ laugh', async () => { - assert.strictEqual(await toDbReaction('😆'), 'laugh'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ hmm', async () => { - assert.strictEqual(await toDbReaction('🤔'), 'hmm'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ surprise', async () => { - assert.strictEqual(await toDbReaction('😮'), 'surprise'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ congrats', async () => { - assert.strictEqual(await toDbReaction('🎉'), 'congrats'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ angry', async () => { - assert.strictEqual(await toDbReaction('💢'), 'angry'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ confused', async () => { - assert.strictEqual(await toDbReaction('😥'), 'confused'); - }); - - test('プリン以外ã®æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯æ–‡å—列化ã™ã‚‹ rip', async () => { - assert.strictEqual(await toDbReaction('😇'), 'rip'); - }); - - test('ãれ以外ã¯Unicodeã®ã¾ã¾', async () => { - assert.strictEqual(await toDbReaction('ðŸ…'), 'ðŸ…'); - }); - - test('異体å—セレクタ除去', async () => { - assert.strictEqual(await toDbReaction('㊗ï¸'), '㊗'); - }); - - test('異体å—セレクタ除去 å¿…è¦ãªã—', async () => { - assert.strictEqual(await toDbReaction('㊗'), '㊗'); - }); - - test('fallback - undefined', async () => { - assert.strictEqual(await toDbReaction(undefined), 'like'); - }); - - test('fallback - null', async () => { - assert.strictEqual(await toDbReaction(null), 'like'); - }); - - test('fallback - empty', async () => { - assert.strictEqual(await toDbReaction(''), 'like'); - }); - - test('fallback - unknown', async () => { - assert.strictEqual(await toDbReaction('unknown'), 'like'); - }); -}); -*/ diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts new file mode 100644 index 0000000000000000000000000000000000000000..5496738778742cc4c00a79c0685d3eeabf380879 --- /dev/null +++ b/packages/backend/test/unit/MfmService.ts @@ -0,0 +1,102 @@ +import * as assert from 'assert'; +import * as mfm from 'mfm-js'; +import { Test } from '@nestjs/testing'; + +import { CoreModule } from '@/core/CoreModule.js'; +import { MfmService } from '@/core/MfmService.js'; +import { GlobalModule } from '@/GlobalModule.js'; + +describe('MfmService', () => { + let mfmService: MfmService; + + beforeAll(async () => { + const app = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule], + }).compile(); + mfmService = app.get<MfmService>(MfmService); + }); + + describe('toHtml', () => { + test('br', () => { + const input = 'foo\nbar\nbaz'; + const output = '<p><span>foo<br>bar<br>baz</span></p>'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); + + test('br alt', () => { + const input = 'foo\r\nbar\rbaz'; + const output = '<p><span>foo<br>bar<br>baz</span></p>'; + assert.equal(mfmService.toHtml(mfm.parse(input)), output); + }); + }); + + describe('fromHtml', () => { + test('p', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a</p><p>b</p>'), 'a\n\nb'); + }); + + test('block element', () => { + assert.deepStrictEqual(mfmService.fromHtml('<div>a</div><div>b</div>'), 'a\nb'); + }); + + test('inline element', () => { + assert.deepStrictEqual(mfmService.fromHtml('<ul><li>a</li><li>b</li></ul>'), 'a\nb'); + }); + + test('block code', () => { + assert.deepStrictEqual(mfmService.fromHtml('<pre><code>a\nb</code></pre>'), '```\na\nb\n```'); + }); + + test('inline code', () => { + assert.deepStrictEqual(mfmService.fromHtml('<code>a</code>'), '`a`'); + }); + + test('quote', () => { + assert.deepStrictEqual(mfmService.fromHtml('<blockquote>a\nb</blockquote>'), '> a\n> b'); + }); + + test('br', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>abc<br><br/>d</p>'), 'abc\n\nd'); + }); + + test('link with different text', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/b">c</a> d</p>'), 'a [c](https://example.com/b) d'); + }); + + test('link with different text, but not encoded', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/ä">c</a> d</p>'), 'a [c](<https://example.com/ä>) d'); + }); + + test('link with same text', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/b">https://example.com/b</a> d</p>'), 'a https://example.com/b d'); + }); + + test('link with same text, but not encoded', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/ä">https://example.com/ä</a> d</p>'), 'a <https://example.com/ä> d'); + }); + + test('link with no url', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="b">c</a> d</p>'), 'a [c](b) d'); + }); + + test('link without href', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a>c</a> d</p>'), 'a c d'); + }); + + test('link without text', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/b"></a> d</p>'), 'a https://example.com/b d'); + }); + + test('link without both', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a></a> d</p>'), 'a d'); + }); + + test('mention', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/@user" class="u-url mention">@user</a> d</p>'), 'a @user@example.com d'); + }); + + test('hashtag', () => { + assert.deepStrictEqual(mfmService.fromHtml('<p>a <a href="https://example.com/tags/a">#a</a> d</p>', ['#a']), 'a #a d'); + }); + }); +}); diff --git a/packages/backend/test/unit/ReactionService.ts b/packages/backend/test/unit/ReactionService.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a20a1e08e15361a9f50c9c3ad21e3e70dce7107 --- /dev/null +++ b/packages/backend/test/unit/ReactionService.ts @@ -0,0 +1,92 @@ +import * as assert from 'assert'; +import { Test } from '@nestjs/testing'; + +import { CoreModule } from '@/core/CoreModule.js'; +import { ReactionService } from '@/core/ReactionService.js'; +import { GlobalModule } from '@/GlobalModule.js'; + +describe('ReactionService', () => { + let reactionService: ReactionService; + + beforeAll(async () => { + const app = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule], + }).compile(); + reactionService = app.get<ReactionService>(ReactionService); + }); + + describe('toDbReaction', () => { + test('絵文å—リアクションã¯ãã®ã¾ã¾', async () => { + assert.strictEqual(await reactionService.toDbReaction('ðŸ‘'), 'ðŸ‘'); + assert.strictEqual(await reactionService.toDbReaction('ðŸ…'), 'ðŸ…'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ pudding', async () => { + assert.strictEqual(await reactionService.toDbReaction('pudding'), 'ðŸ®'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ like', async () => { + assert.strictEqual(await reactionService.toDbReaction('like'), 'ðŸ‘'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ love', async () => { + assert.strictEqual(await reactionService.toDbReaction('love'), 'â¤'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ laugh', async () => { + assert.strictEqual(await reactionService.toDbReaction('laugh'), '😆'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ hmm', async () => { + assert.strictEqual(await reactionService.toDbReaction('hmm'), '🤔'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ surprise', async () => { + assert.strictEqual(await reactionService.toDbReaction('surprise'), '😮'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ congrats', async () => { + assert.strictEqual(await reactionService.toDbReaction('congrats'), '🎉'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ angry', async () => { + assert.strictEqual(await reactionService.toDbReaction('angry'), '💢'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ confused', async () => { + assert.strictEqual(await reactionService.toDbReaction('confused'), '😥'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ rip', async () => { + assert.strictEqual(await reactionService.toDbReaction('rip'), '😇'); + }); + + test('æ—¢å˜ã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯çµµæ–‡å—化ã™ã‚‹ star', async () => { + assert.strictEqual(await reactionService.toDbReaction('star'), 'â'); + }); + + test('異体å—セレクタ除去', async () => { + assert.strictEqual(await reactionService.toDbReaction('㊗ï¸'), '㊗'); + }); + + test('異体å—セレクタ除去 å¿…è¦ãªã—', async () => { + assert.strictEqual(await reactionService.toDbReaction('㊗'), '㊗'); + }); + + test('fallback - undefined', async () => { + assert.strictEqual(await reactionService.toDbReaction(undefined), 'ðŸ‘'); + }); + + test('fallback - null', async () => { + assert.strictEqual(await reactionService.toDbReaction(null), 'ðŸ‘'); + }); + + test('fallback - empty', async () => { + assert.strictEqual(await reactionService.toDbReaction(''), 'ðŸ‘'); + }); + + test('fallback - unknown', async () => { + assert.strictEqual(await reactionService.toDbReaction('unknown'), 'ðŸ‘'); + }); + }); +}); diff --git a/packages/backend/test/tests/activitypub.ts b/packages/backend/test/unit/activitypub.ts similarity index 56% rename from packages/backend/test/tests/activitypub.ts rename to packages/backend/test/unit/activitypub.ts index 19fb5d90d71db5c72575a3ad027f0b5e9e1a43d9..3d0032507e9c28b98da1658e5455a7d75bad5a37 100644 --- a/packages/backend/test/tests/activitypub.ts +++ b/packages/backend/test/unit/activitypub.ts @@ -2,8 +2,39 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import rndstr from 'rndstr'; +import { Test } from '@nestjs/testing'; +import { jest } from '@jest/globals'; + +import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js'; +import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { GlobalModule } from '@/GlobalModule.js'; +import { CoreModule } from '@/core/CoreModule.js'; +import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { MockResolver } from '../misc/mock-resolver.js'; describe('ActivityPub', () => { + let noteService: ApNoteService; + let personService: ApPersonService; + let resolver: MockResolver; + + beforeEach(async () => { + const app = await Test.createTestingModule({ + imports: [GlobalModule, CoreModule], + }).compile(); + + await app.init(); + app.enableShutdownHooks(); + + noteService = app.get<ApNoteService>(ApNoteService); + personService = app.get<ApPersonService>(ApPersonService); + resolver = new MockResolver(await app.resolve<LoggerService>(LoggerService)); + + // Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error + const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService); + jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => {})); + }); + describe('Parse minimum object', () => { const host = 'https://host1.test'; const preferredUsername = `${rndstr('A-Z', 4)}${rndstr('a-z', 4)}`; @@ -28,13 +59,9 @@ describe('ActivityPub', () => { }; test('Minimum Actor', async () => { - const { MockResolver } = await import('../misc/mock-resolver.js'); - const { createPerson } = await import('../../src/activitypub/models/person.js'); - - const resolver = new MockResolver(); resolver._register(actor.id, actor); - const user = await createPerson(actor.id, resolver); + const user = await personService.createPerson(actor.id, resolver); assert.deepStrictEqual(user.uri, actor.id); assert.deepStrictEqual(user.username, actor.preferredUsername); @@ -42,14 +69,10 @@ describe('ActivityPub', () => { }); test('Minimum Note', async () => { - const { MockResolver } = await import('../misc/mock-resolver.js'); - const { createNote } = await import('../../src/activitypub/models/note.js'); - - const resolver = new MockResolver(); resolver._register(actor.id, actor); resolver._register(post.id, post); - const note = await createNote(post.id, resolver, true); + const note = await noteService.createNote(post.id, resolver, true); assert.deepStrictEqual(note?.uri, post.id); assert.deepStrictEqual(note.visibility, 'public'); @@ -75,13 +98,9 @@ describe('ActivityPub', () => { }; test('Actor', async () => { - const { MockResolver } = await import('../misc/mock-resolver.js'); - const { createPerson } = await import('../../src/activitypub/models/person.js'); - - const resolver = new MockResolver(); resolver._register(actor.id, actor); - const user = await createPerson(actor.id, resolver); + const user = await personService.createPerson(actor.id, resolver); assert.deepStrictEqual(user.name, actor.name.substr(0, 128)); }); diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts index 1e9a51bc883c9cb10a7cc0e21bfb7051fc1530cc..5ac4cc18a26bdea8aebd32ff0efef4c6f19be323 100644 --- a/packages/backend/test/unit/chart.ts +++ b/packages/backend/test/unit/chart.ts @@ -19,7 +19,7 @@ import Logger from '@/logger.js'; describe('Chart', () => { const config = loadConfig(); const appLockService = { - getChartInsertLock: jest.fn().mockImplementation(() => Promise.resolve(() => {})), + getChartInsertLock: () => () => Promise.resolve(() => {}), } as unknown as jest.Mocked<AppLockService>; let db: DataSource | undefined; diff --git a/packages/backend/test/tests/extract-mentions.ts b/packages/backend/test/unit/extract-mentions.ts similarity index 81% rename from packages/backend/test/tests/extract-mentions.ts rename to packages/backend/test/unit/extract-mentions.ts index e81d04c2db013e64a068e88babec11576f14e36f..66d32be1c543d066f7bc1c7afd18d7044141137d 100644 --- a/packages/backend/test/tests/extract-mentions.ts +++ b/packages/backend/test/unit/extract-mentions.ts @@ -1,11 +1,11 @@ import * as assert from 'assert'; import { parse } from 'mfm-js'; -import { extractMentions } from '../../src/misc/extract-mentions.js'; +import { extractMentions } from '@/misc/extract-mentions.js'; describe('Extract mentions', () => { test('simple', () => { - const ast = parse('@foo @bar @baz')!; + const ast = parse('@foo @bar @baz'); const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ username: 'foo', @@ -23,7 +23,7 @@ describe('Extract mentions', () => { }); test('nested', () => { - const ast = parse('@foo **@bar** @baz')!; + const ast = parse('@foo **@bar** @baz'); const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ username: 'foo', diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 544b529e943d7d99af0f2dbba63cc542087e9011..6f335a2442f7796ef016de5e9eadd5e71e922ac9 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "allowJs": true, - "noEmitOnError": false, + "noEmitOnError": true, "noImplicitAny": true, "noImplicitReturns": true, "noUnusedParameters": false, diff --git a/packages/frontend/.eslintrc.js b/packages/frontend/.eslintrc.js index 6c3bfb5a6e18947399fde2d7cd5624d5e7468ccf..e8e0e57d2abb7978827e9ab78a8f8a1d11935206 100644 --- a/packages/frontend/.eslintrc.js +++ b/packages/frontend/.eslintrc.js @@ -55,6 +55,7 @@ module.exports = { 'vue/multi-word-component-names': 'warn', 'vue/require-v-for-key': 'warn', 'vue/no-unused-components': 'warn', + 'vue/no-unused-vars': 'warn', 'vue/valid-v-for': 'warn', 'vue/return-in-computed-property': 'warn', 'vue/no-setup-props-destructure': 'warn', diff --git a/packages/frontend/assets/label-red.svg b/packages/frontend/assets/label-red.svg index 45996aa9ce5c00fbad1f5f456e8fb86590bc4de0..c89d3f5f3a4106c660a73b5ff5167ce1cf56940a 100644 Binary files a/packages/frontend/assets/label-red.svg and b/packages/frontend/assets/label-red.svg differ diff --git a/packages/frontend/assets/label.svg b/packages/frontend/assets/label.svg index b1f85f3c075905e8fc884c2159fa3af33c49a929..997335f505dea703797558e16c72f6f860173ad3 100644 Binary files a/packages/frontend/assets/label.svg and b/packages/frontend/assets/label.svg differ diff --git a/packages/frontend/assets/unread.svg b/packages/frontend/assets/unread.svg index 8c3cc9f4758c0be0f6f5a12c2929cfa2204acbf8..8bd4156e51d17c439b6d3852db8c745f474b989f 100644 Binary files a/packages/frontend/assets/unread.svg and b/packages/frontend/assets/unread.svg differ diff --git a/packages/frontend/package.json b/packages/frontend/package.json index bf22f7aaad3a8a3d37bdcbfa9a0a7837ec866d1d..f89a9462820f7f460e349a02e22884ced4bc8068 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -4,7 +4,9 @@ "scripts": { "watch": "vite", "build": "vite build", - "lint": "vue-tsc --noEmit && eslint --quiet \"src/**/*.{ts,vue}\"" + "typecheck": "vue-tsc --noEmit", + "eslint": "eslint --quiet \"src/**/*.{ts,vue}\"", + "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { "@discordapp/twemoji": "14.0.2", @@ -17,11 +19,11 @@ "@vue/compiler-sfc": "3.2.47", "autobind-decorator": "2.4.0", "autosize": "5.0.2", - "blurhash": "2.0.4", + "blurhash": "2.0.5", "broadcast-channel": "4.20.2", "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", "canvas-confetti": "1.6.0", - "chart.js": "4.2.0", + "chart.js": "4.2.1", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", @@ -36,7 +38,7 @@ "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", - "matter-js": "0.18.0", + "matter-js": "0.19.0", "mfm-js": "0.23.3", "misskey-js": "0.0.15", "photoswipe": "5.3.5", @@ -44,13 +46,12 @@ "punycode": "2.3.0", "querystring": "0.2.1", "rndstr": "1.0.0", - "rollup": "3.14.0", + "rollup": "3.17.2", "s-age": "1.1.2", - "sanitize-html": "2.9.0", - "sass": "1.58.0", + "sanitize-html": "2.10.0", + "sass": "1.58.3", "seedrandom": "3.0.5", "strict-event-emitter-types": "2.0.0", - "stringz": "2.1.0", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.149.0", @@ -63,7 +64,7 @@ "uuid": "9.0.0", "vanilla-tilt": "1.8.0", "vue-plyr": "7.0.0", - "vite": "4.1.1", + "vite": "4.1.2", "vue": "3.2.47", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "next" @@ -74,7 +75,7 @@ "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", "@types/matter-js": "0.18.2", - "@types/node": "18.13.0", + "@types/node": "18.14.0", "@types/punycode": "2.1.0", "@types/sanitize-html": "2.8.0", "@types/seedrandom": "3.0.4", @@ -83,16 +84,16 @@ "@types/uuid": "9.0.0", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.51.0", - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/eslint-plugin": "5.52.0", + "@typescript-eslint/parser": "5.52.0", "@vue/runtime-core": "3.2.47", "cross-env": "7.0.3", - "cypress": "12.5.1", - "eslint": "8.33.0", + "cypress": "12.6.0", + "eslint": "8.34.0", "eslint-plugin-import": "2.27.5", "eslint-plugin-vue": "9.9.0", - "start-server-and-test": "1.15.3", + "start-server-and-test": "1.15.4", "vue-eslint-parser": "9.1.0", - "vue-tsc": "1.0.24" + "vue-tsc": "1.1.4" } } diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index 0e18a5a83d288cf4b579216cc4bffa90343d5e95..dee80378e6e57d5b58829573a7699d35a622f670 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -39,7 +39,6 @@ import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; -import { acct, userPage } from '@/filters/user'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { dateString } from '@/filters/date'; diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index a76a1e0f5492a37c7a2d4c57d80e20b39e61a7fe..9f2bf993389f89c3e0b55fb5d1ca1805c7609135 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -43,7 +43,7 @@ const emit = defineEmits<{ }>(); const uiWindow = shallowRef<InstanceType<typeof MkWindow>>(); -const comment = ref(props.initialComment || ''); +const comment = ref(props.initialComment ?? ''); function send() { os.apiWithDialog('users/report-abuse', { diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue index 139e49cc40180609ab68ed9c56e0451995986153..12182026166afa8e558a187b227cfdeb1d180683 100644 --- a/packages/frontend/src/components/MkAnalogClock.vue +++ b/packages/frontend/src/components/MkAnalogClock.vue @@ -73,7 +73,7 @@ </template> <script lang="ts" setup> -import { ref, computed, onMounted, onBeforeUnmount, shallowRef, nextTick } from 'vue'; +import { computed, onMounted, onBeforeUnmount } from 'vue'; import tinycolor from 'tinycolor2'; import { globalEvents } from '@/events.js'; diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index 4f463d73d9fda30a927d2d5f7b0f32d8bacb5b4b..6ade5316c6812d4d8e5ca1495df23f41f0e7d9e2 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -48,7 +48,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, onMounted, onUnmounted, Ref } from 'vue'; +import { Ref } from 'vue'; import * as os from '@/os'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index e523b988b0872144b00f51f5acf77b3b1bd936b3..663c57623d4ab421cd5107322a17f7dc766d78eb 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -43,7 +43,6 @@ import * as os from '@/os'; import { MFM_TAGS } from '@/scripts/mfm-tags'; import { defaultStore } from '@/store'; import { emojilist } from '@/scripts/emojilist'; -import { instance } from '@/instance'; import { i18n } from '@/i18n'; import { miLocalStorage } from '@/local-storage'; import { customEmojis } from '@/custom-emojis'; @@ -210,7 +209,7 @@ function exec() { } } else if (props.type === 'hashtag') { if (!props.q || props.q === '') { - hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') || '[]'); + hashtags.value = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'); fetching.value = false; } else { const cacheKey = `autocomplete:hashtag:${props.q}`; diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index 8db2e54e880d35148d5dfab77232b9a36dc1381b..c72cc2ab1b85b38b8adfe9898ad15dfd6e4bd36b 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -69,7 +69,7 @@ const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown if (loaded) { available.value = true; } else { - (document.getElementById(scriptId.value) || document.head.appendChild(Object.assign(document.createElement('script'), { + (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), { async: true, id: scriptId.value, src: src.value, diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index 57efda44b15482fe299df75331e27a278cad487a..06d5b9949ab06478a9ebf1fa03690dd4f6474174 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -14,7 +14,7 @@ id-denylist violation when setting it. This is causing about 60+ lint issues. As this is part of Chart.js's API it makes sense to disable the check here. */ -import { onMounted, ref, shallowRef, watch, PropType, onUnmounted } from 'vue'; +import { onMounted, ref, shallowRef, watch, PropType } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; import * as os from '@/os'; diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue index b950f2836e924132454181e68972392c4b047196..7bc6194b7a8700dfa7a71093f71aa09cb422c5e7 100644 --- a/packages/frontend/src/components/MkChartLegend.vue +++ b/packages/frontend/src/components/MkChartLegend.vue @@ -8,7 +8,6 @@ </template> <script lang="ts" setup> -import { onMounted, ref, shallowRef, watch, PropType, onUnmounted } from 'vue'; import { Chart, LegendItem } from 'chart.js'; const props = defineProps({ diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 68e0f8185dec4b13a0e5926d67c80b84aa81a5f9..da6439fd2cb64e91118e606404f86ae28f2527dd 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -14,7 +14,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, onMounted, onUnmounted } from 'vue'; +import { computed, onMounted, onUnmounted } from 'vue'; import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; @@ -22,9 +22,6 @@ import * as game from '@/scripts/clicker-game'; import number from '@/filters/number'; import { claimAchievement } from '@/scripts/achievements'; -defineProps<{ -}>(); - const saveData = game.saveData; const cookies = computed(() => saveData.value?.cookies); let cps = $ref(0); diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index 7e4d2016be85242de2c28b7d84dd1182471a81dc..d1b5cc5118949b1f52f555c0bbf387ecf73f6831 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -26,7 +26,7 @@ </template> <script lang="ts" setup> -import { nextTick, onMounted } from 'vue'; +import { onMounted } from 'vue'; import * as misskey from 'misskey-js'; import Cropper from 'cropperjs'; import tinycolor from 'tinycolor2'; diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index e0885f55504ce879111e0b3b409901a9956159bf..7d5579040ae57099cdbb07a03f6d5660c05d7b2a 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -7,7 +7,6 @@ <script lang="ts" setup> import { computed } from 'vue'; -import { length } from 'stringz'; import * as misskey from 'misskey-js'; import { concat } from '@/scripts/array'; import { i18n } from '@/i18n'; @@ -23,7 +22,7 @@ const emit = defineEmits<{ const label = computed(() => { return concat([ - props.note.text ? [i18n.t('_cw.chars', { count: length(props.note.text) })] : [], + props.note.text ? [i18n.t('_cw.chars', { count: props.note.text.length })] : [], props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length })] : [], props.note.poll != null ? [i18n.ts.poll] : [], ] as string[][]).join(' / '); diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 9690353432b6429a59ec925a7c59fd2ac9e0b146..863ea702cde1c6e5c484f7de9ff41b9a557b4e11 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -14,8 +14,12 @@ </div> <header v-if="title" :class="$style.title"><Mfm :text="title"/></header> <div v-if="text" :class="$style.text"><Mfm :text="text"/></div> - <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" @keydown="onInputKeydown"> + <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> <template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template> + <template #caption> + <span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })" /> + <span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })" /> + </template> </MkInput> <MkSelect v-if="select" v-model="selectedValue" autofocus> <template v-if="select.items"> @@ -28,7 +32,7 @@ </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> - <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> + <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> <MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> </div> <div v-if="actions" :class="$style.buttons"> @@ -47,9 +51,12 @@ import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n'; type Input = { - type: HTMLInputElement['type']; + type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; placeholder?: string | null; - default: any | null; + autocomplete?: string; + default: string | number | null; + minLength?: number; + maxLength?: number; }; type Select = { @@ -98,8 +105,28 @@ const emit = defineEmits<{ const modal = shallowRef<InstanceType<typeof MkModal>>(); -const inputValue = ref(props.input?.default || null); -const selectedValue = ref(props.select?.default || null); +const inputValue = ref<string | number | null>(props.input?.default ?? null); +const selectedValue = ref(props.select?.default ?? null); + +let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null); +const okButtonDisabled = $computed<boolean>(() => { + if (props.input) { + if (props.input.minLength) { + if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) { + disabledReason = 'charactersBelow'; + return true; + } + } + if (props.input.maxLength) { + if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) { + disabledReason = 'charactersExceeded'; + return true; + } + } + } + + return false; +}); function done(canceled: boolean, result?) { emit('done', { canceled, result }); diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue index 707444abc9fe5fcd7898049ab5e8bf1ea5f85bc5..9baa90ebfe7da436940b4f7e4439c4ad9bae0d71 100644 --- a/packages/frontend/src/components/MkDonation.vue +++ b/packages/frontend/src/components/MkDonation.vue @@ -31,7 +31,6 @@ </template> <script lang="ts" setup> -import { onMounted, shallowRef } from 'vue'; import MkButton from '@/components/MkButton.vue'; import MkLink from '@/components/MkLink.vue'; import { host } from '@/config'; @@ -51,7 +50,7 @@ function close() { } function neverShow() { - miLocalStorage.setItem('neverShowDonationInfo', 'true') + miLocalStorage.setItem('neverShowDonationInfo', 'true'); close(); } </script> diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index af7175e5cd28b69a8118b1bd958864ef8f3025f9..bfec57d6a04ea4e146b1537f105cbef166bb5a30 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -88,7 +88,7 @@ </template> <script lang="ts" setup> -import { markRaw, nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; +import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from './MkButton.vue'; import XNavFolder from '@/components/MkDrive.navFolder.vue'; diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index c7556ec36e165d797e8806d3615337c4aebe0566..7d280f2f4b6399856ede3b72dfbb6852fa4d2a81 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -95,7 +95,6 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import * as os from '@/os'; import { isTouchUsing } from '@/scripts/touch'; import { deviceKind } from '@/scripts/device-kind'; -import { instance } from '@/instance'; import { i18n } from '@/i18n'; import { defaultStore } from '@/store'; import { customEmojiCategories, customEmojis } from '@/custom-emojis'; diff --git a/packages/frontend/src/components/MkEmojiPickerWindow.vue b/packages/frontend/src/components/MkEmojiPickerWindow.vue index ca7dbccdc86de597f32d4105cbf99aa2ee043387..84970410e92b4e5538e48c2fa946a74b911c344f 100644 --- a/packages/frontend/src/components/MkEmojiPickerWindow.vue +++ b/packages/frontend/src/components/MkEmojiPickerWindow.vue @@ -7,7 +7,7 @@ :front="true" @closed="emit('closed')" > - <MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" as-window @chosen="chosen" :class="$style.picker"/> + <MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" as-window :class="$style.picker" @chosen="chosen"/> </MkWindow> </template> diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue index f340acaf2d709944a665adf4ccfe8db529161136..71a35ae6e8e0c3ac0a47f67f43e036b3b04f643f 100644 --- a/packages/frontend/src/components/MkFileListForAdmin.vue +++ b/packages/frontend/src/components/MkFileListForAdmin.vue @@ -32,12 +32,10 @@ </template> <script lang="ts" setup> -import { computed } from 'vue'; import * as Acct from 'misskey-js/built/acct'; import MkPagination from '@/components/MkPagination.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import bytes from '@/filters/bytes'; -import * as os from '@/os'; import { i18n } from '@/i18n'; import { dateString } from '@/filters/date'; diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue index a96934ddb15e41a1eaa763219be3fceb9113d791..7c9ae155abda37f6b5b6cec2d4f06a5aea9c6ef1 100644 --- a/packages/frontend/src/components/MkFlashPreview.vue +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -15,9 +15,7 @@ <script lang="ts" setup> import { } from 'vue'; -import * as misskey from 'misskey-js'; import { userName } from '@/filters/user'; -import * as os from '@/os'; const props = defineProps<{ //flash: misskey.entities.Flash; diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index a1d7210d7ecbbe63e0b9257a77154bb2d5178571..b97e36cd5f9d533551503867b7f1236b5a95ee7f 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -1,13 +1,20 @@ <template> <div ref="rootEl" :class="[$style.root, { [$style.opened]: opened }]"> <div :class="$style.header" class="_button" @click="toggle"> - <span :class="$style.headerIcon"><slot name="icon"></slot></span> - <span :class="$style.headerText"><slot name="label"></slot></span> - <span :class="$style.headerRight"> + <div :class="$style.headerIcon"><slot name="icon"></slot></div> + <div :class="$style.headerText"> + <div :class="$style.headerTextMain"> + <slot name="label"></slot> + </div> + <div :class="$style.headerTextSub"> + <slot name="caption"></slot> + </div> + </div> + <div :class="$style.headerRight"> <span :class="$style.headerRightText"><slot name="suffix"></slot></span> <i v-if="opened" class="ti ti-chevron-up icon"></i> <i v-else class="ti ti-chevron-down icon"></i> - </span> + </div> </div> <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null }"> <Transition @@ -139,6 +146,17 @@ onMounted(() => { } } +.headerUpper { + display: flex; + align-items: center; +} + +.headerLower { + color: var(--fgTransparentWeak); + font-size: .85em; + padding-left: 4px; +} + .headerIcon { margin-right: 0.75em; flex-shrink: 0; @@ -161,6 +179,15 @@ onMounted(() => { padding-right: 12px; } +.headerTextMain { + +} + +.headerTextSub { + color: var(--fgTransparentWeak); + font-size: .85em; +} + .headerRight { margin-left: auto; opacity: 0.7; diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue index 42f8853bd4da8177b9e488a75f93c05d8105c309..2c5032119f95855bd0db2b5b1e3d1dba12a1a5b0 100644 --- a/packages/frontend/src/components/MkGalleryPostPreview.vue +++ b/packages/frontend/src/components/MkGalleryPostPreview.vue @@ -16,9 +16,7 @@ <script lang="ts" setup> import { } from 'vue'; -import { userName } from '@/filters/user'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; -import * as os from '@/os'; const props = defineProps<{ post: any; diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue index f222fca9a10037b0fba2de4ed46b3e2334bccd8c..e5a39a759b75dbc67ffb71e1b3ea74c2cc27d694 100644 --- a/packages/frontend/src/components/MkHeatmap.vue +++ b/packages/frontend/src/components/MkHeatmap.vue @@ -8,14 +8,11 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; +import { onMounted, nextTick, watch } from 'vue'; import { Chart } from 'chart.js'; -import tinycolor from 'tinycolor2'; -import { MatrixController, MatrixElement } from 'chartjs-chart-matrix'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; -import { chartVLine } from '@/scripts/chart-vline'; import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index e3f68caa9bd3dbd2a8c5f294407a36125d48f85c..3e3d7354c1d6624e3c43ccd1c0aa3987edd41710 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -23,7 +23,7 @@ @input="onInput" > <datalist v-if="datalist" :id="id"> - <option v-for="data in datalist" :value="data"/> + <option v-for="data in datalist" :key="data" :value="data"/> </datalist> <div ref="suffixEl" class="suffix"><slot name="suffix"></slot></div> </div> @@ -34,14 +34,14 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; +import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; import { debounce } from 'throttle-debounce'; import MkButton from '@/components/MkButton.vue'; import { useInterval } from '@/scripts/use-interval'; import { i18n } from '@/i18n'; const props = defineProps<{ - modelValue: string | number; + modelValue: string | number | null; type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; required?: boolean; readonly?: boolean; @@ -49,7 +49,7 @@ const props = defineProps<{ pattern?: string; placeholder?: string; autofocus?: boolean; - autocomplete?: boolean; + autocomplete?: string; spellcheck?: boolean; step?: any; datalist?: string[]; diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index 40c99b60ec9576ddce3c7d342b8d743d32adc98b..80e5cc82709ef9383ee7e0fcc019f114619a8d8c 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -23,11 +23,8 @@ import { } from 'vue'; import MkModal from '@/components/MkModal.vue'; import { navbarItemDef } from '@/navbar'; -import { instanceName } from '@/config'; import { defaultStore } from '@/store'; -import { i18n } from '@/i18n'; import { deviceKind } from '@/scripts/device-kind'; -import * as os from '@/os'; const props = withDefaults(defineProps<{ src?: HTMLElement; diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index 29d15989b76ac479d6238d5c9ff61243ecc5940f..a12bb78e351a1c37896b683ad653cd22466a1fde 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -23,7 +23,6 @@ import XImage from '@/components/MkMediaImage.vue'; import XVideo from '@/components/MkMediaVideo.vue'; import * as os from '@/os'; import { FILE_TYPE_BROWSERSAFE } from '@/const'; -import { defaultStore } from '@/store'; const props = defineProps<{ mediaList: misskey.entities.DriveFile[]; @@ -46,8 +45,8 @@ onMounted(() => { src: media.url, w: media.properties.width, h: media.properties.height, - alt: media.comment || media.name, - comment: media.comment || media.name, + alt: media.comment ?? media.name, + comment: media.comment ?? media.name, }; if (media.properties.orientation != null && media.properties.orientation >= 5) { [item.w, item.h] = [item.h, item.w]; @@ -91,8 +90,8 @@ onMounted(() => { [itemData.w, itemData.h] = [itemData.h, itemData.w]; } itemData.msrc = file.thumbnailUrl; - itemData.alt = file.comment || file.name; - itemData.comment = file.comment || file.name; + itemData.alt = file.comment ?? file.name; + itemData.comment = file.comment ?? file.name; itemData.thumbCropped = true; }); diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue index 0ff8794c5da58ce689fac634de6b31126850204e..cdd9d96b96456ac8f0878b2add0962294ce60052 100644 --- a/packages/frontend/src/components/MkMenu.child.vue +++ b/packages/frontend/src/components/MkMenu.child.vue @@ -5,11 +5,9 @@ </template> <script lang="ts" setup> -import { on } from 'events'; -import { nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; +import { nextTick, onMounted, shallowRef, watch } from 'vue'; import MkMenu from './MkMenu.vue'; import { MenuItem } from '@/types/menu'; -import * as os from '@/os'; const props = defineProps<{ items: MenuItem[]; diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index eee77a9475c785314cfc2e6203fa36f313a70e03..52aba584554d210278f22ff715dc454019718750 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -56,7 +56,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue'; +import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus'; import MkSwitch from '@/components/MkSwitch.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu'; diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue index c64ce163f9844d8b863db6b3c6a8decd8cf5fff5..89050e10f0398bfaf7621b152251a381a2546849 100644 --- a/packages/frontend/src/components/MkMiniChart.vue +++ b/packages/frontend/src/components/MkMiniChart.vue @@ -26,7 +26,7 @@ </template> <script lang="ts" setup> -import { onUnmounted, watch } from 'vue'; +import { watch } from 'vue'; import { v4 as uuid } from 'uuid'; import tinycolor from 'tinycolor2'; import { useInterval } from '@/scripts/use-interval'; diff --git a/packages/frontend/src/components/MkModalPageWindow.vue b/packages/frontend/src/components/MkModalPageWindow.vue index ed892bc007393b613b784dc72b903ca149f5b456..68a3eda3d829d5e28c0770ef93a045d00b4ccc51 100644 --- a/packages/frontend/src/components/MkModalPageWindow.vue +++ b/packages/frontend/src/components/MkModalPageWindow.vue @@ -29,7 +29,7 @@ import { url } from '@/config'; import * as os from '@/os'; import { mainRouter, routes } from '@/router'; import { i18n } from '@/i18n'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { Router } from '@/nirax'; const props = defineProps<{ diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index c9c512c36e834cef23a0148f172501478f4889fe..4986f1b646964b7fae02e22ae85e180dbedb5679 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -9,7 +9,7 @@ > <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> - <!--<div v-if="appearNote._prId_" class="tip"><i class="fas fa-bullhorn"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> + <!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> <!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>--> <div v-if="isRenote" :class="$style.renote"> <MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/> @@ -126,7 +126,7 @@ </template> <script lang="ts" setup> -import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef, Ref, defineAsyncComponent } from 'vue'; +import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent } from 'vue'; import * as mfm from 'mfm-js'; import * as misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; @@ -195,6 +195,8 @@ const isMyRenote = $i && ($i.id === note.userId); const showContent = ref(false); const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null; const isLong = (appearNote.cw == null && appearNote.text != null && ( + (appearNote.text.includes('$[x3')) || + (appearNote.text.includes('$[x4')) || (appearNote.text.split('\n').length > 9) || (appearNote.text.length > 500) || (appearNote.files.length >= 5) || diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 92bdadc562fe4070cc6575f5bb362b51508f3cb7..82e0f3e689c9ed126d2510ce64182ee828c3ab19 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -133,7 +133,7 @@ </template> <script lang="ts" setup> -import { computed, inject, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue'; +import { computed, inject, onMounted, ref, shallowRef } from 'vue'; import * as mfm from 'mfm-js'; import * as misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 0d42e8ffbfde2b5afc8b4e89319cfc2c33cc306e..38bf416ea897dc0e623112637f5a6fc699f6c19e 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -9,7 +9,6 @@ <i v-if="notification.type === 'follow'" class="ti ti-plus"></i> <i v-else-if="notification.type === 'receiveFollowRequest'" class="ti ti-clock"></i> <i v-else-if="notification.type === 'followRequestAccepted'" class="ti ti-check"></i> - <i v-else-if="notification.type === 'groupInvited'" class="ti ti-certificate-2"></i> <i v-else-if="notification.type === 'renote'" class="ti ti-repeat"></i> <i v-else-if="notification.type === 'reply'" class="ti ti-arrow-back-up"></i> <i v-else-if="notification.type === 'mention'" class="ti ti-at"></i> @@ -74,12 +73,6 @@ <button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button> </div> </template> - <template v-else-if="notification.type === 'groupInvited'"> - <span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b></span> - <div v-if="full && !groupInviteDone"> - <button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button> - </div> - </template> <span v-else-if="notification.type === 'app'" :class="$style.text"> <Mfm :text="notification.body" :nowrap="false"/> </span> @@ -145,7 +138,6 @@ onUnmounted(() => { }); const followRequestDone = ref(false); -const groupInviteDone = ref(false); const acceptFollowRequest = () => { followRequestDone.value = true; @@ -157,16 +149,6 @@ const rejectFollowRequest = () => { os.api('following/requests/reject', { userId: props.notification.user.id }); }; -const acceptGroupInvitation = () => { - groupInviteDone.value = true; - os.apiWithDialog('users/groups/invitations/accept', { invitationId: props.notification.invitation.id }); -}; - -const rejectGroupInvitation = () => { - groupInviteDone.value = true; - os.api('users/groups/invitations/reject', { invitationId: props.notification.invitation.id }); -}; - useTooltip(reactionRef, (showing) => { os.popup(XReactionTooltip, { showing, @@ -224,7 +206,7 @@ useTooltip(reactionRef, (showing) => { } } -.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest, .t_groupInvited { +.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest { padding: 3px; background: #36aed2; pointer-events: none; diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue index e30340387267074ad1f5adcf43dde6a25d954a16..2d8d30e337a359dd937b0cf32ac2663525f6b44f 100644 --- a/packages/frontend/src/components/MkNotificationSettingWindow.vue +++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue @@ -54,7 +54,7 @@ const props = withDefaults(defineProps<{ showGlobalToggle: true, }); -let includingTypes = $computed(() => props.includingTypes || []); +let includingTypes = $computed(() => props.includingTypes ?? []); const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index ab5dff8db54adf0830b1cc72d7f9084ca49c400a..37ce7635a3aa038e602c922fcc76e88b09817386 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -17,13 +17,12 @@ </template> <script lang="ts" setup> -import { defineComponent, markRaw, onUnmounted, onMounted, computed, shallowRef } from 'vue'; +import { onUnmounted, onMounted, computed, shallowRef } from 'vue'; import { notificationTypes } from 'misskey-js'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import XNotification from '@/components/MkNotification.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; import XNote from '@/components/MkNote.vue'; -import * as os from '@/os'; import { stream } from '@/stream'; import { $i } from '@/account'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/components/MkNumber.vue b/packages/frontend/src/components/MkNumber.vue index 58279be0b64ec9a14e0c90e45fe4581b02e0f2f8..51fc4d02bbb90a9bd90ec8771d7af5468bc663a8 100644 --- a/packages/frontend/src/components/MkNumber.vue +++ b/packages/frontend/src/components/MkNumber.vue @@ -3,7 +3,7 @@ </template> <script lang="ts" setup> -import { ref, reactive, watch } from 'vue'; +import { reactive, watch } from 'vue'; import gsap from 'gsap'; import number from '@/filters/number'; diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue index 0c7230d7830ff26390039350050ec6a112750ccc..e7fc73bce362eb4a965338b54531cf670b53a4b6 100644 --- a/packages/frontend/src/components/MkObjectView.value.vue +++ b/packages/frontend/src/components/MkObjectView.value.vue @@ -29,7 +29,7 @@ </template> <script lang="ts"> -import { computed, defineComponent, reactive, ref } from 'vue'; +import { defineComponent, reactive } from 'vue'; import number from '@/filters/number'; export default defineComponent({ diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue index 5a834c9800d710252b643701f3150548c95723e5..a806d92b22ecfc20263b11adc1ec3a5616002ac0 100644 --- a/packages/frontend/src/components/MkOmit.vue +++ b/packages/frontend/src/components/MkOmit.vue @@ -8,7 +8,7 @@ </template> <script lang="ts" setup> -import { nextTick, onMounted } from 'vue'; +import { onMounted } from 'vue'; const props = withDefaults(defineProps<{ maxHeight: number; diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue index a78431e2a7035d77ea6347e0bce791ff506ecdf1..4fda6df5e9ce86c933db5ca638c50145cc5e6a23 100644 --- a/packages/frontend/src/components/MkPagePreview.vue +++ b/packages/frontend/src/components/MkPagePreview.vue @@ -18,7 +18,6 @@ import { } from 'vue'; import * as misskey from 'misskey-js'; import { userName } from '@/filters/user'; -import * as os from '@/os'; const props = defineProps<{ page: misskey.entities.Page; diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index d12aafd06d44cc8180fcab3fd87a1e59c32490fb..02ce58451db56bb42b8e90a2e391d1f7b45680f1 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -18,23 +18,22 @@ </template> <div :class="$style.root" :style="{ background: pageMetadata?.value?.bg }" style="container-type: inline-size;"> - <RouterView :router="router"/> + <RouterView :key="reloadCount" :router="router"/> </div> </MkWindow> </template> <script lang="ts" setup> -import { ComputedRef, inject, onMounted, onUnmounted, provide } from 'vue'; +import { ComputedRef, onMounted, onUnmounted, provide } from 'vue'; import RouterView from '@/components/global/RouterView.vue'; import MkWindow from '@/components/MkWindow.vue'; import { popout as _popout } from '@/scripts/popout'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; -import * as os from '@/os'; import { mainRouter, routes } from '@/router'; import { Router } from '@/nirax'; import { i18n } from '@/i18n'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { openingWindowsCount } from '@/os'; import { claimAchievement } from '@/scripts/achievements'; @@ -68,6 +67,10 @@ const buttonsLeft = $computed(() => { }); const buttonsRight = $computed(() => { const buttons = [{ + icon: 'ti ti-reload', + title: i18n.ts.reload, + onClick: reload, + }, { icon: 'ti ti-player-eject', title: i18n.ts.showInPage, onClick: expand, @@ -75,6 +78,7 @@ const buttonsRight = $computed(() => { return buttons; }); +let reloadCount = $ref(0); router.addListener('push', ctx => { history.push({ path: ctx.path, key: ctx.key }); @@ -116,6 +120,10 @@ function back() { router.replace(history[history.length - 1].path, history[history.length - 1].key); } +function reload() { + reloadCount++; +} + function close() { windowEl.close(); } diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 224a42cdc25aa7cb82619086f628676e80e89770..84ba94361ed06646a6fa57987a10553da123d083 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -104,7 +104,7 @@ const { enableInfiniteScroll, } = defaultStore.reactiveState; -const contentEl = $computed(() => props.pagination.pageEl || rootEl); +const contentEl = $computed(() => props.pagination.pageEl ?? rootEl); const scrollableElement = $computed(() => getScrollContainer(contentEl)); // å…ˆé ãŒè¡¨ç¤ºã•ã‚Œã¦ã„ã‚‹ã‹ã©ã†ã‹ã‚’検出 diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue index 33e625a0581864365c12254895a895104c05ebab..fcbd8ad351e22e1b5034a5aa7daabe81d9d30c68 100644 --- a/packages/frontend/src/components/MkPoll.vue +++ b/packages/frontend/src/components/MkPoll.vue @@ -22,7 +22,7 @@ </template> <script lang="ts" setup> -import { computed, onUnmounted, ref, toRef } from 'vue'; +import { computed, ref } from 'vue'; import * as misskey from 'misskey-js'; import { sum } from '@/scripts/array'; import { pleaseLogin } from '@/scripts/please-login'; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index f15906c1c12ae5bf8e978520019800eb92e8fc45..54512aa4d8055afe02724b2a59c595a9d9b8e880 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -73,10 +73,8 @@ import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue'; import * as mfm from 'mfm-js'; import * as misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; -import { length } from 'stringz'; import { toASCII } from 'punycode/'; import * as Acct from 'misskey-js/built/acct'; -import { throttle } from 'throttle-debounce'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; import XNotePreview from '@/components/MkNotePreview.vue'; import XPostFormAttaches from '@/components/MkPostFormAttaches.vue'; @@ -87,7 +85,6 @@ import { extractMentions } from '@/scripts/extract-mentions'; import { formatTimeString } from '@/scripts/format-time-string'; import { Autocomplete } from '@/scripts/autocomplete'; import * as os from '@/os'; -import { stream } from '@/stream'; import { selectFiles } from '@/scripts/select-file'; import { defaultStore, notePostInterruptors, postFormActions } from '@/store'; import MkInfo from '@/components/MkInfo.vue'; @@ -157,15 +154,9 @@ let autocomplete = $ref(null); let draghover = $ref(false); let quoteId = $ref(null); let hasNotSpecifiedMentions = $ref(false); -let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') || '[]')); +let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]')); let imeText = $ref(''); -const typing = throttle(3000, () => { - if (props.channel) { - stream.send('typingOnChannel', { channel: props.channel.id }); - } -}); - const draftKey = $computed((): string => { let key = props.channel ? `channel:${props.channel.id}` : ''; @@ -209,7 +200,7 @@ const submitText = $computed((): string => { }); const textLength = $computed((): number => { - return length((text + imeText).trim()); + return (text + imeText).trim().length; }); const maxTextLength = $computed((): number => { @@ -228,7 +219,7 @@ const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags')); watch($$(text), () => { checkMissingMention(); -}); +}, { immediate: true }); watch($$(visibleUsers), () => { checkMissingMention(); @@ -447,12 +438,10 @@ function clear() { function onKeydown(ev: KeyboardEvent) { if ((ev.which === 10 || ev.which === 13) && (ev.ctrlKey || ev.metaKey) && canPost) post(); if (ev.which === 27) emit('esc'); - typing(); } function onCompositionUpdate(ev: CompositionEvent) { imeText = ev.data; - typing(); } function onCompositionEnd(ev: CompositionEvent) { @@ -544,7 +533,7 @@ function onDrop(ev): void { } function saveDraft() { - const draftData = JSON.parse(miLocalStorage.getItem('drafts') || '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); draftData[draftKey] = { updatedAt: new Date(), @@ -653,7 +642,7 @@ async function post(ev?: MouseEvent) { emit('posted'); if (postData.text && postData.text !== '') { const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); - const history = JSON.parse(miLocalStorage.getItem('hashtags') || '[]') as string[]; + const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); } posting = false; @@ -757,7 +746,7 @@ onMounted(() => { nextTick(() => { // 書ãã‹ã‘ã®æŠ•ç¨¿ã‚’復元 if (!props.instant && !props.mention && !props.specified) { - const draft = JSON.parse(miLocalStorage.getItem('drafts') || '{}')[draftKey]; + const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey]; if (draft) { text = draft.data.text; useCw = draft.data.useCw; diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index 766cc9a06c490c47e9ce8a55285b05a08cf24a30..5fb820f03f215494e648cec2aae1513208543311 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -15,10 +15,9 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, watch } from 'vue'; +import { defineAsyncComponent } from 'vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import * as os from '@/os'; -import { deepClone } from '@/scripts/clone'; import { i18n } from '@/i18n'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); diff --git a/packages/frontend/src/components/MkReactedUsersDialog.vue b/packages/frontend/src/components/MkReactedUsersDialog.vue index 2a8dffc0147da9fa2d83caaf8b8ff46179506dc1..1506e24ce885ed1dc23a5a9ba633207b4e01d079 100644 --- a/packages/frontend/src/components/MkReactedUsersDialog.vue +++ b/packages/frontend/src/components/MkReactedUsersDialog.vue @@ -10,15 +10,21 @@ <MkSpacer :margin-min="20" :margin-max="28"> <div v-if="note" class="_gaps"> - <div :class="$style.tabs"> - <button v-for="reaction in reactions" :key="reaction" :class="[$style.tab, { [$style.tabActive]: tab === reaction }]" class="_button" @click="tab = reaction"> - <MkReactionIcon :reaction="reaction"/> - <span style="margin-left: 4px;">{{ note.reactions[reaction] }}</span> - </button> + <div v-if="reactions.length === 0" class="_fullinfo"> + <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> </div> - <MkA v-for="user in users" :key="user.id" :to="userPage(user)"> - <MkUserCardMini :user="user" :with-chart="false"/> - </MkA> + <template v-else> + <div :class="$style.tabs"> + <button v-for="reaction in reactions" :key="reaction" :class="[$style.tab, { [$style.tabActive]: tab === reaction }]" class="_button" @click="tab = reaction"> + <MkReactionIcon :reaction="reaction"/> + <span style="margin-left: 4px;">{{ note.reactions[reaction] }}</span> + </button> + </div> + <MkA v-for="user in users" :key="user.id" :to="userPage(user)"> + <MkUserCardMini :user="user" :with-chart="false"/> + </MkA> + </template> </div> <div v-else> <MkLoading/> diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 4abd2562dfa223bdc00572838b48b5a115aeed47..fd0f42e9fc445fbd0d81eee9d83e360bf3322059 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -12,7 +12,7 @@ </template> <script lang="ts" setup> -import { computed, onMounted, ref, shallowRef, watch } from 'vue'; +import { computed, onMounted, shallowRef, watch } from 'vue'; import * as misskey from 'misskey-js'; import XDetails from '@/components/MkReactionsViewer.details.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index cdd6f528e76c5fd1e1334e86de0ee74c887fc1b0..76faffe926b071f3238f8c02af83174d2eed130f 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -51,7 +51,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe ...Object.entries(newSource) .sort(([, a], [, b]) => b - a) .filter(([y], i) => i < maxNumber && !newReactionsNames.includes(y)), - ] + ]; newReactions = newReactions.slice(0, props.maxNumber); diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue index 52c8b6d026d35617fed3aab1ffbd70ea467147d5..8326ec7ef3d7f47cde3d4f4523b0cc5afdeae81f 100644 --- a/packages/frontend/src/components/MkRetentionHeatmap.vue +++ b/packages/frontend/src/components/MkRetentionHeatmap.vue @@ -8,14 +8,11 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; +import { onMounted, nextTick } from 'vue'; import { Chart } from 'chart.js'; -import tinycolor from 'tinycolor2'; -import { MatrixController, MatrixElement } from 'chartjs-chart-matrix'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; -import { chartVLine } from '@/scripts/chart-vline'; import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue index 2edc224b1c9c852aa2f6f41665fae93d43c92d3e..2f5866f3401e8169fd0569a02500769cee7777be 100644 --- a/packages/frontend/src/components/MkRolePreview.vue +++ b/packages/frontend/src/components/MkRolePreview.vue @@ -1,10 +1,15 @@ <template> -<MkA v-adaptive-bg :to="`/admin/roles/${role.id}`" class="_panel" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }"> +<MkA v-adaptive-bg :to="forModeration ? `/admin/roles/${role.id}` : `/roles/${role.id}`" class="_panel" :class="$style.root" tabindex="-1" :style="{ '--color': role.color }"> <div :class="$style.title"> <span :class="$style.icon"> - <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i> - <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i> - <i v-else class="ti ti-user" style="opacity: 0.7;"></i> + <template v-if="role.iconUrl"> + <img :class="$style.badge" :src="role.iconUrl"/> + </template> + <template v-else> + <i v-if="role.isAdministrator" class="ti ti-crown" style="color: var(--accent);"></i> + <i v-else-if="role.isModerator" class="ti ti-shield" style="color: var(--accent);"></i> + <i v-else class="ti ti-user" style="opacity: 0.7;"></i> + </template> </span> <span :class="$style.name">{{ role.name }}</span> <span v-if="role.target === 'manual'" :class="$style.users">{{ role.usersCount }} users</span> @@ -16,12 +21,11 @@ <script lang="ts" setup> import { } from 'vue'; -import * as misskey from 'misskey-js'; -import * as os from '@/os'; import { i18n } from '@/i18n'; const props = defineProps<{ role: any; + forModeration: boolean; }>(); </script> @@ -40,6 +44,11 @@ const props = defineProps<{ margin-right: 8px; } +.badge { + height: 1.3em; + vertical-align: -20%; +} + .name { font-weight: bold; } diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index 12099153ebd4ed027c26688d214cea38a58b2fa8..2de890186aeb2ae991df470392f52f0833a31ef7 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -27,14 +27,14 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue'; +import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; import { i18n } from '@/i18n'; const props = defineProps<{ - modelValue: string; + modelValue: string | null; required?: boolean; readonly?: boolean; disabled?: boolean; @@ -48,7 +48,7 @@ const props = defineProps<{ const emit = defineEmits<{ (ev: 'change', _ev: KeyboardEvent): void; - (ev: 'update:modelValue', value: string): void; + (ev: 'update:modelValue', value: string | null): void; }>(); const slots = useSlots(); diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index 8cce1d43f6a21206ab91950202f614322411d09c..ffc5e82b56198de5a2c3f3acaa0cf2186d95b756 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -10,7 +10,7 @@ <template #prefix>@</template> <template #suffix>@{{ host }}</template> </MkInput> - <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" :with-password-toggle="true" required data-cy-signin-password> + <MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password" :with-password-toggle="true" required data-cy-signin-password> <template #prefix><i class="ti ti-lock"></i></template> <template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template> </MkInput> @@ -28,11 +28,11 @@ </div> <div class="twofa-group totp-group"> <p style="margin-bottom:0;">{{ i18n.ts.twoStepAuthentication }}</p> - <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" :with-password-toggle="true" required> + <MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :with-password-toggle="true" required> <template #label>{{ i18n.ts.password }}</template> <template #prefix><i class="ti ti-lock"></i></template> </MkInput> - <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false" required> + <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="one-time-code" :spellcheck="false" required> <template #label>{{ i18n.ts.token }}</template> <template #prefix><i class="ti ti-123"></i></template> </MkInput> @@ -50,7 +50,7 @@ import { showSuspendedDialog } from '../scripts/show-suspended-dialog'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkInfo from '@/components/MkInfo.vue'; -import { apiUrl, host as configHost } from '@/config'; +import { host as configHost } from '@/config'; import { byteify, hexify } from '@/scripts/2fa'; import * as os from '@/os'; import { login } from '@/account'; diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 5d33ad0ad383bc33e2048925229f2dac352c3184..2a8e43c570b498f335d918f0d7b7f2176240f7b6 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -24,7 +24,7 @@ </template> <script lang="ts"> -import { defineComponent, ref, unref } from 'vue'; +import { defineComponent } from 'vue'; export default defineComponent({ props: { diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 133f437ca1721510e4fcbe653036d79a34f32527..8bb8637dda7e4c3e1c734cfab6f17e0d7c0d01a3 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -22,7 +22,6 @@ <script lang="ts" setup> import { toRefs, Ref } from 'vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; const props = defineProps<{ diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue index 9f7e76f18e6035e9b9e555111e5bba62e197b67a..4e8d5bab7fd0753799187a0e50f6a66dba65b8f9 100644 --- a/packages/frontend/src/components/MkTagCloud.vue +++ b/packages/frontend/src/components/MkTagCloud.vue @@ -10,7 +10,7 @@ </template> <script lang="ts" setup> -import { onMounted, ref, watch, PropType, onBeforeUnmount } from 'vue'; +import { onMounted, watch, onBeforeUnmount } from 'vue'; import tinycolor from 'tinycolor2'; const loaded = !!window.TagCanvas; diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue index 0147ac7f8369f4aa1adfa0e6463a0287b0be8170..82b631edda3ef4a5bcb1d7dcebf46870e91c7c2f 100644 --- a/packages/frontend/src/components/MkTextarea.vue +++ b/packages/frontend/src/components/MkTextarea.vue @@ -27,7 +27,7 @@ </template> <script lang="ts"> -import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; +import { defineComponent, onMounted, nextTick, ref, watch, computed, toRefs } from 'vue'; import { debounce } from 'throttle-debounce'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 831a194ce3a18cd1660cae99007c3f15e3e58378..87f7c61a92875468702d6ec3ee0fe746b8783619 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -1,11 +1,10 @@ <template> -<XNotes ref="tlComponent" :no-gap="!$store.state.showGapBetweenNotesInTimeline" :pagination="pagination" @queue="emit('queue', $event)"/> +<MkNotes ref="tlComponent" :no-gap="!$store.state.showGapBetweenNotesInTimeline" :pagination="pagination" @queue="emit('queue', $event)"/> </template> <script lang="ts" setup> -import { ref, computed, provide, onUnmounted } from 'vue'; -import XNotes from '@/components/MkNotes.vue'; -import * as os from '@/os'; +import { computed, provide, onUnmounted } from 'vue'; +import MkNotes from '@/components/MkNotes.vue'; import { stream } from '@/stream'; import * as sound from '@/scripts/sound'; import { $i } from '@/account'; @@ -25,7 +24,7 @@ const emit = defineEmits<{ provide('inChannel', computed(() => props.src === 'channel')); -const tlComponent: InstanceType<typeof XNotes> = $ref(); +const tlComponent: InstanceType<typeof MkNotes> = $ref(); const prepend = note => { tlComponent.pagingComponent?.prepend(note); diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue index 95ab23f76d1786021732a6313f3d68dcc158412e..1aa48f88e62d3b4ead36c275d3683fba5bb0be6f 100644 --- a/packages/frontend/src/components/MkToast.vue +++ b/packages/frontend/src/components/MkToast.vue @@ -17,7 +17,7 @@ </template> <script lang="ts" setup> -import { onMounted, ref } from 'vue'; +import { onMounted } from 'vue'; import * as os from '@/os'; defineProps<{ diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue index 17c8ea01284dd970d4c165a8ed8654efd2f8c642..0b0556de39c817298116005cc6cf35ffa51f202b 100644 --- a/packages/frontend/src/components/MkTooltip.vue +++ b/packages/frontend/src/components/MkTooltip.vue @@ -16,7 +16,7 @@ </template> <script lang="ts" setup> -import { nextTick, onMounted, onUnmounted, ref, shallowRef } from 'vue'; +import { nextTick, onMounted, onUnmounted, shallowRef } from 'vue'; import * as os from '@/os'; import { calcPopupPosition } from '@/scripts/popup-position'; diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 62e58e1553c18eb576f2ee1d2f6e13166e12712d..b97b7cf07bc84ce09c9008ab7510feff3a5e9322 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -45,7 +45,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, onMounted, onUnmounted } from 'vue'; +import { defineAsyncComponent, onUnmounted } from 'vue'; import { url as local } from '@/config'; import { i18n } from '@/i18n'; import * as os from '@/os'; diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue index b7bc200a55ffcfd8308999d593a26f039fcceb3a..51eb426e97d2d704a18044252c1a126b7e081a8e 100644 --- a/packages/frontend/src/components/MkUserList.vue +++ b/packages/frontend/src/components/MkUserList.vue @@ -7,25 +7,26 @@ </div> </template> - <template #default="{ items: users }"> + <template #default="{ items }"> <div class="efvhhmdq"> - <MkUserInfo v-for="user in users" :key="user.id" class="user" :user="user"/> + <MkUserInfo v-for="item in items" :key="item.id" class="user" :user="extractor(item)"/> </div> </template> </MkPagination> </template> <script lang="ts" setup> -import { shallowRef } from 'vue'; import MkUserInfo from '@/components/MkUserInfo.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; -import { userPage } from '@/filters/user'; import { i18n } from '@/i18n'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ pagination: Paging; noGap?: boolean; -}>(); + extractor?: (item: any) => any; +}>(), { + extractor: (item) => item, +}); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/components/MkUserPreview.vue b/packages/frontend/src/components/MkUserPreview.vue index eacc66de4f84ff683d283ac32c55d54d8ae81334..1086a2c651ecc10a4b1ce926740b4e2e3b18abb9 100644 --- a/packages/frontend/src/components/MkUserPreview.vue +++ b/packages/frontend/src/components/MkUserPreview.vue @@ -89,7 +89,7 @@ onMounted(() => { <style lang="scss" scoped> .popup-enter-active, .popup-leave-active { - transition: opacity 0.3s, transform 0.3s !important; + transition: opacity 0.15s, transform 0.15s !important; } .popup-enter-from, .popup-leave-to { opacity: 0; @@ -183,8 +183,10 @@ onMounted(() => { > .menu { position: absolute; top: 8px; - right: 42px; - padding: 8px; + right: 44px; + padding: 6px; + background: var(--panel); + border-radius: 999px; } > .koudoku-button { diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index c17b97f283289a04e55d6728737112a599843d64..dc78bbf42d3faa1c2db5e88fb76346bf6afc9565 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -16,7 +16,7 @@ <template #label>{{ i18n.ts.username }}</template> <template #prefix>@</template> </MkInput> - <MkInput v-model="host" @update:model-value="search"> + <MkInput v-model="host" :datalist="[hostname]" @update:model-value="search"> <template #label>{{ i18n.ts.host }}</template> <template #prefix>@</template> </MkInput> @@ -52,7 +52,7 @@ </template> <script lang="ts" setup> -import { nextTick, onMounted } from 'vue'; +import { onMounted } from 'vue'; import * as misskey from 'misskey-js'; import MkInput from '@/components/MkInput.vue'; import FormSplit from '@/components/form/split.vue'; @@ -61,6 +61,7 @@ import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { $i } from '@/account'; +import { hostname } from '@/config'; const emit = defineEmits<{ (ev: 'ok', selected: misskey.entities.UserDetailed): void; @@ -115,7 +116,7 @@ onMounted(() => { os.api('users/show', { userIds: defaultStore.state.recentlyUsedUsers, }).then(users => { - if (props.includeSelf) { + if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) { recentUsers = [$i, ...users]; } else { recentUsers = users; diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue index eff64c12e5be796b4500e57f9ed46f1f0f511066..19c735c5f8817f20ea94f70aeaf4f0b802e269c9 100644 --- a/packages/frontend/src/components/MkWidgets.vue +++ b/packages/frontend/src/components/MkWidgets.vue @@ -43,14 +43,13 @@ export type DefaultStoredWidget = { } & Widget; </script> <script lang="ts" setup> -import { defineAsyncComponent, reactive, ref, computed } from 'vue'; +import { defineAsyncComponent, ref } from 'vue'; import { v4 as uuid } from 'uuid'; import MkSelect from '@/components/MkSelect.vue'; import MkButton from '@/components/MkButton.vue'; import { widgets as widgetDefs } from '@/widgets'; import * as os from '@/os'; import { i18n } from '@/i18n'; -import { deepClone } from '@/scripts/clone'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue index 5a0ba0d8d3c642af1325c83b3885bde2e5906f00..40d134dffb33a3cc65130f19cf4e6c8dd9048742 100644 --- a/packages/frontend/src/components/global/MkA.vue +++ b/packages/frontend/src/components/global/MkA.vue @@ -5,7 +5,6 @@ </template> <script lang="ts" setup> -import { inject } from 'vue'; import * as os from '@/os'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 9ad06545f201812aa35dcdc9de8f5ba3bc63fa80..d392ec6d6fe310b971619bd9540e4cd910cdfa7d 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -18,7 +18,7 @@ </template> <script lang="ts" setup> -import { onMounted, watch } from 'vue'; +import { watch } from 'vue'; import * as misskey from 'misskey-js'; import { getStaticImageUrl } from '@/scripts/media-proxy'; import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash'; diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index e6dedd035457947c86c5749f6d36c92840c31237..82aad44c1f31f48ef246d1651c4d60148f868baa 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -24,7 +24,7 @@ const rawUrl = computed(() => { return props.url; } if (props.host == null && !customEmojiName.value.includes('@')) { - return customEmojis.value.find(x => x.name === customEmojiName.value)?.url || null; + return customEmojis.value.find(x => x.name === customEmojiName.value)?.url ?? null; } return props.host ? `/emoji/${customEmojiName.value}@${props.host}.webp` : `/emoji/${customEmojiName.value}.webp`; }); @@ -32,7 +32,7 @@ const rawUrl = computed(() => { const url = computed(() => defaultStore.reactiveState.disableShowingAnimatedImages.value && rawUrl.value ? getStaticImageUrl(rawUrl.value) - : rawUrl.value + : rawUrl.value, ); const alt = computed(() => `:${customEmojiName.value}:`); diff --git a/packages/frontend/src/components/global/MkError.vue b/packages/frontend/src/components/global/MkError.vue index d412934a31431c73976b8abd3636d43610118dce..7390a9dfb999158035720fa91f7e78bfaf84b7b8 100644 --- a/packages/frontend/src/components/global/MkError.vue +++ b/packages/frontend/src/components/global/MkError.vue @@ -3,7 +3,7 @@ <div :class="$style.root"> <img :class="$style.img" src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> <p :class="$style.text"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.somethingHappened }}</p> - <MkButton :class="$style.button" @click="() => $emit('retry')">{{ i18n.ts.retry }}</MkButton> + <MkButton :class="$style.button" @click="() => emit('retry')">{{ i18n.ts.retry }}</MkButton> </div> </Transition> </template> @@ -11,6 +11,10 @@ <script lang="ts" setup> import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n'; + +const emit = defineEmits<{ + (ev: 'retry'): void; +}>(); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index dae68c7e9cc029765707e57033fb710f8ef4c7af..3beedf34b297d031aa95cc20df1f0a0d282ea307 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -1,23 +1,33 @@ <template> - <div ref="el" :class="$style.tabs" @wheel="onTabWheel"> - <div :class="$style.tabsInner"> - <button v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" - class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]" - @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)"> - <div :class="$style.tabInner"> - <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i> - <div v-if="!t.iconOnly || (!defaultStore.reactiveState.animation.value && t.key === tab)" - :class="$style.tabTitle">{{ t.title }}</div> - <Transition v-else @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave" - mode="in-out"> - <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div> - </Transition> +<div ref="el" :class="$style.tabs" @wheel="onTabWheel"> + <div :class="$style.tabsInner"> + <button + v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" + class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]" + @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)" + > + <div :class="$style.tabInner"> + <i v-if="t.icon" :class="[$style.tabIcon, t.icon]"></i> + <div + v-if="!t.iconOnly || (!defaultStore.reactiveState.animation.value && t.key === tab)" + :class="$style.tabTitle" + > + {{ t.title }} </div> - </button> - </div> - <div ref="tabHighlightEl" - :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]"></div> + <Transition + v-else mode="in-out" @enter="enter" @after-enter="afterEnter" @leave="leave" + @after-leave="afterLeave" + > + <div v-show="t.key === tab" :class="[$style.tabTitle, $style.animate]">{{ t.title }}</div> + </Transition> + </div> + </button> </div> + <div + ref="tabHighlightEl" + :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]" + ></div> +</div> </template> <script lang="ts"> @@ -34,7 +44,7 @@ export type Tab = { </script> <script lang="ts" setup> -import { onMounted, onUnmounted, watch, nextTick, Transition, shallowRef } from 'vue'; +import { onMounted, onUnmounted, watch, nextTick, shallowRef } from 'vue'; import { defaultStore } from '@/store'; const props = withDefaults(defineProps<{ @@ -206,8 +216,8 @@ onUnmounted(() => { align-items: center; } -.tabIcon+.tabTitle { - padding-left: 8px; +.tabIcon + .tabTitle { + padding-left: 4px; } .tabTitle { diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 803efb16908528dc67e6159fb839e1f09cc48e85..a4e25bbe1a88db814234229c1fd033644778e9e9 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -2,9 +2,9 @@ <div v-if="show" ref="el" :class="[$style.root]" :style="{ background: bg }"> <div :class="[$style.upper, { [$style.slim]: narrow, [$style.thin]: thin_ }]"> <div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu"> - <MkAvatar :class="$style.avatar" :user="$i" /> + <MkAvatar :class="$style.avatar" :user="$i"/> </div> - <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft" /> + <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/> <template v-if="metadata"> <div v-if="!hideTitle" :class="$style.titleContainer" @click="top"> @@ -19,7 +19,7 @@ </div> </div> </div> - <XTabs v-if="!narrow || hideTitle" :class="$style.tabs" :tab="tab" @update:tab="key => emit('update:tab', key)" :tabs="tabs" :root-el="el" @tab-click="onTabClick"/> + <XTabs v-if="!narrow || hideTitle" :class="$style.tabs" :tab="tab" :tabs="tabs" :root-el="el" @update:tab="key => emit('update:tab', key)" @tab-click="onTabClick"/> </template> <div v-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttonsRight"> <template v-for="action in actions"> @@ -28,7 +28,7 @@ </div> </div> <div v-if="(narrow && !hideTitle) && hasTabs" :class="[$style.lower, { [$style.slim]: narrow, [$style.thin]: thin_ }]"> - <XTabs :class="$style.tabs" :tab="tab" @update:tab="key => emit('update:tab', key)" :tabs="tabs" :root-el="el" @tab-click="onTabClick"/> + <XTabs :class="$style.tabs" :tab="tab" :tabs="tabs" :root-el="el" @update:tab="key => emit('update:tab', key)" @tab-click="onTabClick"/> </div> </div> </template> @@ -36,11 +36,11 @@ <script lang="ts" setup> import { onMounted, onUnmounted, ref, inject } from 'vue'; import tinycolor from 'tinycolor2'; +import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll'; import { globalEvents } from '@/events'; import { injectPageMetadata } from '@/scripts/page-metadata'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; -import XTabs, { Tab } from './MkPageHeader.tabs.vue' const props = withDefaults(defineProps<{ tabs?: Tab[]; @@ -96,7 +96,7 @@ function onTabClick(): void { } const calcBg = () => { - const rawBg = metadata?.bg || 'var(--bg)'; + const rawBg = metadata?.bg ?? 'var(--bg)'; const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue index 78e9a1a9c27126eeb8d77c1450cff4c1184aaa94..ba7c0400c749028b3122ed4d07702aeaef9de157 100644 --- a/packages/frontend/src/components/global/MkSpacer.vue +++ b/packages/frontend/src/components/global/MkSpacer.vue @@ -7,7 +7,7 @@ </template> <script lang="ts" setup> -import { inject, onMounted, onUnmounted, ref } from 'vue'; +import { inject } from 'vue'; import { deviceKind } from '@/scripts/device-kind'; const props = withDefaults(defineProps<{ diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index e5270ffefa14aae50d49a0a5ee14f1fcb33087a7..5763c84e811016f1f7caf10aae2f342a1a9acd16 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -11,7 +11,7 @@ </template> <script lang="ts" setup> -import { inject, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, watch } from 'vue'; +import { inject, onBeforeUnmount, provide } from 'vue'; import { Resolved, Router } from '@/nirax'; import { defaultStore } from '@/store'; diff --git a/packages/frontend/src/components/mfm.ts b/packages/frontend/src/components/mfm.ts index 816a42a5fb7d12b0483bfb6fc3ad060d2e1fb828..e84eabcbcc5741eb7f2085460c23200c7992fc82 100644 --- a/packages/frontend/src/components/mfm.ts +++ b/packages/frontend/src/components/mfm.ts @@ -5,13 +5,11 @@ import MkLink from '@/components/MkLink.vue'; import MkMention from '@/components/MkMention.vue'; import MkEmoji from '@/components/global/MkEmoji.vue'; import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue'; -import { concat } from '@/scripts/array'; import MkCode from '@/components/MkCode.vue'; import MkGoogle from '@/components/MkGoogle.vue'; import MkSparkle from '@/components/MkSparkle.vue'; import MkA from '@/components/global/MkA.vue'; import { host } from '@/config'; -import { MFM_TAGS } from '@/scripts/mfm-tags'; import { defaultStore } from '@/store'; const QUOTE_STYLE = ` @@ -280,7 +278,7 @@ export default defineComponent({ case 'hashtag': { return [h(MkA, { key: Math.random(), - to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.props.hashtag)}`, + to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`, style: 'color:var(--hashtag);', }, `#${token.props.hashtag}`)]; } diff --git a/packages/frontend/src/components/page/page.canvas.vue b/packages/frontend/src/components/page/page.canvas.vue index 80f6c8339c4529fdf24fc8e6a83758bd6f37f041..82ff36ec36e0de9ababd950e6f219e57e88520f9 100644 --- a/packages/frontend/src/components/page/page.canvas.vue +++ b/packages/frontend/src/components/page/page.canvas.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent, onMounted, PropType, Ref, ref } from 'vue'; -import * as os from '@/os'; import { CanvasBlock } from '@/scripts/hpml/block'; import { Hpml } from '@/scripts/hpml/evaluator'; diff --git a/packages/frontend/src/components/page/page.counter.vue b/packages/frontend/src/components/page/page.counter.vue index a9e1f41a548f6482934bba904214efbdaa0fcce7..63fde6a1207f9bc5cf51a7f240a73924f95db9df 100644 --- a/packages/frontend/src/components/page/page.counter.vue +++ b/packages/frontend/src/components/page/page.counter.vue @@ -7,7 +7,6 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; import MkButton from '../MkButton.vue'; -import * as os from '@/os'; import { CounterVarBlock } from '@/scripts/hpml/block'; import { Hpml } from '@/scripts/hpml/evaluator'; diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue index 8ba70c5855bb666de7125642d4c001bcc1a2a0f5..0237644d297df0dad9cc8e887f0e8b0c3fcc139b 100644 --- a/packages/frontend/src/components/page/page.image.vue +++ b/packages/frontend/src/components/page/page.image.vue @@ -5,9 +5,8 @@ </template> <script lang="ts" setup> -import { defineComponent, PropType } from 'vue'; +import { PropType } from 'vue'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; -import * as os from '@/os'; import { ImageBlock } from '@/scripts/hpml/block'; import { Hpml } from '@/scripts/hpml/evaluator'; diff --git a/packages/frontend/src/components/page/page.number-input.vue b/packages/frontend/src/components/page/page.number-input.vue index 4c5aae10401845f54b56671c16acd7428ede8dda..72c1b6deb091fbcd68329fe3381f3cd384cbb81c 100644 --- a/packages/frontend/src/components/page/page.number-input.vue +++ b/packages/frontend/src/components/page/page.number-input.vue @@ -9,7 +9,6 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; import MkInput from '../MkInput.vue'; -import * as os from '@/os'; import { Hpml } from '@/scripts/hpml/evaluator'; import { NumberInputVarBlock } from '@/scripts/hpml/block'; diff --git a/packages/frontend/src/components/page/page.radio-button.vue b/packages/frontend/src/components/page/page.radio-button.vue index 2ae8d00ffc0dbe96bc4dca79205216a79bf9269d..ce8f252e440f633eb2d46a95adb56ae31c4b3221 100644 --- a/packages/frontend/src/components/page/page.radio-button.vue +++ b/packages/frontend/src/components/page/page.radio-button.vue @@ -8,7 +8,6 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; import MkRadio from '../MkRadio.vue'; -import * as os from '@/os'; import { Hpml } from '@/scripts/hpml/evaluator'; import { RadioButtonVarBlock } from '@/scripts/hpml/block'; diff --git a/packages/frontend/src/components/page/page.section.vue b/packages/frontend/src/components/page/page.section.vue index 630c1f517966c8345586e34bdcf7a9da701bd303..50181b390568f7fd3446ab553c3534f2c64e3123 100644 --- a/packages/frontend/src/components/page/page.section.vue +++ b/packages/frontend/src/components/page/page.section.vue @@ -10,7 +10,6 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent, PropType } from 'vue'; -import * as os from '@/os'; import { SectionBlock } from '@/scripts/hpml/block'; import { Hpml } from '@/scripts/hpml/evaluator'; diff --git a/packages/frontend/src/components/page/page.switch.vue b/packages/frontend/src/components/page/page.switch.vue index d0d2637afa4591c430e91026588d9339f1135ec3..b5f3464512babdeb315f4474b82251c44c5b56bf 100644 --- a/packages/frontend/src/components/page/page.switch.vue +++ b/packages/frontend/src/components/page/page.switch.vue @@ -7,7 +7,6 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; import MkSwitch from '../MkSwitch.vue'; -import * as os from '@/os'; import { Hpml } from '@/scripts/hpml/evaluator'; import { SwitchVarBlock } from '@/scripts/hpml/block'; diff --git a/packages/frontend/src/components/page/page.text-input.vue b/packages/frontend/src/components/page/page.text-input.vue index 50731a9c9d73d22ec3f60ced0850700a022644c2..d020a99de88318fc7dfa8c521885f12a9377d0b4 100644 --- a/packages/frontend/src/components/page/page.text-input.vue +++ b/packages/frontend/src/components/page/page.text-input.vue @@ -9,7 +9,6 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; import MkInput from '../MkInput.vue'; -import * as os from '@/os'; import { Hpml } from '@/scripts/hpml/evaluator'; import { TextInputVarBlock } from '@/scripts/hpml/block'; diff --git a/packages/frontend/src/components/page/page.textarea-input.vue b/packages/frontend/src/components/page/page.textarea-input.vue index 7905c7eded9b835c18e3b78f120cf80a441203a8..db3a96dd1b12387783ab514f09b69b8ada8b1c3f 100644 --- a/packages/frontend/src/components/page/page.textarea-input.vue +++ b/packages/frontend/src/components/page/page.textarea-input.vue @@ -9,9 +9,7 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; import MkTextarea from '../MkTextarea.vue'; -import * as os from '@/os'; import { Hpml } from '@/scripts/hpml/evaluator'; -import { HpmlTextInput } from '@/scripts/hpml'; import { TextInputVarBlock } from '@/scripts/hpml/block'; export default defineComponent({ diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue index 87a288befe9fb87b85da75889d5af69ac326cb2d..5f1f62581ea481c29636992327b2717a3b431924 100644 --- a/packages/frontend/src/components/page/page.vue +++ b/packages/frontend/src/components/page/page.vue @@ -5,12 +5,11 @@ </template> <script lang="ts"> -import { defineComponent, onMounted, nextTick, onUnmounted, PropType } from 'vue'; +import { defineComponent, onMounted, nextTick, PropType } from 'vue'; import XBlock from './page.block.vue'; import { Hpml } from '@/scripts/hpml/evaluator'; import { url } from '@/config'; import { $i } from '@/account'; -import { defaultStore } from '@/store'; export default defineComponent({ components: { diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 64c252ce552c2f26b67844ea755be19c6afec9c2..8c657295f991d7f65fec70cea380bc3ad30bd362 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -25,7 +25,7 @@ import JSON5 from 'json5'; import widgets from '@/widgets'; import directives from '@/directives'; import components from '@/components'; -import { version, ui, lang, host, updateLocale } from '@/config'; +import { version, ui, lang, updateLocale } from '@/config'; import { applyTheme } from '@/scripts/theme'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; import { i18n, updateI18n } from '@/i18n'; @@ -505,15 +505,6 @@ if ($i) { updateAccount({ hasUnreadSpecifiedNotes: false }); }); - main.on('readAllMessagingMessages', () => { - updateAccount({ hasUnreadMessagingMessage: false }); - }); - - main.on('unreadMessagingMessage', () => { - updateAccount({ hasUnreadMessagingMessage: true }); - sound.play('chatBg'); - }); - main.on('readAllAntennas', () => { updateAccount({ hasUnreadAntenna: false }); }); diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 08dbd9737c55ec0372f7575a34b296ceab30e117..f4c1988704caa4f7f7f2bebdcf57a39bf385662f 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -1,4 +1,4 @@ -import { computed, reactive } from 'vue'; +import { reactive } from 'vue'; import * as Misskey from 'misskey-js'; import { api } from './os'; import { miLocalStorage } from './local-storage'; diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 4f809d888ef30e770329090abd66e4fa1129e372..95bf6e818181f2ae659cdd9fef288946d295395d 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -1,4 +1,4 @@ -import { computed, ref, reactive } from 'vue'; +import { computed, reactive } from 'vue'; import { $i } from './account'; import { miLocalStorage } from './local-storage'; import { search } from '@/scripts/search'; @@ -15,13 +15,6 @@ export const navbarItemDef = reactive({ indicated: computed(() => $i != null && $i.hasUnreadNotification), to: '/my/notifications', }, - messaging: { - title: i18n.ts.messaging, - icon: 'ti ti-messages', - show: computed(() => $i != null), - indicated: computed(() => $i != null && $i.hasUnreadMessagingMessage), - to: '/my/messaging', - }, drive: { title: i18n.ts.drive, icon: 'ti ti-cloud', @@ -57,14 +50,6 @@ export const navbarItemDef = reactive({ show: computed(() => $i != null), to: '/my/lists', }, - /* - groups: { - title: i18n.ts.groups, - icon: 'ti ti-users', - show: computed(() => $i != null), - to: '/my/groups', - }, - */ antennas: { title: i18n.ts.antennas, icon: 'ti ti-antenna', diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 53e73a8d481e7202f33aaa282c0aeec1f28972fd..68977ed796fad23f2d99c848b0e7f57d7a0f7440 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -1,7 +1,7 @@ // NIRAX --- A lightweight router import { EventEmitter } from 'eventemitter3'; -import { Ref, Component, ref, shallowRef, ShallowRef } from 'vue'; +import { Component, shallowRef, ShallowRef } from 'vue'; import { pleaseLogin } from '@/scripts/please-login'; import { safeURIDecode } from '@/scripts/safe-uri-decode'; diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 6bff12661f57b057765f724215afca1cadd4bef1..a69fe73f30573af49c67de02b39d8ba05e936645 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -246,7 +246,10 @@ export function inputText(props: { title?: string | null; text?: string | null; placeholder?: string | null; + autocomplete?: string; default?: string | null; + minLength?: number; + maxLength?: number; }): Promise<{ canceled: true; result: undefined; } | { canceled: false; result: string; }> { @@ -257,7 +260,10 @@ export function inputText(props: { input: { type: props.type, placeholder: props.placeholder, + autocomplete: props.autocomplete, default: props.default, + minLength: props.minLength, + maxLength: props.maxLength, }, }, { done: result => { @@ -271,6 +277,7 @@ export function inputNumber(props: { title?: string | null; text?: string | null; placeholder?: string | null; + autocomplete?: string; default?: number | null; }): Promise<{ canceled: true; result: undefined; } | { canceled: false; result: number; @@ -282,6 +289,7 @@ export function inputNumber(props: { input: { type: 'number', placeholder: props.placeholder, + autocomplete: props.autocomplete, default: props.default, }, }, { diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index bc3d2481933acfd912da68869cf59d9affd3d4cf..782fe9fdb2f2b271967cd7e90e78fe0c005f6b02 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -84,6 +84,10 @@ </div> <p>{{ i18n.ts._aboutMisskey.morePatrons }}</p> </FormSection> + <FormSection> + <template #label>Credits</template> + <p>Misskeyã§ä½¿ã‚れる画åƒã®ä¸€éƒ¨ã¯ã€è¨±å¯ã‚’å¾—ã¦ã€Œã‚ã®åãŒã“ã£ã¡ã‚’見ã¦ã‚‹ãƒ¡ãƒ¼ã‚«ãƒ¼ã€ã§ä½œæˆã—ãŸã‚‚ã®ãŒå«ã¾ã‚Œã¾ã™ã€‚</p> + </FormSection> </div> </MkSpacer> </div> @@ -111,6 +115,12 @@ const patronsWithIcon = [{ }, { name: 'ã ã‚Œã‹ã•ã‚“', icon: 'https://misskey-hub.net/patrons/f7409b5e5a88477a9b9d740c408de125.jpg', +}, { + name: 'narazaka', + icon: 'https://misskey-hub.net/patrons/e3affff31ffb4877b1196c7360abc3e5.jpg', +}, { + name: 'ã²ã¨ã…', + icon: 'https://misskey-hub.net/patrons/8cc0d0a0a6d84c88bca1aedabf6ed5ab.jpg', }]; const patrons = [ diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index d964e48b3113759a006a000a40bbc79151330c57..7f3b4fd9372a887802e0ed6c339587631a1108d1 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -31,14 +31,11 @@ </template> <script lang="ts" setup> -import { defineComponent, computed, watch } from 'vue'; +import { watch } from 'vue'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; -import MkSelect from '@/components/MkSelect.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkTab from '@/components/MkTab.vue'; -import * as os from '@/os'; import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis'; import { i18n } from '@/i18n'; import * as Misskey from 'misskey-js'; diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue index 90d6893f37e5aa71b6d4ad79538338ee906ec91e..8fe613a9a86751d7482c5581b4bb3d56945e8820 100644 --- a/packages/frontend/src/pages/about.federation.vue +++ b/packages/frontend/src/pages/about.federation.vue @@ -46,15 +46,12 @@ <script lang="ts" setup> import { computed } from 'vue'; -import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue'; import FormSplit from '@/components/form/split.vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; -import { dateString } from '@/filters/date'; let host = $ref(''); let state = $ref('federating'); diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index e5b9aecc6127c8d5a48fd61733077a9586c71800..be0c1828a32ce6119adcefad5e3e859c70cb86ba 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -86,10 +86,10 @@ </template> <script lang="ts" setup> -import { ref, computed, watch } from 'vue'; +import { computed, watch } from 'vue'; import XEmojis from './about.emojis.vue'; import XFederation from './about.federation.vue'; -import { version, instanceName, host } from '@/config'; +import { version, host } from '@/config'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import FormSuspense from '@/components/form/suspense.vue'; diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue index effa2fcaf4c44d07e98d233a47317a014aa19e14..1eef7a53fe37a328b09a66389488ab90184357bf 100644 --- a/packages/frontend/src/pages/achievements.vue +++ b/packages/frontend/src/pages/achievements.vue @@ -8,7 +8,7 @@ </template> <script lang="ts" setup> -import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue'; +import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'; import MkAchievements from '@/components/MkAchievements.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 6aac064c36b4dce3e9d0f2cbebd399db384c2f4f..1d309a7377880f65cbea59ecd270384bd0a04acd 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -75,7 +75,6 @@ import bytes from '@/filters/bytes'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { acct } from '@/filters/user'; import { iAmAdmin, iAmModerator } from '@/account'; let tab = $ref('overview'); diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue index 5bd38034860d114d863d7a7cd3a6779990623e3d..07729b8cf94badb7e05f715803978a0650f41390 100644 --- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue +++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue @@ -52,12 +52,7 @@ import { computed, defineAsyncComponent, ref, watch } from 'vue'; import { v4 as uuid } from 'uuid'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; -import MkTextarea from '@/components/MkTextarea.vue'; -import MkFolder from '@/components/MkFolder.vue'; -import MkSwitch from '@/components/MkSwitch.vue'; import MkButton from '@/components/MkButton.vue'; -import FormSlot from '@/components/form/slot.vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; import { deepClone } from '@/scripts/clone'; diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue index a342644516cbb8194158364c5366ef40e73da3b1..372c63ff4c2ac3f97902e5055fa974b36857f875 100644 --- a/packages/frontend/src/pages/admin/_header_.vue +++ b/packages/frontend/src/pages/admin/_header_.vue @@ -28,13 +28,11 @@ </template> <script lang="ts" setup> -import { computed, onMounted, onUnmounted, ref, shallowRef, inject, watch, nextTick } from 'vue'; +import { computed, onMounted, onUnmounted, ref, shallowRef, watch, nextTick } from 'vue'; import tinycolor from 'tinycolor2'; import { popupMenu } from '@/os'; -import { url } from '@/config'; import { scrollToTop } from '@/scripts/scroll'; import MkButton from '@/components/MkButton.vue'; -import { i18n } from '@/i18n'; import { globalEvents } from '@/events'; import { injectPageMetadata } from '@/scripts/page-metadata'; @@ -115,7 +113,7 @@ function onTabClick(tab: Tab, ev: MouseEvent): void { } const calcBg = () => { - const rawBg = metadata?.bg || 'var(--bg)'; + const rawBg = metadata?.bg ?? 'var(--bg)'; const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index 1c8557400f5cdb0245f02f31c7de4661d5ac620d..9e8af43024e6eca006fe08db606a2ad5401bb7e8 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -50,11 +50,9 @@ import { computed } from 'vue'; import XHeader from './_header_.vue'; -import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; import XAbuseReport from '@/components/MkAbuseReport.vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index 701ec31b65dad8b6540bdcb233f145171538a1b7..828bfe60077a92db73d68beff0c4de55e92f60c6 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -29,6 +29,9 @@ <MkInput v-model="ad.ratio" type="number"> <template #label>{{ i18n.ts.ratio }}</template> </MkInput> + <MkInput v-model="ad.startsAt" type="datetime-local"> + <template #label>{{ i18n.ts.startingperiod }}</template> + </MkInput> <MkInput v-model="ad.expiresAt" type="datetime-local"> <template #label>{{ i18n.ts.expiration }}</template> </MkInput> @@ -41,6 +44,9 @@ <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> </div> + <MkButton class="button" @click="more()"> + <i class="ti ti-reload"></i>{{ i18n.ts.more }} + </MkButton> </div> </MkSpacer> </MkStickyContainer> @@ -66,11 +72,14 @@ const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000; os.api('admin/ad/list').then(adsResponse => { ads = adsResponse.map(r => { - const date = new Date(r.expiresAt); - date.setMilliseconds(date.getMilliseconds() - localTimeDiff); + const exdate = new Date(r.expiresAt); + const stdate = new Date(r.startsAt); + exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); + stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff); return { ...r, - expiresAt: date.toISOString().slice(0, 16), + expiresAt: exdate.toISOString().slice(0, 16), + startsAt: stdate.toISOString().slice(0, 16), }; }); }); @@ -85,6 +94,7 @@ function add() { url: '', imageUrl: null, expiresAt: null, + startsAt: null, }); } @@ -106,15 +116,31 @@ function save(ad) { os.apiWithDialog('admin/ad/create', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), + startsAt: new Date(ad.startsAt).getTime(), }); } else { os.apiWithDialog('admin/ad/update', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), + startsAt: new Date(ad.startsAt).getTime(), }); } } - +function more() { + os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id }).then(adsResponse => { + ads = ads.concat(adsResponse.map(r => { + const exdate = new Date(r.expiresAt); + const stdate = new Date(r.startsAt); + exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff); + stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff); + return { + ...r, + expiresAt: exdate.toISOString().slice(0, 16), + startsAt: stdate.toISOString().slice(0, 16), + }; + })); + }); +} const headerActions = $computed(() => [{ asFullButton: true, icon: 'ti ti-plus', diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue index f2c114ca215061c3ba7106155e55a1def942adc4..6af1610431b4c2018108cf67b48197e2f534a5a2 100644 --- a/packages/frontend/src/pages/admin/federation.vue +++ b/packages/frontend/src/pages/admin/federation.vue @@ -54,15 +54,12 @@ <script lang="ts" setup> import { computed } from 'vue'; import XHeader from './_header_.vue'; -import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue'; import FormSplit from '@/components/form/split.vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; -import { dateString } from '@/filters/date'; import { definePageMetadata } from '@/scripts/page-metadata'; let host = $ref(''); diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue index 1d61cdf8e464002316f46ca647faf01c09461ca0..c189437246abc1d6b005bdfd7cb44246d918e98f 100644 --- a/packages/frontend/src/pages/admin/files.vue +++ b/packages/frontend/src/pages/admin/files.vue @@ -33,14 +33,11 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent } from 'vue'; -import * as Acct from 'misskey-js/built/acct'; +import { computed } from 'vue'; import XHeader from './_header_.vue'; -import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; -import bytes from '@/filters/bytes'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 9a07d3c959c8d9cbfaaebc1efe7066be71ed5c81..b054999303f02e48c9e710b25fa7e391b5783014 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -23,16 +23,15 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, inject, nextTick, onMounted, onUnmounted, provide, watch } from 'vue'; +import { onMounted, onUnmounted, provide, watch } from 'vue'; import { i18n } from '@/i18n'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; -import { scroll } from '@/scripts/scroll'; import { instance } from '@/instance'; import * as os from '@/os'; import { lookupUser } from '@/scripts/lookup-user'; import { useRouter } from '@/router'; -import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; const isEmpty = (x: string | null) => x == null || x === ''; diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 6a6af637eddb8f95524fa5ecb7b280b69f70ea3e..bd7c2035122db5b9bc8706fcc793969bf72b4477 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -75,7 +75,6 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; -import FormSection from '@/components/form/section.vue'; import * as os from '@/os'; import { fetchInstance } from '@/instance'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue index 3ec7694f26f163a2d3e5afc34c4a452e779130a0..fc10ad75f807a4622ae9125c6a1a7ecdc13ce63f 100644 --- a/packages/frontend/src/pages/admin/overview.active-users.vue +++ b/packages/frontend/src/pages/admin/overview.active-users.vue @@ -8,15 +8,13 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; +import { onMounted } from 'vue'; import { Chart } from 'chart.js'; -import tinycolor from 'tinycolor2'; import gradient from 'chartjs-plugin-gradient'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; -import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; initChart(); diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue index efb335fff1a9e03188046892af32549acf1794b6..ad8e6234155155bc49a5fc48a1724fa2a2db00c9 100644 --- a/packages/frontend/src/pages/admin/overview.ap-requests.vue +++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue @@ -15,15 +15,10 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; +import { onMounted } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; -import tinycolor from 'tinycolor2'; -import MkMiniChart from '@/components/MkMiniChart.vue'; import * as os from '@/os'; -import number from '@/filters/number'; -import MkNumberDiff from '@/components/MkNumberDiff.vue'; -import { i18n } from '@/i18n'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; import { defaultStore } from '@/store'; diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue index 2789adf64314822c5df7629b303ecdd219c9f142..ab78c4c393da07e334aa3c44f00d8be6be325399 100644 --- a/packages/frontend/src/pages/admin/overview.federation.vue +++ b/packages/frontend/src/pages/admin/overview.federation.vue @@ -41,9 +41,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; +import { onMounted } from 'vue'; import XPie from './overview.pie.vue'; -import MkMiniChart from '@/components/MkMiniChart.vue'; import * as os from '@/os'; import number from '@/filters/number'; import MkNumberDiff from '@/components/MkNumberDiff.vue'; diff --git a/packages/frontend/src/pages/admin/overview.instances.vue b/packages/frontend/src/pages/admin/overview.instances.vue index 15dbdc4639742f47f6656e1d4401ecdf635e8a26..7d530d6b95212af1b622e4086ec67b5dc8ebd5cf 100644 --- a/packages/frontend/src/pages/admin/overview.instances.vue +++ b/packages/frontend/src/pages/admin/overview.instances.vue @@ -12,7 +12,7 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; +import { ref } from 'vue'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue'; diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue index 445217d8252d07f48bd42af10ab32b74947e8ac8..ff689b8bf9fc0c78bb17ffa9022d76fa1f5a8788 100644 --- a/packages/frontend/src/pages/admin/overview.moderators.vue +++ b/packages/frontend/src/pages/admin/overview.moderators.vue @@ -12,10 +12,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; +import { onMounted } from 'vue'; import * as os from '@/os'; -import number from '@/filters/number'; -import { i18n } from '@/i18n'; let moderators: any = $ref(null); let fetching = $ref(true); diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue index 416e963356b363cc5c1d1408aa826a4e0b0de8f0..08a29bf550373933f6c13cee800f352069c7d84e 100644 --- a/packages/frontend/src/pages/admin/overview.pie.vue +++ b/packages/frontend/src/pages/admin/overview.pie.vue @@ -3,10 +3,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref, shallowRef } from 'vue'; +import { onMounted, shallowRef } from 'vue'; import { Chart } from 'chart.js'; -import number from '@/filters/number'; -import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { initChart } from '@/scripts/init-chart'; diff --git a/packages/frontend/src/pages/admin/overview.queue.chart.vue b/packages/frontend/src/pages/admin/overview.queue.chart.vue index 0162d536656ad89bd56ebdeb28b95e4b5ecf2397..6a11e8b768fa622c0ab6232aeb9f508c92b7b387 100644 --- a/packages/frontend/src/pages/admin/overview.queue.chart.vue +++ b/packages/frontend/src/pages/admin/overview.queue.chart.vue @@ -3,10 +3,8 @@ </template> <script lang="ts" setup> -import { watch, onMounted, onUnmounted, ref, shallowRef } from 'vue'; +import { onMounted, shallowRef } from 'vue'; import { Chart } from 'chart.js'; -import number from '@/filters/number'; -import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue index 7e5888293841105dae37fd50efbdf49500753d73..1f56a2826aca0ac8ae6c14853b7f680f74b3a29e 100644 --- a/packages/frontend/src/pages/admin/overview.queue.vue +++ b/packages/frontend/src/pages/admin/overview.queue.vue @@ -33,9 +33,7 @@ import { markRaw, onMounted, onUnmounted, ref } from 'vue'; import XChart from './overview.queue.chart.vue'; import number from '@/filters/number'; -import * as os from '@/os'; import { stream } from '@/stream'; -import { i18n } from '@/i18n'; const connection = markRaw(stream.useChannel('queueStats')); diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue index bd636cc3ef703234a7b46e2e67b2ef0d6e12b809..3dc1ed8ec5adf5b8f3d8c2fccb073f96ec2d9a94 100644 --- a/packages/frontend/src/pages/admin/overview.stats.vue +++ b/packages/frontend/src/pages/admin/overview.stats.vue @@ -56,10 +56,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; -import MkMiniChart from '@/components/MkMiniChart.vue'; +import { onMounted } from 'vue'; import * as os from '@/os'; -import number from '@/filters/number'; import MkNumberDiff from '@/components/MkNumberDiff.vue'; import MkNumber from '@/components/MkNumber.vue'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue index 5390d9d8cbbe2b1619f46b6b93b5cb4c90933eb1..3379d064cd70f984cf31c30c4910c19523e7185c 100644 --- a/packages/frontend/src/pages/admin/overview.users.vue +++ b/packages/frontend/src/pages/admin/overview.users.vue @@ -12,7 +12,6 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue index 0166724e01b66570f3db39989759a50510fcda28..5c96c07bfbe2a5256f71a64c131d0c7a6ea0a0fb 100644 --- a/packages/frontend/src/pages/admin/overview.vue +++ b/packages/frontend/src/pages/admin/overview.vue @@ -60,7 +60,7 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; +import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue'; import XFederation from './overview.federation.vue'; import XInstances from './overview.instances.vue'; import XQueue from './overview.queue.vue'; @@ -71,14 +71,10 @@ import XStats from './overview.stats.vue'; import XRetention from './overview.retention.vue'; import XModerators from './overview.moderators.vue'; import XHeatmap from './overview.heatmap.vue'; -import MkTagCloud from '@/components/MkTagCloud.vue'; -import { version, url } from '@/config'; import * as os from '@/os'; import { stream } from '@/stream'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { defaultStore } from '@/store'; -import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; const rootEl = $shallowRef<HTMLElement>(); diff --git a/packages/frontend/src/pages/admin/queue.chart.chart.vue b/packages/frontend/src/pages/admin/queue.chart.chart.vue index a0c05df9834b90035f8bf7023ee6d3af3d12be11..1a1f6a9db4dd434426d8a6ba50a5f0824547f348 100644 --- a/packages/frontend/src/pages/admin/queue.chart.chart.vue +++ b/packages/frontend/src/pages/admin/queue.chart.chart.vue @@ -3,10 +3,8 @@ </template> <script lang="ts" setup> -import { watch, onMounted, onUnmounted, ref, shallowRef } from 'vue'; +import { onMounted, shallowRef } from 'vue'; import { Chart } from 'chart.js'; -import number from '@/filters/number'; -import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue index 8d19b49fc5479a3dfe764884bf8faeec77c54d81..80e97fed93834e702261a6569af845629f749479 100644 --- a/packages/frontend/src/pages/admin/queue.vue +++ b/packages/frontend/src/pages/admin/queue.vue @@ -9,10 +9,8 @@ </template> <script lang="ts" setup> -import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue'; import XQueue from './queue.chart.vue'; import XHeader from './_header_.vue'; -import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import * as config from '@/config'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue index 3cb4e2deb98b9a91714cecd6a94489206af1be6e..ae884c0111175a16616fed4bbe410618e4121e7d 100644 --- a/packages/frontend/src/pages/admin/roles.edit.vue +++ b/packages/frontend/src/pages/admin/roles.edit.vue @@ -13,13 +13,6 @@ import { computed } from 'vue'; import XHeader from './_header_.vue'; import XEditor from './roles.editor.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkSelect from '@/components/MkSelect.vue'; -import MkTextarea from '@/components/MkTextarea.vue'; -import MkFolder from '@/components/MkFolder.vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import MkButton from '@/components/MkButton.vue'; -import FormSlot from '@/components/form/slot.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index d89a68f9823c990682fc79691da90363f8bc5bca..4eea827de7059e7e363af5d0fc679d30a3454be4 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -384,7 +384,7 @@ </template> <script lang="ts" setup> -import { computed, reactive, watch } from 'vue'; +import { reactive, watch } from 'vue'; import { v4 as uuid } from 'uuid'; import RolesEditorFormula from './RolesEditorFormula.vue'; import MkInput from '@/components/MkInput.vue'; diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 0365165b5d8d0a0fb0e7cf0382ad1a3e79ed7f7e..1b9f0e7c5321c2a29d9d9749c6b76ff80b66baca 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -16,16 +16,29 @@ <MkFolder v-if="role.target === 'manual'" default-open> <template #icon><i class="ti ti-users"></i></template> <template #label>{{ i18n.ts.users }}</template> - <template #suffix>{{ role.users.length }}</template> + <template #suffix>{{ role.usersCount }}</template> <div class="_gaps"> <MkButton primary rounded @click="assign"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> - <div v-for="user in role.users" :key="user.id" :class="$style.userItem"> - <MkA :class="$style.user" :to="`/user-info/${user.id}`"> - <MkUserCardMini :user="user"/> - </MkA> - <button class="_button" :class="$style.unassign" @click="unassign(user, $event)"><i class="ti ti-x"></i></button> - </div> + <MkPagination :pagination="usersPagination"> + <template #empty> + <div class="_fullinfo"> + <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> + + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.user.id" :class="$style.userItem"> + <MkA :class="$style.user" :to="`/user-info/${item.user.id}`"> + <MkUserCardMini :user="item.user"/> + </MkA> + <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button> + </div> + </div> + </template> + </MkPagination> </div> </MkFolder> <MkInfo v-else>{{ i18n.ts._role.isConditionalRole }}</MkInfo> @@ -47,6 +60,7 @@ import { useRouter } from '@/router'; import MkButton from '@/components/MkButton.vue'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkInfo from '@/components/MkInfo.vue'; +import MkPagination, { Paging } from '@/components/MkPagination.vue'; const router = useRouter(); @@ -54,6 +68,14 @@ const props = defineProps<{ id?: string; }>(); +const usersPagination = { + endpoint: 'admin/roles/users' as const, + limit: 20, + params: computed(() => ({ + roleId: props.id, + })), +}; + const role = reactive(await os.api('admin/roles/show', { roleId: props.id, })); diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index ff8f8a356f078ea88660efcea7096cea9502dc98..d89f0d2a7dfb18a565c2e6e28c9d03a277ce4953 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -133,7 +133,7 @@ </div> </MkFolder> <div class="_gaps_s"> - <MkRolePreview v-for="role in roles" :key="role.id" :role="role"/> + <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="true"/> </div> </div> </MkSpacer> @@ -145,8 +145,6 @@ import { computed, reactive } from 'vue'; import XHeader from './_header_.vue'; import MkInput from '@/components/MkInput.vue'; -import MkSelect from '@/components/MkSelect.vue'; -import MkPagination from '@/components/MkPagination.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkButton from '@/components/MkButton.vue'; diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 191da506e93fa500e727f6ecddeec4ab48500749..cd8ef9e68ba383d3e0cf11e7d6d1a924b1fa9097 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -108,7 +108,6 @@ import XHeader from './_header_.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkSwitch from '@/components/MkSwitch.vue'; -import FormInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import MkRange from '@/components/MkRange.vue'; import MkInput from '@/components/MkInput.vue'; diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 12d852a90ef4abeccfb6a1c937ee682a1f2d1986..7840c55ee4514e6aa0164483500ec34b841dfd9a 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -143,7 +143,6 @@ import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; -import FormInfo from '@/components/MkInfo.vue'; import FormSection from '@/components/form/section.vue'; import FormSplit from '@/components/form/split.vue'; import FormSuspense from '@/components/form/suspense.vue'; diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue index ff31c3ab2cd8ccd4e611f43e6e6246d97d641349..cf803d6c7f05c2e6aafbf68b641993398c2af16c 100644 --- a/packages/frontend/src/pages/antenna-timeline.vue +++ b/packages/frontend/src/pages/antenna-timeline.vue @@ -4,7 +4,7 @@ <div ref="rootEl" v-hotkey.global="keymap" class="tqmomfks"> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> <div class="tl"> - <XTimeline + <MkTimeline ref="tlEl" :key="antennaId" class="tl" src="antenna" @@ -18,8 +18,8 @@ </template> <script lang="ts" setup> -import { computed, inject, watch } from 'vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import { computed, watch } from 'vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; import { useRouter } from '@/router'; @@ -35,7 +35,7 @@ const props = defineProps<{ let antenna = $ref(null); let queue = $ref(0); let rootEl = $shallowRef<HTMLElement>(); -let tlEl = $shallowRef<InstanceType<typeof XTimeline>>(); +let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>(); const keymap = $computed(() => ({ 't': focus, })); @@ -72,7 +72,7 @@ watch(() => props.antennaId, async () => { }, { immediate: true }); const headerActions = $computed(() => antenna ? [{ - icon: 'fas fa-calendar-alt', + icon: 'ti ti-calendar-time', text: i18n.ts.jumpToSpecifiedDate, handler: timetravel, }, { diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue index 50afffc4607ab196409de9479e3da14d3ed42ac5..4f8afb9ea24d12061cd04188c8f5113d5e4992d5 100644 --- a/packages/frontend/src/pages/auth.vue +++ b/packages/frontend/src/pages/auth.vue @@ -1,12 +1,12 @@ <template> <MkStickyContainer> - <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template> + <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="500"> <div v-if="state == 'fetch-session-error'"> <p>{{ i18n.ts.somethingHappened }}</p> </div> <div v-else-if="$i && !session"> - <MkLoading /> + <MkLoading/> </div> <div v-else-if="$i && session"> <XForm @@ -21,15 +21,16 @@ </div> <div v-if="state == 'accepted' && session"> <h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1> - <p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }} - <MkEllipsis /> + <p v-if="session.app.callbackUrl"> + {{ i18n.ts._auth.callback }} + <MkEllipsis/> </p> <p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p> </div> </div> <div v-else> <p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p> - <MkSignin @login="onLogin" /> + <MkSignin @login="onLogin"/> </div> </MkSpacer> </MkStickyContainer> @@ -37,12 +38,12 @@ <script lang="ts" setup> import { onMounted } from 'vue'; +import { AuthSession } from 'misskey-js/built/entities'; import XForm from './auth.form.vue'; import MkSignin from '@/components/MkSignin.vue'; import * as os from '@/os'; import { $i, login } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { AuthSession } from 'misskey-js/built/entities'; import { i18n } from '@/i18n'; const props = defineProps<{ @@ -82,7 +83,7 @@ onMounted(async () => { } else { state = 'waiting'; } - } catch (e) { + } catch (err) { state = 'fetch-session-error'; } }); diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue index df8417e4ad2f4a1dabf439202aa5017f88c5dad3..38c5b1e082dbee03633f9ca42940c3ecb7b74cf9 100644 --- a/packages/frontend/src/pages/channel-editor.vue +++ b/packages/frontend/src/pages/channel-editor.vue @@ -27,7 +27,7 @@ </template> <script lang="ts" setup> -import { computed, inject, watch } from 'vue'; +import { computed, watch } from 'vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index 0fb33e30f713afd8c7d43176fc95ca47de34bbf8..0e6d0e269153efcdffba8f03bed37c51922a9aa3 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -25,17 +25,16 @@ <MkPostForm v-if="$i" :channel="channel" class="post-form _panel _margin" fixed/> - <XTimeline :key="channelId" class="_margin" src="channel" :channel="channelId" @before="before" @after="after"/> + <MkTimeline :key="channelId" class="_margin" src="channel" :channel="channelId" @before="before" @after="after"/> </div> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> -import { computed, inject, watch } from 'vue'; -import MkContainer from '@/components/MkContainer.vue'; +import { computed, watch } from 'vue'; import MkPostForm from '@/components/MkPostForm.vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import XChannelFollowButton from '@/components/MkChannelFollowButton.vue'; import * as os from '@/os'; import { useRouter } from '@/router'; diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue index 9043d06c52b99aa78cefa57c0cd9f63b8890d88b..3550c7f43691e62655343610ce2997ab46861b7d 100644 --- a/packages/frontend/src/pages/channels.vue +++ b/packages/frontend/src/pages/channels.vue @@ -23,7 +23,7 @@ </template> <script lang="ts" setup> -import { computed, defineComponent, inject } from 'vue'; +import { computed } from 'vue'; import MkChannelPreview from '@/components/MkChannelPreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue index 082a303e6fc1e3ea007eef2dea2c2a6ae61bc1e9..24eae32e1310729b8597070a8c768122c89d2b72 100644 --- a/packages/frontend/src/pages/clicker.vue +++ b/packages/frontend/src/pages/clicker.vue @@ -8,9 +8,7 @@ </template> <script lang="ts" setup> -import { ref } from 'vue'; import MkClickerGame from '@/components/MkClickerGame.vue'; -import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; definePageMetadata({ diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index cd9cec0d4f16a26963257088e03eefac09721f4a..d4e8f27005d71291f3fb9a960b94b24bf23dd404 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -12,7 +12,7 @@ </div> </div> - <XNotes :pagination="pagination" :detail="true"/> + <MkNotes :pagination="pagination" :detail="true"/> </div> </MkSpacer> </MkStickyContainer> @@ -21,7 +21,7 @@ <script lang="ts" setup> import { computed, watch, provide } from 'vue'; import * as misskey from 'misskey-js'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 87d205ed780aa878b4957e684182e559087ebfc9..59cb3262b785b7bae2aea6c6592bf4927e7b5a56 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -68,11 +68,10 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, defineComponent, ref, shallowRef } from 'vue'; +import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkPagination from '@/components/MkPagination.vue'; -import MkTab from '@/components/MkTab.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import FormSplit from '@/components/form/split.vue'; import { selectFile, selectFiles } from '@/scripts/select-file'; diff --git a/packages/frontend/src/pages/drive.vue b/packages/frontend/src/pages/drive.vue index 04ade5c20751bd56b2a1e42c8de819832858802f..0b398684ca37db0ca5da0bd0780f147b58df900e 100644 --- a/packages/frontend/src/pages/drive.vue +++ b/packages/frontend/src/pages/drive.vue @@ -7,7 +7,6 @@ <script lang="ts" setup> import { computed } from 'vue'; import XDrive from '@/components/MkDrive.vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 4d84ed7f1643251e26efb6d4e23e73457866d0ca..9be30f76a04e0eb2ad07ed271cad69efd0682e69 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -34,7 +34,6 @@ import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import * as os from '@/os'; -import { unique } from '@/scripts/array'; import { i18n } from '@/i18n'; import { customEmojiCategories } from '@/custom-emojis'; diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index 18a371a0863ac42d8f5354dca7861f167bbd81b1..a972ae04ec32926332dbd4e62eed330eda9925eb 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -4,13 +4,13 @@ <option value="notes">{{ i18n.ts.notes }}</option> <option value="polls">{{ i18n.ts.poll }}</option> </MkTab> - <XNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/> - <XNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/> + <MkNotes v-if="tab === 'notes'" :pagination="paginationForNotes"/> + <MkNotes v-else-if="tab === 'polls'" :pagination="paginationForPolls"/> </MkSpacer> </template> <script lang="ts" setup> -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import MkTab from '@/components/MkTab.vue'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue new file mode 100644 index 0000000000000000000000000000000000000000..8be11008c27c2bde69fd19665155c515a64ffa52 --- /dev/null +++ b/packages/frontend/src/pages/explore.roles.vue @@ -0,0 +1,22 @@ +<template> +<MkSpacer :content-max="1200"> + <div class="_gaps_s"> + <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="false"/> + </div> +</MkSpacer> +</template> + +<script lang="ts" setup> +import { } from 'vue'; +import MkRolePreview from '@/components/MkRolePreview.vue'; +import * as os from '@/os'; + +let roles = $ref(); + +os.api('roles/list', { + limit: 30, +}).then(res => { + roles = res; +}); +</script> + diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue index 3a74e8518d32a9cda8979ec16ab2e0c4f566eb1d..c441407d9797bb69f3eeaf1cf723b8a04056c092 100644 --- a/packages/frontend/src/pages/explore.users.vue +++ b/packages/frontend/src/pages/explore.users.vue @@ -7,20 +7,20 @@ <div v-if="origin === 'local'"> <template v-if="tag == null"> <MkFoldableSection class="_margin" persist-key="explore-pinned-users"> - <template #header><i class="fas fa-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template> - <XUserList :pagination="pinnedUsers"/> + <template #header><i class="ti ti-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template> + <MkUserList :pagination="pinnedUsers"/> </MkFoldableSection> <MkFoldableSection class="_margin" persist-key="explore-popular-users"> - <template #header><i class="fas fa-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> - <XUserList :pagination="popularUsers"/> + <template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> + <MkUserList :pagination="popularUsers"/> </MkFoldableSection> <MkFoldableSection class="_margin" persist-key="explore-recently-updated-users"> - <template #header><i class="fas fa-comment-alt ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsers"/> + <template #header><i class="ti ti-message ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> + <MkUserList :pagination="recentlyUpdatedUsers"/> </MkFoldableSection> <MkFoldableSection class="_margin" persist-key="explore-recently-registered-users"> <template #header><i class="ti ti-plus ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsers"/> + <MkUserList :pagination="recentlyRegisteredUsers"/> </MkFoldableSection> </template> </div> @@ -29,28 +29,28 @@ <template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template> <div class="vxjfqztj"> - <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/explore/tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA> - <MkA v-for="tag in tagsRemote" :key="'remote:' + tag.tag" :to="`/explore/tags/${tag.tag}`">{{ tag.tag }}</MkA> + <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/user-tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA> + <MkA v-for="tag in tagsRemote" :key="'remote:' + tag.tag" :to="`/user-tags/${tag.tag}`">{{ tag.tag }}</MkA> </div> </MkFoldableSection> <MkFoldableSection v-if="tag != null" :key="`${tag}`" class="_margin"> <template #header><i class="ti ti-hash ti-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> - <XUserList :pagination="tagUsers"/> + <MkUserList :pagination="tagUsers"/> </MkFoldableSection> <template v-if="tag == null"> <MkFoldableSection class="_margin"> - <template #header><i class="fas fa-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> - <XUserList :pagination="popularUsersF"/> + <template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> + <MkUserList :pagination="popularUsersF"/> </MkFoldableSection> <MkFoldableSection class="_margin"> - <template #header><i class="fas fa-comment-alt ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsersF"/> + <template #header><i class="ti ti-message ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> + <MkUserList :pagination="recentlyUpdatedUsersF"/> </MkFoldableSection> <MkFoldableSection class="_margin"> - <template #header><i class="fas fa-rocket ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsersF"/> + <template #header><i class="ti ti-rocket ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template> + <MkUserList :pagination="recentlyRegisteredUsersF"/> </MkFoldableSection> </template> </div> @@ -58,14 +58,12 @@ </template> <script lang="ts" setup> -import { computed, watch } from 'vue'; -import XUserList from '@/components/MkUserList.vue'; +import { watch } from 'vue'; +import MkUserList from '@/components/MkUserList.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkTab from '@/components/MkTab.vue'; -import number from '@/filters/number'; import * as os from '@/os'; import { i18n } from '@/i18n'; -import { instance } from '@/instance'; const props = defineProps<{ tag?: string; diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue index 57df58bbb8378b12e8f6f542c9286b2b7d428b8c..0ed0a7ebc2ad52bf2504d9e48673843b0dc7a7b8 100644 --- a/packages/frontend/src/pages/explore.vue +++ b/packages/frontend/src/pages/explore.vue @@ -8,6 +8,9 @@ <div v-else-if="tab === 'users'"> <XUsers/> </div> + <div v-else-if="tab === 'roles'"> + <XRoles/> + </div> <div v-else-if="tab === 'search'"> <MkSpacer :content-max="1200"> <div> @@ -22,7 +25,7 @@ </MkRadios> </div> - <XUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/> + <MkUserList v-if="searchQuery" ref="searchEl" class="_margin" :pagination="searchPagination"/> </MkSpacer> </div> </div> @@ -33,15 +36,13 @@ import { computed, watch } from 'vue'; import XFeatured from './explore.featured.vue'; import XUsers from './explore.users.vue'; +import XRoles from './explore.roles.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkInput from '@/components/MkInput.vue'; import MkRadios from '@/components/MkRadios.vue'; -import number from '@/filters/number'; -import * as os from '@/os'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; -import { instance } from '@/instance'; -import XUserList from '@/components/MkUserList.vue'; +import MkUserList from '@/components/MkUserList.vue'; const props = withDefaults(defineProps<{ tag?: string; @@ -78,8 +79,13 @@ const headerTabs = $computed(() => [{ key: 'users', icon: 'ti ti-users', title: i18n.ts.users, +}, { + key: 'roles', + icon: 'ti ti-badges', + title: i18n.ts.roles, }, { key: 'search', + icon: 'ti ti-search', title: i18n.ts.search, }]); diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue index 0bbed411b1a8f6fc9d666537e2d0f10c03c228eb..07dd7684999fdb727df69979a4206e425fb4dba1 100644 --- a/packages/frontend/src/pages/favorites.vue +++ b/packages/frontend/src/pages/favorites.vue @@ -21,7 +21,6 @@ </template> <script lang="ts" setup> -import { ref } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import XNote from '@/components/MkNote.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 734c467e3be9b55258be6c6bfd7c1209b62d4f6a..2b7fcf74e1eec3fa3de01b293db6ded7a9cbc3d4 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -24,10 +24,9 @@ </template> <script lang="ts" setup> -import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue'; +import { computed } from 'vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; -import { url } from '@/config'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import MkTextarea from '@/components/MkTextarea.vue'; @@ -174,6 +173,119 @@ var cursor = 0 do() `; +const PRESET_QUIZ = `/// @ 0.12.4 +let title = '地ç†ã‚¯ã‚¤ã‚º' + +let qas = [{ + q: 'オーストラリアã®é¦–都ã¯ï¼Ÿ' + choices: ['シドニー' 'ã‚ャンベラ' 'メルボルン'] + a: 'ã‚ャンベラ' + aDescription: '最大ã®éƒ½å¸‚ã¯ã‚·ãƒ‰ãƒ‹ãƒ¼ã§ã™ãŒé¦–都ã¯ã‚ャンベラã§ã™ã€‚' +} { + q: '国土é¢ç©2番目ã®å›½ã¯ï¼Ÿ' + choices: ['カナダ' 'アメリカ' 'ä¸å›½'] + a: 'カナダ' + aDescription: '大ãã„é †ã«ãƒã‚·ã‚¢ã€ã‚«ãƒŠãƒ€ã€ã‚¢ãƒ¡ãƒªã‚«ã€ä¸å›½ã§ã™ã€‚' +} { + q: '二é‡å†…陸国ã§ã¯ãªã„ã®ã¯ï¼Ÿ' + choices: ['リヒテンシュタイン' 'ウズベã‚スタン' 'レソト'] + a: 'レソト' + aDescription: 'レソトã¯(一é‡)内陸国ã§ã™ã€‚' +} { + q: '閘門ãŒãªã„é‹æ²³ã¯ï¼Ÿ' + choices: ['ã‚ールé‹æ²³' 'スエズé‹æ²³' 'パナマé‹æ²³'] + a: 'スエズé‹æ²³' + aDescription: 'スエズé‹æ²³ã¯é«˜ä½Žå·®ãŒãªã„ã®ã§é–˜é–€ã¯ã‚ã‚Šã¾ã›ã‚“。' +}] + +let qaEls = [Ui:C:container({ + align: 'center' + children: [ + Ui:C:text({ + size: 1.5 + bold: true + text: title + }) + ] +})] + +var qn = 0 +each (let qa, qas) { + qn += 1 + qa.id = Util:uuid() + qaEls.push(Ui:C:container({ + align: 'center' + bgColor: '#000' + fgColor: '#fff' + padding: 16 + rounded: true + children: [ + Ui:C:text({ + text: \`Q{qn} {qa.q}\` + }) + Ui:C:select({ + items: qa.choices.map(@(c) {{ text: c, value: c }}) + onChange: @(v) { qa.userAnswer = v } + }) + Ui:C:container({ + children: [] + } \`{qa.id}:a\`) + ] + } qa.id)) +} + +@finish() { + var score = 0 + + each (let qa, qas) { + let correct = qa.userAnswer == qa.a + if (correct) score += 1 + let el = Ui:get(\`{qa.id}:a\`) + el.update({ + children: [ + Ui:C:text({ + size: 1.2 + bold: true + color: if (correct) '#f00' else '#00f' + text: if (correct) '🎉æ£è§£' else 'ä¸æ£è§£' + }) + Ui:C:text({ + text: qa.aDescription + }) + ] + }) + } + + let result = \`{title}ã®çµæžœã¯{qas.len}å•ä¸{score}å•æ£è§£ã§ã—ãŸã€‚\` + Ui:get('footer').update({ + children: [ + Ui:C:postFormButton({ + text: 'çµæžœã‚’共有' + rounded: true + primary: true + form: { + text: \`{result}{Str:lf}{THIS_URL}\` + } + }) + ] + }) +} + +qaEls.push(Ui:C:container({ + align: 'center' + children: [ + Ui:C:button({ + text: 'ç”ãˆåˆã‚ã›' + primary: true + rounded: true + onClick: finish + }) + ] +} 'footer')) + +Ui:render(qaEls) +`; + const PRESET_TIMELINE = `/// @ 0.12.4 // APIリクエストを行ã„ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインを表示ã™ã‚‹ãƒ—リセット @@ -259,6 +371,11 @@ function selectPreset(ev: MouseEvent) { action: () => { script = PRESET_SHUFFLE; }, + }, { + text: 'Quiz', + action: () => { + script = PRESET_QUIZ; + }, }, { text: 'Timeline viewer', action: () => { diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index a3a48d3b97526b4e2b7e9a1065af07b92081b1cc..f1dca5f240726ea1ba59c71c382cfffe23407fba 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -33,7 +33,7 @@ </template> <script lang="ts" setup> -import { computed, inject } from 'vue'; +import { computed } from 'vue'; import MkFlashPreview from '@/components/MkFlashPreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue index c82559d55a3572b8fec8570d082cb1e479930067..3528e7e1459927bbf89b7cd40e6bf4776dcb6ca3 100644 --- a/packages/frontend/src/pages/flash/flash.vue +++ b/packages/frontend/src/pages/flash/flash.vue @@ -52,18 +52,14 @@ <script lang="ts" setup> import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue'; -import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; +import { Interpreter, Parser, values } from '@syuilo/aiscript'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { url } from '@/config'; -import MkFollowButton from '@/components/MkFollowButton.vue'; -import MkContainer from '@/components/MkContainer.vue'; -import MkPagination from '@/components/MkPagination.vue'; -import MkPagePreview from '@/components/MkPagePreview.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import MkAsUi from '@/components/MkAsUi.vue'; -import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; +import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui'; import { createAiScriptEnv } from '@/scripts/aiscript/api'; import MkFolder from '@/components/MkFolder.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue index f47632c85f90a8ddf110c6a2956bb9f9d6325368..1fae7686e5f95fbbc8976f110eac81a98bbdea21 100644 --- a/packages/frontend/src/pages/gallery/edit.vue +++ b/packages/frontend/src/pages/gallery/edit.vue @@ -31,7 +31,7 @@ </template> <script lang="ts" setup> -import { computed, inject, watch } from 'vue'; +import { computed, watch } from 'vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue index 1001c3b2e792e85980bf40fb223c3c2abe19df9e..de8f448da165e4008bd83721bf01c4800be2b1d5 100644 --- a/packages/frontend/src/pages/gallery/index.vue +++ b/packages/frontend/src/pages/gallery/index.vue @@ -42,16 +42,10 @@ </template> <script lang="ts" setup> -import { computed, defineComponent, watch } from 'vue'; -import XUserList from '@/components/MkUserList.vue'; +import { watch } from 'vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkButton from '@/components/MkButton.vue'; -import MkTab from '@/components/MkTab.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; -import number from '@/filters/number'; -import * as os from '@/os'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; import { useRouter } from '@/router'; diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index 31e2727bb57d74ccaeb01afb556bc974f2ff10c7..4bf7c8c514da003fd214d1393cb334c7476257d5 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -47,7 +47,7 @@ </MkPagination> </MkContainer> </div> - <MkError v-else-if="error" @retry="fetch()"/> + <MkError v-else-if="error" @retry="fetchPost()"/> <MkLoading v-else/> </Transition> </div> @@ -56,11 +56,10 @@ </template> <script lang="ts" setup> -import { computed, defineComponent, inject, watch } from 'vue'; +import { computed, watch } from 'vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; -import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 714f95add98dd45ac294c7cc01b74c7a37bbeaeb..ba5fda137a4c978e490e6eff08db6e20d3e1bfe3 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -124,7 +124,6 @@ import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os'; import number from '@/filters/number'; -import bytes from '@/filters/bytes'; import { iAmModerator } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/messaging/index.vue b/packages/frontend/src/pages/messaging/index.vue deleted file mode 100644 index 3d11cf13e9322a0c178fcd6659262e615d62139f..0000000000000000000000000000000000000000 --- a/packages/frontend/src/pages/messaging/index.vue +++ /dev/null @@ -1,305 +0,0 @@ -<template> -<MkStickyContainer> - <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :content-max="800"> - <div class="yweeujhr"> - <MkButton primary class="start" @click="start"><i class="ti ti-plus"></i> {{ $ts.startMessaging }}</MkButton> - - <div v-if="messages.length > 0" class="history"> - <MkA - v-for="(message, i) in messages" - :key="message.id" - v-anim="i" - class="message _panel" - :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }" - :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" - :data-index="i" - > - <div> - <MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" indicator link preview/> - <header v-if="message.groupId"> - <span class="name">{{ message.group.name }}</span> - <MkTime :time="message.createdAt" class="time"/> - </header> - <header v-else> - <span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span> - <span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span> - <MkTime :time="message.createdAt" class="time"/> - </header> - <div class="body"> - <p class="text"><span v-if="isMe(message)" class="me">{{ $ts.you }}:</span>{{ message.text }}</p> - </div> - </div> - </MkA> - </div> - <div v-if="!fetching && messages.length == 0" class="_fullinfo"> - <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> - <div>{{ $ts.noHistory }}</div> - </div> - <MkLoading v-if="fetching"/> - </div> - </MkSpacer> -</MkStickyContainer> -</template> - -<script lang="ts" setup> -import { defineAsyncComponent, defineComponent, inject, markRaw, onMounted, onUnmounted } from 'vue'; -import * as Acct from 'misskey-js/built/acct'; -import MkButton from '@/components/MkButton.vue'; -import { acct } from '@/filters/user'; -import * as os from '@/os'; -import { stream } from '@/stream'; -import { useRouter } from '@/router'; -import { i18n } from '@/i18n'; -import { definePageMetadata } from '@/scripts/page-metadata'; -import { $i } from '@/account'; - -const router = useRouter(); - -let fetching = $ref(true); -let moreFetching = $ref(false); -let messages = $ref([]); -let connection = $ref(null); - -const getAcct = Acct.toString; - -function isMe(message) { - return message.userId === $i.id; -} - -function onMessage(message) { - if (message.recipientId) { - messages = messages.filter(m => !( - (m.recipientId === message.recipientId && m.userId === message.userId) || - (m.recipientId === message.userId && m.userId === message.recipientId))); - - messages.unshift(message); - } else if (message.groupId) { - messages = messages.filter(m => m.groupId !== message.groupId); - messages.unshift(message); - } -} - -function onRead(ids) { - for (const id of ids) { - const found = messages.find(m => m.id === id); - if (found) { - if (found.recipientId) { - found.isRead = true; - } else if (found.groupId) { - found.reads.push($i.id); - } - } - } -} - -function start(ev) { - os.popupMenu([{ - text: i18n.ts.messagingWithUser, - icon: 'ti ti-user', - action: () => { startUser(); }, - }, { - text: i18n.ts.messagingWithGroup, - icon: 'ti ti-users', - action: () => { startGroup(); }, - }], ev.currentTarget ?? ev.target); -} - -async function startUser() { - os.selectUser().then(user => { - router.push(`/my/messaging/${Acct.toString(user)}`); - }); -} - -async function startGroup() { - const groups1 = await os.api('users/groups/owned'); - const groups2 = await os.api('users/groups/joined'); - if (groups1.length === 0 && groups2.length === 0) { - os.alert({ - type: 'warning', - title: i18n.ts.youHaveNoGroups, - text: i18n.ts.joinOrCreateGroup, - }); - return; - } - const { canceled, result: group } = await os.select({ - title: i18n.ts.group, - items: groups1.concat(groups2).map(group => ({ - value: group, text: group.name, - })), - }); - if (canceled) return; - router.push(`/my/messaging/group/${group.id}`); -} - -onMounted(() => { - connection = markRaw(stream.useChannel('messagingIndex')); - - connection.on('message', onMessage); - connection.on('read', onRead); - - os.api('messaging/history', { group: false }).then(userMessages => { - os.api('messaging/history', { group: true }).then(groupMessages => { - const _messages = userMessages.concat(groupMessages); - _messages.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); - messages = _messages; - fetching = false; - }); - }); -}); - -onUnmounted(() => { - if (connection) connection.dispose(); -}); - -const headerActions = $computed(() => []); - -const headerTabs = $computed(() => []); - -definePageMetadata({ - title: i18n.ts.messaging, - icon: 'ti ti-messages', -}); -</script> - -<style lang="scss" scoped> -.yweeujhr { - - > .start { - margin: 0 auto var(--margin) auto; - } - - > .history { - > .message { - display: block; - text-decoration: none; - margin-bottom: var(--margin); - - * { - pointer-events: none; - user-select: none; - } - - &:hover { - .avatar { - filter: saturate(200%); - } - } - - &:active { - } - - &.isRead, - &.isMe { - opacity: 0.8; - } - - &:not(.isMe):not(.isRead) { - > div { - background-image: url("/client-assets/unread.svg"); - background-repeat: no-repeat; - background-position: 0 center; - } - } - - &:after { - content: ""; - display: block; - clear: both; - } - - > div { - padding: 20px 30px; - - &:after { - content: ""; - display: block; - clear: both; - } - - > header { - display: flex; - align-items: center; - margin-bottom: 2px; - white-space: nowrap; - overflow: hidden; - - > .name { - margin: 0; - padding: 0; - overflow: hidden; - text-overflow: ellipsis; - font-size: 1em; - font-weight: bold; - transition: all 0.1s ease; - } - - > .username { - margin: 0 8px; - } - - > .time { - margin: 0 0 0 auto; - } - } - - > .avatar { - float: left; - width: 54px; - height: 54px; - margin: 0 16px 0 0; - border-radius: 8px; - transition: all 0.1s ease; - } - - > .body { - - > .text { - display: block; - margin: 0 0 0 0; - padding: 0; - overflow: hidden; - overflow-wrap: break-word; - font-size: 1.1em; - color: var(--faceText); - - .me { - opacity: 0.7; - } - } - - > .image { - display: block; - max-width: 100%; - max-height: 512px; - } - } - } - } - } -} - -@container (max-width: 400px) { - .yweeujhr { - > .history { - > .message { - &:not(.isMe):not(.isRead) { - > div { - background-image: none; - border-left: solid 4px #3aa2dc; - } - } - - > div { - padding: 16px; - font-size: 0.9em; - - > .avatar { - margin: 0 12px 0 0; - } - } - } - } - } -} -</style> diff --git a/packages/frontend/src/pages/messaging/messaging-room.form.vue b/packages/frontend/src/pages/messaging/messaging-room.form.vue deleted file mode 100644 index d6113668dd015b29521cf4dae67b1fd834f1037a..0000000000000000000000000000000000000000 --- a/packages/frontend/src/pages/messaging/messaging-room.form.vue +++ /dev/null @@ -1,366 +0,0 @@ -<template> -<div - :class="$style['root']" - @dragover.stop="onDragover" - @drop.stop="onDrop" -> - <textarea - :class="$style['textarea']" - class="_acrylic" - ref="textEl" - v-model="text" - :placeholder="i18n.ts.inputMessageHere" - @keydown="onKeydown" - @compositionupdate="onCompositionUpdate" - @paste="onPaste" - ></textarea> - <footer :class="$style['footer']"> - <div v-if="file" :class="$style['file']" @click="file = null">{{ file.name }}</div> - <div :class="$style['buttons']"> - <button class="_button" :class="$style['button']" @click="chooseFile"><i class="ti ti-photo-plus"></i></button> - <button class="_button" :class="$style['button']" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> - <button class="_button" :class="[$style['button'], $style['send']]" :disabled="!canSend || sending" :title="i18n.ts.send" @click="send"> - <template v-if="!sending"><i class="ti ti-send"></i></template><template v-if="sending"><MkLoading :em="true"/></template> - </button> - </div> - </footer> - <input :class="$style['file-input']" ref="fileEl" type="file" @change="onChangeFile"/> -</div> -</template> - -<script lang="ts" setup> -import { onMounted, watch } from 'vue'; -import * as Misskey from 'misskey-js'; -import autosize from 'autosize'; -//import insertTextAtCursor from 'insert-text-at-cursor'; -import { throttle } from 'throttle-debounce'; -import { formatTimeString } from '@/scripts/format-time-string'; -import { selectFile } from '@/scripts/select-file'; -import * as os from '@/os'; -import { stream } from '@/stream'; -import { defaultStore } from '@/store'; -import { i18n } from '@/i18n'; -//import { Autocomplete } from '@/scripts/autocomplete'; -import { uploadFile } from '@/scripts/upload'; -import { miLocalStorage } from '@/local-storage'; - -const props = defineProps<{ - user?: Misskey.entities.UserDetailed | null; - group?: Misskey.entities.UserGroup | null; -}>(); - -let textEl = $shallowRef<HTMLTextAreaElement>(); -let fileEl = $shallowRef<HTMLInputElement>(); - -let text = $ref<string>(''); -let file = $ref<Misskey.entities.DriveFile | null>(null); -let sending = $ref(false); -const typing = throttle(3000, () => { - stream.send('typingOnMessaging', props.user ? { partner: props.user.id } : { group: props.group?.id }); -}); - -let draftKey = $computed(() => props.user ? 'user:' + props.user.id : 'group:' + props.group?.id); -let canSend = $computed(() => (text != null && text !== '') || file != null); - -watch([$$(text), $$(file)], saveDraft); - -async function onPaste(ev: ClipboardEvent) { - if (!ev.clipboardData) return; - - const clipboardData = ev.clipboardData; - const items = clipboardData.items; - - if (items.length === 1) { - if (items[0].kind === 'file') { - const pastedFile = items[0].getAsFile(); - if (!pastedFile) return; - const lio = pastedFile.name.lastIndexOf('.'); - const ext = lio >= 0 ? pastedFile.name.slice(lio) : ''; - const formatted = formatTimeString(new Date(pastedFile.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, '1') + ext; - if (formatted) upload(pastedFile, formatted); - } - } else { - if (items[0].kind === 'file') { - os.alert({ - type: 'error', - text: i18n.ts.onlyOneFileCanBeAttached, - }); - } - } -} - -function onDragover(ev: DragEvent) { - if (!ev.dataTransfer) return; - - const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; - if (isFile || isDriveFile) { - ev.preventDefault(); - switch (ev.dataTransfer.effectAllowed) { - case 'all': - case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': - ev.dataTransfer.dropEffect = 'copy'; - break; - case 'linkMove': - case 'move': - ev.dataTransfer.dropEffect = 'move'; - break; - default: - ev.dataTransfer.dropEffect = 'none'; - break; - } - } -} - -function onDrop(ev: DragEvent): void { - if (!ev.dataTransfer) return; - - // ファイルã ã£ãŸã‚‰ - if (ev.dataTransfer.files.length === 1) { - ev.preventDefault(); - upload(ev.dataTransfer.files[0]); - return; - } else if (ev.dataTransfer.files.length > 1) { - ev.preventDefault(); - os.alert({ - type: 'error', - text: i18n.ts.onlyOneFileCanBeAttached, - }); - return; - } - - //#region ドライブã®ãƒ•ã‚¡ã‚¤ãƒ« - const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); - if (driveFile != null && driveFile !== '') { - file = JSON.parse(driveFile); - ev.preventDefault(); - } - //#endregion -} - -function onKeydown(ev: KeyboardEvent) { - typing(); - if ((ev.key === 'Enter') && (ev.ctrlKey || ev.metaKey) && canSend) { - send(); - } -} - -function onCompositionUpdate() { - typing(); -} - -function chooseFile(ev: MouseEvent) { - selectFile(ev.currentTarget ?? ev.target, i18n.ts.selectFile).then(selectedFile => { - file = selectedFile; - }); -} - -function onChangeFile() { - if (fileEl.files![0]) upload(fileEl.files[0]); -} - -function upload(fileToUpload: File, name?: string) { - uploadFile(fileToUpload, defaultStore.state.uploadFolder, name).then(res => { - file = res; - }); -} - -function send() { - sending = true; - os.api('messaging/messages/create', { - userId: props.user ? props.user.id : undefined, - groupId: props.group ? props.group.id : undefined, - text: text ? text : undefined, - fileId: file ? file.id : undefined, - }).then(message => { - clear(); - }).catch(err => { - console.error(err); - }).then(() => { - sending = false; - }); -} - -function clear() { - text = ''; - file = null; - deleteDraft(); -} - -function saveDraft() { - const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}'); - - drafts[draftKey] = { - updatedAt: new Date(), - // eslint-disable-next-line id-denylist - data: { - text: text, - file: file, - }, - }; - - miLocalStorage.setItem('message_drafts', JSON.stringify(drafts)); -} - -function deleteDraft() { - const drafts = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}'); - - delete drafts[draftKey]; - - miLocalStorage.setItem('message_drafts', JSON.stringify(drafts)); -} - -async function insertEmoji(ev: MouseEvent) { - os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textEl); -} - -onMounted(() => { - autosize(textEl); - - // TODO: detach when unmount - // TODO - //new Autocomplete(textEl, this, { model: 'text' }); - - // 書ãã‹ã‘ã®æŠ•ç¨¿ã‚’復元 - const draft = JSON.parse(miLocalStorage.getItem('message_drafts') || '{}')[draftKey]; - if (draft) { - text = draft.data.text; - file = draft.data.file; - } -}); - -defineExpose({ - file, - upload, -}); -</script> - -<style lang="scss" module> -.root { - position: relative; -} - -.textarea { - cursor: auto; - display: block; - width: 100%; - min-width: 100%; - max-width: 100%; - min-height: 80px; - margin: 0; - padding: 16px 16px 0 16px; - resize: none; - font-size: 1em; - font-family: inherit; - outline: none; - border: none; - border-radius: 0; - box-shadow: none; - box-sizing: border-box; - color: var(--fg); -} - -.footer { - position: sticky; - bottom: 0; - background: var(--panel); -} - -.file { - padding: 8px; - color: var(--fg); - background: transparent; - cursor: pointer; -} -/* -.files { - display: block; - margin: 0; - padding: 0 8px; - list-style: none; - - &:after { - content: ''; - display: block; - clear: both; - } - - > li { - display: block; - float: left; - margin: 4px; - padding: 0; - width: 64px; - height: 64px; - background-color: #eee; - background-repeat: no-repeat; - background-position: center center; - background-size: cover; - cursor: move; - - &:hover { - > .remove { - display: block; - } - } - } -} - -.file-remove { - display: none; - position: absolute; - right: -6px; - top: -6px; - margin: 0; - padding: 0; - background: transparent; - outline: none; - border: none; - border-radius: 0; - box-shadow: none; - cursor: pointer; -} -*/ - -.buttons { - display: flex; -} - -.button { - margin: 0; - padding: 16px; - font-size: 1em; - font-weight: normal; - text-decoration: none; - transition: color 0.1s ease; - - &:hover { - color: var(--accent); - } - - &:active { - color: var(--accentDarken); - transition: color 0s ease; - } -} -.send { - margin-left: auto; - color: var(--accent); - - &:hover { - color: var(--accentLighten); - } - - &:active { - color: var(--accentDarken); - transition: color 0s ease; - } -} - -.file-input { - display: none; -} -</style> diff --git a/packages/frontend/src/pages/messaging/messaging-room.message.vue b/packages/frontend/src/pages/messaging/messaging-room.message.vue deleted file mode 100644 index d10798b92ef8aca8cac6a56aaab94478b0dd37e5..0000000000000000000000000000000000000000 --- a/packages/frontend/src/pages/messaging/messaging-room.message.vue +++ /dev/null @@ -1,338 +0,0 @@ -<template> -<div class="thvuemwp" :class="{ isMe }"> - <MkAvatar class="avatar" :user="message.user" indicator link preview/> - <div class="content"> - <div class="balloon" :class="{ noText: message.text == null }"> - <button v-if="isMe" class="delete-button" :title="$ts.delete" @click="del"> - <img src="/client-assets/remove.png" alt="Delete"/> - </button> - <div v-if="!message.isDeleted" class="content"> - <Mfm v-if="message.text" ref="text" class="text" :text="message.text" :i="$i"/> - <div v-if="message.file" class="file"> - <a :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name"> - <img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/> - <p v-else>{{ message.file.name }}</p> - </a> - </div> - </div> - <div v-else class="content"> - <p class="is-deleted">{{ $ts.deleted }}</p> - </div> - </div> - <div></div> - <MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/> - <footer> - <template v-if="isGroup"> - <span v-if="message.reads.length > 0" class="read">{{ $ts.messageRead }} {{ message.reads.length }}</span> - </template> - <template v-else> - <span v-if="isMe && message.isRead" class="read">{{ $ts.messageRead }}</span> - </template> - <MkTime :time="message.createdAt"/> - <template v-if="message.is_edited"><i class="ti ti-pencil"></i></template> - </footer> - </div> -</div> -</template> - -<script lang="ts" setup> -import { } from 'vue'; -import * as mfm from 'mfm-js'; -import * as Misskey from 'misskey-js'; -import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm'; -import MkUrlPreview from '@/components/MkUrlPreview.vue'; -import * as os from '@/os'; -import { $i } from '@/account'; - -const props = defineProps<{ - message: Misskey.entities.MessagingMessage; - isGroup?: boolean; -}>(); - -const isMe = $computed(() => props.message.userId === $i?.id); -const urls = $computed(() => props.message.text ? extractUrlFromMfm(mfm.parse(props.message.text)) : []); - -function del(): void { - os.api('messaging/messages/delete', { - messageId: props.message.id, - }); -} -</script> - -<style lang="scss" scoped> -.thvuemwp { - $me-balloon-color: var(--accent); - - position: relative; - background-color: transparent; - display: flex; - - > .avatar { - position: sticky; - top: calc(var(--stickyTop, 0px) + 16px); - display: block; - width: 54px; - height: 54px; - transition: all 0.1s ease; - } - - > .content { - min-width: 0; - - > .balloon { - position: relative; - display: inline-flex; - align-items: center; - padding: 0; - min-height: 38px; - border-radius: 16px; - max-width: 100%; - - &:before { - content: ""; - pointer-events: none; - display: block; - position: absolute; - top: 12px; - } - - & + * { - clear: both; - } - - &:hover { - > .delete-button { - display: block; - } - } - - > .delete-button { - display: none; - position: absolute; - z-index: 1; - top: -4px; - right: -4px; - margin: 0; - padding: 0; - cursor: pointer; - outline: none; - border: none; - border-radius: 0; - box-shadow: none; - background: transparent; - - > img { - vertical-align: bottom; - width: 16px; - height: 16px; - cursor: pointer; - } - } - - > .content { - max-width: 100%; - - > .is-deleted { - display: block; - margin: 0; - padding: 0; - overflow: hidden; - overflow-wrap: break-word; - font-size: 1em; - color: rgba(#000, 0.5); - } - - > .text { - display: block; - margin: 0; - padding: 12px 18px; - overflow: hidden; - overflow-wrap: break-word; - word-break: break-word; - font-size: 1em; - color: rgba(#000, 0.8); - - & + .file { - > a { - border-radius: 0 0 16px 16px; - } - } - } - - > .file { - > a { - display: block; - max-width: 100%; - border-radius: 16px; - overflow: hidden; - text-decoration: none; - - &:hover { - text-decoration: none; - - > p { - background: #ccc; - } - } - - > * { - display: block; - margin: 0; - width: 100%; - max-height: 512px; - object-fit: contain; - box-sizing: border-box; - } - - > p { - padding: 30px; - text-align: center; - color: #555; - background: #ddd; - } - } - } - } - } - - > footer { - display: block; - margin: 2px 0 0 0; - font-size: 0.65em; - - > .read { - margin: 0 8px; - } - - > i { - margin-left: 4px; - } - } - } - - &:not(.isMe) { - padding-left: var(--margin); - - > .content { - padding-left: 16px; - padding-right: 32px; - - > .balloon { - $color: var(--messageBg); - background: $color; - - &.noText { - background: transparent; - } - - &:not(.noText):before { - left: -14px; - border-top: solid 8px transparent; - border-right: solid 8px $color; - border-bottom: solid 8px transparent; - border-left: solid 8px transparent; - } - - > .content { - > .text { - color: var(--fg); - } - } - } - - > footer { - text-align: left; - } - } - } - - &.isMe { - flex-direction: row-reverse; - padding-right: var(--margin); - right: var(--margin); // 削除時ã«position: absoluteã«ãªã£ãŸã¨ãã«ä½¿ã† - - > .content { - padding-right: 16px; - padding-left: 32px; - text-align: right; - - > .balloon { - background: $me-balloon-color; - text-align: left; - - ::selection { - color: var(--accent); - background-color: #fff; - } - - &.noText { - background: transparent; - } - - &:not(.noText):before { - right: -14px; - left: auto; - border-top: solid 8px transparent; - border-right: solid 8px transparent; - border-bottom: solid 8px transparent; - border-left: solid 8px $me-balloon-color; - } - - > .content { - - > p.is-deleted { - color: rgba(#fff, 0.5); - } - - > .text { - &, ::v-deep(*) { - color: var(--fgOnAccent) !important; - } - } - } - } - - > footer { - text-align: right; - - > .read { - user-select: none; - } - } - } - } -} - -@container (max-width: 400px) { - .thvuemwp { - > .avatar { - width: 48px; - height: 48px; - } - - > .content { - > .balloon { - > .content { - > .text { - font-size: 0.9em; - } - } - } - } - } -} - -@container (max-width: 500px) { - .thvuemwp { - > .content { - > .balloon { - > .content { - > .text { - padding: 8px 16px; - } - } - } - } - } -} -</style> diff --git a/packages/frontend/src/pages/messaging/messaging-room.vue b/packages/frontend/src/pages/messaging/messaging-room.vue deleted file mode 100644 index 0867f003a386ce8c53fc0fd656df875fe7ee4d94..0000000000000000000000000000000000000000 --- a/packages/frontend/src/pages/messaging/messaging-room.vue +++ /dev/null @@ -1,415 +0,0 @@ -<template> -<MkStickyContainer> -<template #header> - <MkPageHeader /> -</template> -<div - ref="rootEl" - :class="$style['root']" - @dragover.prevent.stop="onDragover" - @drop.prevent.stop="onDrop" -> - <div :class="$style['body']"> - <MkPagination v-if="pagination" ref="pagingComponent" :key="userAcct || groupId" :pagination="pagination"> - <template #empty> - <div class="_fullinfo"> - <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> - <div>{{ i18n.ts.noMessagesYet }}</div> - </div> - </template> - <template #default="{ items: messages, fetching: pFetching }"> - <MkDateSeparatedList - v-if="messages.length > 0" - v-slot="{ item: message }" - :class="{ [$style['messages']]: true, 'deny-move-transition': pFetching }" - :items="messages" - direction="up" - reversed - > - <XMessage :key="message.id" :message="message" :is-group="group != null"/> - </MkDateSeparatedList> - </template> - </MkPagination> - </div> - <footer :class="$style['footer']"> - <div v-if="typers.length > 0" :class="$style['typers']"> - <I18n :src="i18n.ts.typingUsers" text-tag="span"> - <template #users> - <b v-for="typer in typers" :key="typer.id" :class="$style['user']">{{ typer.username }}</b> - </template> - </I18n> - <MkEllipsis/> - </div> - <Transition :name="animation ? 'fade' : ''"> - <div v-show="showIndicator" :class="$style['new-message']"> - <button class="_buttonPrimary" @click="onIndicatorClick" :class="$style['new-message-button']"> - <i class="fas ti-fw fa-arrow-circle-down" :class="$style['new-message-icon']"></i>{{ i18n.ts.newMessageExists }} - </button> - </div> - </Transition> - <XForm v-if="!fetching" ref="formEl" :user="user" :group="group" :class="$style['form']"/> - </footer> -</div> -</MkStickyContainer> -</template> - -<script lang="ts" setup> -import { computed, watch, onMounted, nextTick, onBeforeUnmount } from 'vue'; -import * as Misskey from 'misskey-js'; -import * as Acct from 'misskey-js/built/acct'; -import XMessage from './messaging-room.message.vue'; -import XForm from './messaging-room.form.vue'; -import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; -import MkPagination, { Paging } from '@/components/MkPagination.vue'; -import { isBottomVisible, onScrollBottom, scrollToBottom } from '@/scripts/scroll'; -import * as os from '@/os'; -import { stream } from '@/stream'; -import * as sound from '@/scripts/sound'; -import { i18n } from '@/i18n'; -import { $i } from '@/account'; -import { defaultStore } from '@/store'; -import { definePageMetadata } from '@/scripts/page-metadata'; - -const props = defineProps<{ - userAcct?: string; - groupId?: string; -}>(); - -let rootEl = $shallowRef<HTMLDivElement>(); -let formEl = $shallowRef<InstanceType<typeof XForm>>(); -let pagingComponent = $shallowRef<InstanceType<typeof MkPagination>>(); - -let fetching = $ref(true); -let user: Misskey.entities.UserDetailed | null = $ref(null); -let group: Misskey.entities.UserGroup | null = $ref(null); -let typers: Misskey.entities.User[] = $ref([]); -let connection: Misskey.ChannelConnection<Misskey.Channels['messaging']> | null = $ref(null); -let showIndicator = $ref(false); -const { - animation, -} = defaultStore.reactiveState; - -let pagination: Paging | null = $ref(null); - -watch([() => props.userAcct, () => props.groupId], () => { - if (connection) connection.dispose(); - fetch(); -}); - -async function fetch() { - fetching = true; - - if (props.userAcct) { - const acct = Acct.parse(props.userAcct); - user = await os.api('users/show', { username: acct.username, host: acct.host || undefined }); - group = null; - - pagination = { - endpoint: 'messaging/messages', - limit: 20, - params: { - userId: user.id, - }, - reversed: true, - pageEl: $$(rootEl).value, - }; - connection = stream.useChannel('messaging', { - otherparty: user.id, - }); - } else { - user = null; - group = await os.api('users/groups/show', { groupId: props.groupId }); - - pagination = { - endpoint: 'messaging/messages', - limit: 20, - params: { - groupId: group?.id, - }, - reversed: true, - pageEl: $$(rootEl).value, - }; - connection = stream.useChannel('messaging', { - group: group?.id, - }); - } - - connection.on('message', onMessage); - connection.on('read', onRead); - connection.on('deleted', onDeleted); - connection.on('typers', _typers => { - typers = _typers.filter(u => u.id !== $i?.id); - }); - - document.addEventListener('visibilitychange', onVisibilitychange); - - nextTick(() => { - pagingComponent.inited.then(() => { - thisScrollToBottom(); - }); - window.setTimeout(() => { - fetching = false; - }, 300); - }); -} - -function onDragover(ev: DragEvent) { - if (!ev.dataTransfer) return; - - const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; - - if (isFile || isDriveFile) { - switch (ev.dataTransfer.effectAllowed) { - case 'all': - case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': - ev.dataTransfer.dropEffect = 'copy'; - break; - case 'linkMove': - case 'move': - ev.dataTransfer.dropEffect = 'move'; - break; - default: - ev.dataTransfer.dropEffect = 'none'; - break; - } - } else { - ev.dataTransfer.dropEffect = 'none'; - } -} - -function onDrop(ev: DragEvent): void { - if (!ev.dataTransfer) return; - - // ファイルã ã£ãŸã‚‰ - if (ev.dataTransfer.files.length === 1) { - formEl.upload(ev.dataTransfer.files[0]); - return; - } else if (ev.dataTransfer.files.length > 1) { - os.alert({ - type: 'error', - text: i18n.ts.onlyOneFileCanBeAttached, - }); - return; - } - - //#region ドライブã®ãƒ•ã‚¡ã‚¤ãƒ« - const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); - if (driveFile != null && driveFile !== '') { - const file = JSON.parse(driveFile); - formEl.file = file; - } - //#endregion -} - -function onMessage(message) { - sound.play('chat'); - - const _isBottom = isBottomVisible(rootEl, 64); - - pagingComponent.prepend(message); - if (message.userId !== $i?.id && !document.hidden) { - connection?.send('read', { - id: message.id, - }); - } - - if (_isBottom) { - // Scroll to bottom - nextTick(() => { - thisScrollToBottom(); - }); - } else if (message.userId !== $i?.id) { - // Notify - notifyNewMessage(); - } -} - -function onRead(x) { - if (user) { - if (!Array.isArray(x)) x = [x]; - for (const id of x) { - if (pagingComponent.items.some(y => y.id === id)) { - const exist = pagingComponent.items.map(y => y.id).indexOf(id); - pagingComponent.items[exist] = { - ...pagingComponent.items[exist], - isRead: true, - }; - } - } - } else if (group) { - for (const id of x.ids) { - if (pagingComponent.items.some(y => y.id === id)) { - const exist = pagingComponent.items.map(y => y.id).indexOf(id); - pagingComponent.items[exist] = { - ...pagingComponent.items[exist], - reads: [...pagingComponent.items[exist].reads, x.userId], - }; - } - } - } -} - -function onDeleted(id) { - const msg = pagingComponent.items.find(m => m.id === id); - if (msg) { - pagingComponent.items = pagingComponent.items.filter(m => m.id !== msg.id); - } -} - -function thisScrollToBottom() { - scrollToBottom($$(rootEl).value, { behavior: 'smooth' }); -} - -function onIndicatorClick() { - showIndicator = false; - thisScrollToBottom(); -} - -let scrollRemove: (() => void) | null = $ref(null); - -function notifyNewMessage() { - showIndicator = true; - - scrollRemove = onScrollBottom(rootEl, () => { - showIndicator = false; - scrollRemove = null; - }); -} - -function onVisibilitychange() { - if (document.hidden) return; - for (const message of pagingComponent.items) { - if (message.userId !== $i?.id && !message.isRead) { - connection?.send('read', { - id: message.id, - }); - } - } -} - -onMounted(() => { - fetch(); -}); - -onBeforeUnmount(() => { - connection?.dispose(); - document.removeEventListener('visibilitychange', onVisibilitychange); - if (scrollRemove) scrollRemove(); -}); - -definePageMetadata(computed(() => !fetching ? user ? { - userName: user, - avatar: user, -} : { - title: group?.name, - icon: 'ti ti-users', -} : null)); -</script> - -<style lang="scss" module> -.root { - display: content; -} - -.body { - min-height: 80%; -} - -.more { - display: block; - margin: 16px auto; - padding: 0 12px; - line-height: 24px; - color: #fff; - background: rgba(#000, 0.3); - border-radius: 12px; - &:hover { - background: rgba(#000, 0.4); - } - &:active { - background: rgba(#000, 0.5); - } - > i { - margin-right: 4px; - } -} - -.fetching { - cursor: wait; -} - -.messages { - padding: 16px 0 0; - - > * { - margin-bottom: 16px; - } -} - -.footer { - width: 100%; - position: sticky; - z-index: 2; - padding-top: 8px; - bottom: var(--minBottomSpacing); -} - -.new-message { - width: 100%; - padding-bottom: 8px; - text-align: center; -} - -.new-message-button { - display: inline-block; - margin: 0; - padding: 0 12px; - line-height: 32px; - font-size: 12px; - border-radius: 16px; -} - -.new-message-icon { - display: inline-block; - margin-right: 8px; -} - -.typers { - position: absolute; - bottom: 100%; - padding: 0 8px 0 8px; - font-size: 0.9em; - color: var(--fgTransparentWeak); -} - - -.user + .user:before { - content: ", "; - font-weight: normal; -} - -.user:last-of-type:after { - content: " "; -} - -.form { - max-height: 12em; - overflow-y: scroll; - border-top: solid 0.5px var(--divider); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.fade-enter-active, .fade-leave-active { - transition: opacity 0.1s; -} - -.fade-enter-from, .fade-leave-to { - transition: opacity 0.5s; - opacity: 0; -} -</style> diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue index 005b036696c121d9ef53dc3858baeeb9dff5d0d9..c35af3e22a8c04ea84bfd090e9485d9ee2f75fe7 100644 --- a/packages/frontend/src/pages/my-antennas/create.vue +++ b/packages/frontend/src/pages/my-antennas/create.vue @@ -5,7 +5,6 @@ </template> <script lang="ts" setup> -import { inject } from 'vue'; import XAntenna from './editor.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; @@ -17,7 +16,6 @@ let draft = $ref({ name: '', src: 'all', userListId: null, - userGroupId: null, users: [], keywords: [], excludeKeywords: [], diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue index cb583faaeb53a035308ecad58d0c17b4aab3f2b0..913fbde8e95620fe1b1c51f60d899b1c9c964712 100644 --- a/packages/frontend/src/pages/my-antennas/edit.vue +++ b/packages/frontend/src/pages/my-antennas/edit.vue @@ -5,7 +5,6 @@ </template> <script lang="ts" setup> -import { inject, watch } from 'vue'; import XAntenna from './editor.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue index 5c1eb66947717b84ef2b5a42b292fe27c422cb83..26b7bcc71b6614e017a510cc6b075b588fcd392f 100644 --- a/packages/frontend/src/pages/my-antennas/editor.vue +++ b/packages/frontend/src/pages/my-antennas/editor.vue @@ -11,16 +11,11 @@ <!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>--> <option value="users">{{ i18n.ts._antennaSources.users }}</option> <!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>--> - <!--<option value="group">{{ i18n.ts._antennaSources.userGroup }}</option>--> </MkSelect> <MkSelect v-if="src === 'list'" v-model="userListId"> <template #label>{{ i18n.ts.userList }}</template> <option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option> </MkSelect> - <MkSelect v-else-if="src === 'group'" v-model="userGroupId"> - <template #label>{{ i18n.ts.userGroup }}</template> - <option v-for="group in userGroups" :key="group.id" :value="group.id">{{ group.name }}</option> - </MkSelect> <MkTextarea v-else-if="src === 'users'" v-model="users"> <template #label>{{ i18n.ts.users }}</template> <template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template> @@ -70,7 +65,6 @@ const emit = defineEmits<{ let name: string = $ref(props.antenna.name); let src: string = $ref(props.antenna.src); let userListId: any = $ref(props.antenna.userListId); -let userGroupId: any = $ref(props.antenna.userGroupId); let users: string = $ref(props.antenna.users.join('\n')); let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n')); let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n')); @@ -79,19 +73,11 @@ let withReplies: boolean = $ref(props.antenna.withReplies); let withFile: boolean = $ref(props.antenna.withFile); let notify: boolean = $ref(props.antenna.notify); let userLists: any = $ref(null); -let userGroups: any = $ref(null); watch(() => src, async () => { if (src === 'list' && userLists === null) { userLists = await os.api('users/lists/list'); } - - if (src === 'group' && userGroups === null) { - const groups1 = await os.api('users/groups/owned'); - const groups2 = await os.api('users/groups/joined'); - - userGroups = [...groups1, ...groups2]; - } }); async function saveAntenna() { @@ -99,7 +85,6 @@ async function saveAntenna() { name, src, userListId, - userGroupId, withReplies, withFile, notify, diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index 86b3fce3c52489d937fd8007f75b7a120bfe0f45..165e357ebdce14cbe4e4b9a2a02e3c1b13b2291b 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -6,7 +6,7 @@ <Transition :name="$store.state.animation ? 'fade' : ''" mode="out-in"> <div v-if="note" class="note"> <div v-if="showNext" class="_margin"> - <XNotes class="" :pagination="nextPagination" :no-gap="true"/> + <MkNotes class="" :pagination="nextPagination" :no-gap="true"/> </div> <div class="main _margin"> @@ -29,10 +29,10 @@ </div> <div v-if="showPrev" class="_margin"> - <XNotes class="" :pagination="prevPagination" :no-gap="true"/> + <MkNotes class="" :pagination="prevPagination" :no-gap="true"/> </div> </div> - <MkError v-else-if="error" @retry="fetch()"/> + <MkError v-else-if="error" @retry="fetchNote()"/> <MkLoading v-else/> </Transition> </div> @@ -41,11 +41,10 @@ </template> <script lang="ts" setup> -import { computed, defineComponent, watch } from 'vue'; +import { computed, watch } from 'vue'; import * as misskey from 'misskey-js'; -import XNote from '@/components/MkNote.vue'; import XNoteDetailed from '@/components/MkNoteDetailed.vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import MkRemoteCaution from '@/components/MkRemoteCaution.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 7106951de28492732792066a2fae001f9a326745..0fcf0f65c421305051ae98db42cf877d87b332c4 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -6,10 +6,10 @@ <XNotifications class="notifications" :include-types="includeTypes" :unread-only="unreadOnly"/> </div> <div v-else-if="tab === 'mentions'"> - <XNotes :pagination="mentionsPagination"/> + <MkNotes :pagination="mentionsPagination"/> </div> <div v-else-if="tab === 'directNotes'"> - <XNotes :pagination="directNotesPagination"/> + <MkNotes :pagination="directNotesPagination"/> </div> </MkSpacer> </MkStickyContainer> @@ -19,7 +19,7 @@ import { computed } from 'vue'; import { notificationTypes } from 'misskey-js'; import XNotifications from '@/components/MkNotifications.vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue index a84cb1e80ed32edd30b577267fffacc19a1ff113..fe230ad095ca7ecd7887cefa2770622224c13888 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue @@ -1,10 +1,10 @@ <template> <!-- eslint-disable vue/no-mutating-props --> <XContainer :draggable="true" @remove="() => $emit('remove')"> - <template #header><i class="fas fa-image"></i> {{ $ts._pages.blocks.image }}</template> + <template #header><i class="ti ti-photo"></i> {{ $ts._pages.blocks.image }}</template> <template #func> <button @click="choose()"> - <i class="fas fa-folder-open"></i> + <i class="ti ti-folder"></i> </button> </template> diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue index 6f11e2a08b1c0185af02f47190f0b08f4cfaff98..ee494b75740f9c88c749d047a6f8730857b1c944 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue @@ -1,7 +1,7 @@ <template> <!-- eslint-disable vue/no-mutating-props --> <XContainer :draggable="true" @remove="() => $emit('remove')"> - <template #header><i class="fas fa-align-left"></i> {{ $ts._pages.blocks.text }}</template> + <template #header><i class="ti ti-align-left"></i> {{ $ts._pages.blocks.text }}</template> <section class="vckmsadr"> <textarea v-model="text"></textarea> diff --git a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue index f99fcb202fd0e3e36a7fbb65655d6b2fc4dc0097..97bdcfe80f1ee6aabe48b328dad9a21f01490580 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue @@ -15,8 +15,6 @@ import XSection from './els/page-editor.el.section.vue'; import XText from './els/page-editor.el.text.vue'; import XImage from './els/page-editor.el.image.vue'; import XNote from './els/page-editor.el.note.vue'; -import * as os from '@/os'; -import { deepClone } from '@/scripts/clone'; export default defineComponent({ components: { diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 11575ae7f47242675b225b7f71f05e03997de855..c4b37c91c65d8270384649e67a5c0f931fd3db0a 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -56,10 +56,9 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, provide, watch } from 'vue'; +import { computed, provide, watch } from 'vue'; import { v4 as uuid } from 'uuid'; import XBlocks from './page-editor.blocks.vue'; -import MkTextarea from '@/components/MkTextarea.vue'; import MkButton from '@/components/MkButton.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue index af5f631caf433a17934d1892a4721ede5ff9c147..0427332ab274d47e4ed5f9d84e50419caa944157 100644 --- a/packages/frontend/src/pages/pages.vue +++ b/packages/frontend/src/pages/pages.vue @@ -25,7 +25,7 @@ </template> <script lang="ts" setup> -import { computed, inject } from 'vue'; +import { computed } from 'vue'; import MkPagePreview from '@/components/MkPagePreview.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkButton from '@/components/MkButton.vue'; diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index 2c2a1444c1d50e59f2d1b1a274586147cfc124cb..c687b89eab09c2f947b118c7ad2be84e50ba8ecc 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -28,7 +28,7 @@ </template> <script lang="ts" setup> -import { ref, watch } from 'vue'; +import { watch } from 'vue'; import JSON5 from 'json5'; import * as os from '@/os'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue index bd4dbe7679fc564fdeace13008b0aeb338070ad5..00e2ca5e036dbf130ae2cf02ff2ce43358d12a34 100644 --- a/packages/frontend/src/pages/registry.value.vue +++ b/packages/frontend/src/pages/registry.value.vue @@ -40,13 +40,11 @@ </template> <script lang="ts" setup> -import { ref, watch } from 'vue'; +import { watch } from 'vue'; import JSON5 from 'json5'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; -import FormLink from '@/components/form/link.vue'; -import FormSection from '@/components/form/section.vue'; import MkButton from '@/components/MkButton.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkTextarea from '@/components/MkTextarea.vue'; diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue index a2c65294fcb9062d15eabef515809926d68185ee..5a029cb0c7e7462035e63bc55b347b8ba3edadcf 100644 --- a/packages/frontend/src/pages/registry.vue +++ b/packages/frontend/src/pages/registry.vue @@ -15,7 +15,6 @@ </template> <script lang="ts" setup> -import { ref, watch } from 'vue'; import JSON5 from 'json5'; import * as os from '@/os'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue new file mode 100644 index 0000000000000000000000000000000000000000..2e9d3d616948b241b5e8804e35e6c75cabf99cd1 --- /dev/null +++ b/packages/frontend/src/pages/role.vue @@ -0,0 +1,47 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + + <MkSpacer :content-max="1200"> + <div class="_gaps_s"> + <div v-if="role">{{ role.description }}</div> + <MkUserList :pagination="users" :extractor="(item) => item.user"/> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, watch } from 'vue'; +import * as os from '@/os'; +import MkUserList from '@/components/MkUserList.vue'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +const props = defineProps<{ + role: string; +}>(); + +let role = $ref(); + +watch(() => props.role, () => { + os.api('roles/show', { + roleId: props.role, + }).then(res => { + role = res; + }); +}, { immediate: true }); + +const users = $computed(() => ({ + endpoint: 'roles/users' as const, + limit: 30, + params: { + roleId: props.role, + }, +})); + +definePageMetadata(computed(() => ({ + title: role?.name, + icon: 'ti ti-badge', +}))); +</script> + diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 6075dde32623fa92bc9da809a1ec906124123664..fb78546cb1086eeb120910e6a8761d290956e70b 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -44,7 +44,7 @@ import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; +import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui'; import MkAsUi from '@/components/MkAsUi.vue'; import { miLocalStorage } from '@/local-storage'; import { claimAchievement } from '@/scripts/achievements'; diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index 7918f9f577d67a2819dfd995d431e8be33c2dd65..e52c97b35017c1442120b0263d7558da1715fbcc 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -2,14 +2,14 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <XNotes ref="notes" :pagination="pagination"/> + <MkNotes ref="notes" :pagination="pagination"/> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> import { computed } from 'vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import * as os from '@/os'; diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..1d836db5f5d78da89329c8b5f5925f8343438bb1 --- /dev/null +++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue @@ -0,0 +1,82 @@ +<template> +<MkModal + ref="dialogEl" + :prefer-type="'dialog'" + :z-priority="'low'" + @click="cancel" + @close="cancel" + @closed="emit('closed')" +> + <div :class="$style.root" class="_gaps_m"> + <I18n :src="i18n.ts._2fa.step1" tag="div"> + <template #a> + <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a> + </template> + <template #b> + <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a> + </template> + </I18n> + <div> + {{ i18n.ts._2fa.step2 }}<br> + {{ i18n.ts._2fa.step2Click }} + </div> + <a :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a> + <MkKeyValue :copy="twoFactorData.url"> + <template #key>{{ i18n.ts._2fa.step2Url }}</template> + <template #value>{{ twoFactorData.url }}</template> + </MkKeyValue> + <div class="_buttons"> + <MkButton primary @click="ok">{{ i18n.ts.next }}</MkButton> + <MkButton @click="cancel">{{ i18n.ts.cancel }}</MkButton> + </div> + </div> +</MkModal> +</template> + +<script lang="ts" setup> +import MkButton from '@/components/MkButton.vue'; +import MkModal from '@/components/MkModal.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import { i18n } from '@/i18n'; + +defineProps<{ + twoFactorData: { + qr: string; + url: string; + }; +}>(); + +const emit = defineEmits<{ + (ev: 'ok'): void; + (ev: 'cancel'): void; + (ev: 'closed'): void; +}>(); + +const cancel = () => { + emit('cancel'); + emit('closed'); +}; + +const ok = () => { + emit('ok'); + emit('closed'); +}; +</script> + +<style lang="scss" module> +.root { + position: relative; + margin: auto; + padding: 32px; + min-width: 320px; + max-width: calc(100svw - 64px); + box-sizing: border-box; + background: var(--panel); + border-radius: var(--radius); +} + +.qr { + width: 20em; + max-width: 100%; +} +</style> diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue index e6ef09668c96e1fc9830be888f79b911372700ec..891934d706a83c6efa1b8c956b5f48184064a200 100644 --- a/packages/frontend/src/pages/settings/2fa.vue +++ b/packages/frontend/src/pages/settings/2fa.vue @@ -1,216 +1,258 @@ <template> -<div> - <MkButton v-if="!twoFactorData && !$i.twoFactorEnabled" @click="register">{{ i18n.ts._2fa.registerDevice }}</MkButton> - <template v-if="$i.twoFactorEnabled"> - <p>{{ i18n.ts._2fa.alreadyRegistered }}</p> - <MkButton @click="unregister">{{ i18n.ts.unregister }}</MkButton> - - <template v-if="supportsCredentials"> - <hr class="totp-method-sep"> - - <h2 class="heading">{{ i18n.ts.securityKey }}</h2> - <p>{{ i18n.ts._2fa.securityKeyInfo }}</p> - <div class="key-list"> - <div v-for="key in $i.securityKeysList" class="key"> - <h3>{{ key.name }}</h3> - <div class="last-used">{{ i18n.ts.lastUsed }}<MkTime :time="key.lastUsed"/></div> - <MkButton @click="unregisterKey(key)">{{ i18n.ts.unregister }}</MkButton> - </div> +<FormSection :first="first"> + <template #label>{{ i18n.ts['2fa'] }}</template> + + <div v-if="$i" class="_gaps_s"> + <MkFolder> + <template #icon><i class="ti ti-shield-lock"></i></template> + <template #label>{{ i18n.ts.totp }}</template> + <template #caption>{{ i18n.ts.totpDescription }}</template> + <div v-if="$i.twoFactorEnabled" class="_gaps_s"> + <div v-text="i18n.ts._2fa.alreadyRegistered"/> + <template v-if="$i.securityKeysList.length > 0"> + <MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton> + <MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo> + </template> + <MkButton v-else @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton> + </div> + + <MkButton v-else-if="!twoFactorData && !$i.twoFactorEnabled" @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton> + </MkFolder> + + <MkFolder> + <template #icon><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.securityKeyAndPasskey }}</template> + <div class="_gaps_s"> + <MkInfo> + {{ i18n.ts._2fa.securityKeyInfo }}<br> + <br> + {{ i18n.ts._2fa.chromePasskeyNotSupported }} + </MkInfo> + + <MkInfo v-if="!supportsCredentials" warn> + {{ i18n.ts._2fa.securityKeyNotSupported }} + </MkInfo> + + <MkInfo v-else-if="supportsCredentials && !$i.twoFactorEnabled" warn> + {{ i18n.ts._2fa.registerTOTPBeforeKey }} + </MkInfo> + + <template v-else> + <MkButton primary @click="addSecurityKey">{{ i18n.ts._2fa.registerSecurityKey }}</MkButton> + <MkFolder v-for="key in $i.securityKeysList" :key="key.id"> + <template #label>{{ key.name }}</template> + <template #suffix><I18n :src="i18n.ts.lastUsedAt"><template #t><MkTime :time="key.lastUsed"/></template></I18n></template> + <div class="_buttons"> + <MkButton @click="renameKey(key)"><i class="ti ti-forms"></i> {{ i18n.ts.rename }}</MkButton> + <MkButton danger @click="unregisterKey(key)"><i class="ti ti-trash"></i> {{ i18n.ts.unregister }}</MkButton> + </div> + </MkFolder> + </template> </div> + </MkFolder> - <MkSwitch v-if="$i.securityKeysList.length > 0" v-model="usePasswordLessLogin" @update:model-value="updatePasswordLessLogin">{{ i18n.ts.passwordLessLogin }}</MkSwitch> - - <MkInfo v-if="registration && registration.error" warn>{{ i18n.ts.error }} {{ registration.error }}</MkInfo> - <MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ i18n.ts._2fa.registerKey }}</MkButton> - - <ol v-if="registration && !registration.error"> - <li v-if="registration.stage >= 0"> - {{ i18n.ts.tapSecurityKey }} - <MkLoading v-if="registration.saving && registration.stage == 0" :em="true"/> - </li> - <li v-if="registration.stage >= 1"> - <MkForm :disabled="registration.stage != 1 || registration.saving"> - <MkInput v-model="keyName" :max="30"> - <template #label>{{ i18n.ts.securityKeyName }}</template> - </MkInput> - <MkButton :disabled="keyName.length == 0" @click="registerKey">{{ i18n.ts.registerSecurityKey }}</MkButton> - <MkLoading v-if="registration.saving && registration.stage == 1" :em="true"/> - </MkForm> - </li> - </ol> - </template> - </template> - <div v-if="twoFactorData && !$i.twoFactorEnabled"> - <ol style="margin: 0; padding: 0 0 0 1em;"> - <li> - <I18n :src="i18n.ts._2fa.step1" tag="span"> - <template #a> - <a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a> - </template> - <template #b> - <a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a> - </template> - </I18n> - </li> - <li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li> - <li> - {{ i18n.ts._2fa.step3 }}<br> - <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput> - <MkButton primary @click="submit">{{ i18n.ts.done }}</MkButton> - </li> - </ol> - <MkInfo>{{ i18n.ts._2fa.step4 }}</MkInfo> + <MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :model-value="usePasswordLessLogin" @update:model-value="v => updatePasswordLessLogin(v)"> + <template #label>{{ i18n.ts.passwordLessLogin }}</template> + <template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template> + </MkSwitch> </div> -</div> +</FormSection> </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { ref, defineAsyncComponent } from 'vue'; import { hostname } from '@/config'; import { byteify, hexify, stringify } from '@/scripts/2fa'; import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; -import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; +import FormSection from '@/components/form/section.vue'; +import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; +// メモ: å„エンドãƒã‚¤ãƒ³ãƒˆã¯meUpdatedを発行ã™ã‚‹ãŸã‚ã€refreshAccountã¯ä¸è¦ + +withDefaults(defineProps<{ + first?: boolean; +}>(), { + first: false, +}); + const twoFactorData = ref<any>(null); const supportsCredentials = ref(!!navigator.credentials); -const usePasswordLessLogin = ref($i!.usePasswordLessLogin); -const registration = ref<any>(null); -const keyName = ref(''); -const token = ref(null); +const usePasswordLessLogin = $computed(() => $i!.usePasswordLessLogin); -function register() { - os.inputText({ - title: i18n.ts.password, +async function registerTOTP() { + const password = await os.inputText({ + title: i18n.ts._2fa.registerTOTP, + text: i18n.ts._2fa.passwordToTOTP, type: 'password', - }).then(({ canceled, result: password }) => { - if (canceled) return; - os.api('i/2fa/register', { - password: password, - }).then(data => { - twoFactorData.value = data; - }); + autocomplete: 'current-password', + }); + if (password.canceled) return; + + const twoFactorData = await os.apiWithDialog('i/2fa/register', { + password: password.result, + }); + + const qrdialog = await new Promise<boolean>(res => { + os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), { + twoFactorData, + }, { + 'ok': () => res(true), + 'cancel': () => res(false), + }, 'closed'); + }); + if (!qrdialog) return; + + const token = await os.inputNumber({ + title: i18n.ts._2fa.step3Title, + text: i18n.ts._2fa.step3, + autocomplete: 'one-time-code', + }); + if (token.canceled) return; + + await os.apiWithDialog('i/2fa/done', { + token: token.result.toString(), + }); + + await os.alert({ + type: 'success', + text: i18n.ts._2fa.step4, }); } -function unregister() { +function unregisterTOTP() { os.inputText({ title: i18n.ts.password, type: 'password', + autocomplete: 'current-password', }).then(({ canceled, result: password }) => { if (canceled) return; - os.api('i/2fa/unregister', { + os.apiWithDialog('i/2fa/unregister', { password: password, - }).then(() => { - usePasswordLessLogin.value = false; - updatePasswordLessLogin(); - }).then(() => { - os.success(); - $i!.twoFactorEnabled = false; + }).catch(error => { + os.alert({ + type: 'error', + text: error, + }); }); }); } -function submit() { - os.api('i/2fa/done', { - token: token.value, - }).then(() => { - os.success(); - $i!.twoFactorEnabled = true; - }).catch(err => { - os.alert({ - type: 'error', - text: err, - }); +function renewTOTP() { + os.confirm({ + type: 'question', + title: i18n.ts._2fa.renewTOTP, + text: i18n.ts._2fa.renewTOTPConfirm, + okText: i18n.ts._2fa.renewTOTPOk, + cancelText: i18n.ts._2fa.renewTOTPCancel, + }).then(({ canceled }) => { + if (canceled) return; + registerTOTP(); }); } -function registerKey() { - registration.value.saving = true; - os.api('i/2fa/key-done', { - password: registration.value.password, - name: keyName.value, - challengeId: registration.value.challengeId, - // we convert each 16 bits to a string to serialise - clientDataJSON: stringify(registration.value.credential.response.clientDataJSON), - attestationObject: hexify(registration.value.credential.response.attestationObject), - }).then(key => { - registration.value = null; - key.lastUsed = new Date(); - os.success(); +async function unregisterKey(key) { + const confirm = await os.confirm({ + type: 'question', + title: i18n.ts._2fa.removeKey, + text: i18n.t('_2fa.removeKeyConfirm', { name: key.name }), }); -} + if (confirm.canceled) return; -function unregisterKey(key) { - os.inputText({ + const password = await os.inputText({ title: i18n.ts.password, type: 'password', - }).then(({ canceled, result: password }) => { - if (canceled) return; - return os.api('i/2fa/remove-key', { - password, - credentialId: key.id, - }).then(() => { - usePasswordLessLogin.value = false; - updatePasswordLessLogin(); - }).then(() => { - os.success(); - }); + autocomplete: 'current-password', + }); + if (password.canceled) return; + + await os.apiWithDialog('i/2fa/remove-key', { + password: password.result, + credentialId: key.id, }); + os.success(); } -function addSecurityKey() { - os.inputText({ +async function renameKey(key) { + const name = await os.inputText({ + title: i18n.ts.rename, + default: key.name, + type: 'text', + minLength: 1, + maxLength: 30, + }); + if (name.canceled) return; + + await os.apiWithDialog('i/2fa/update-key', { + name: name.result, + credentialId: key.id, + }); +} + +async function addSecurityKey() { + const password = await os.inputText({ title: i18n.ts.password, type: 'password', - }).then(({ canceled, result: password }) => { - if (canceled) return; - os.api('i/2fa/register-key', { - password, - }).then(reg => { - registration.value = { - password, - challengeId: reg!.challengeId, - stage: 0, - publicKeyOptions: { - challenge: byteify(reg!.challenge, 'base64'), - rp: { - id: hostname, - name: 'Misskey', - }, - user: { - id: byteify($i!.id, 'ascii'), - name: $i!.username, - displayName: $i!.name, - }, - pubKeyCredParams: [{ alg: -7, type: 'public-key' }], - timeout: 60000, - attestation: 'direct', - }, - saving: true, - }; - return navigator.credentials.create({ - publicKey: registration.value.publicKeyOptions, - }); - }).then(credential => { - registration.value.credential = credential; - registration.value.saving = false; - registration.value.stage = 1; - }).catch(err => { - console.warn('Error while registering?', err); - registration.value.error = err.message; - registration.value.stage = -1; - }); + autocomplete: 'current-password', + }); + if (password.canceled) return; + + const challenge: any = await os.apiWithDialog('i/2fa/register-key', { + password: password.result, + }); + + const name = await os.inputText({ + title: i18n.ts._2fa.registerSecurityKey, + text: i18n.ts._2fa.securityKeyName, + type: 'text', + minLength: 1, + maxLength: 30, + }); + if (name.canceled) return; + + const webAuthnCreation = navigator.credentials.create({ + publicKey: { + challenge: byteify(challenge.challenge, 'base64'), + rp: { + id: hostname, + name: 'Misskey', + }, + user: { + id: byteify($i!.id, 'ascii'), + name: $i!.username, + displayName: $i!.name, + }, + pubKeyCredParams: [{ alg: -7, type: 'public-key' }], + timeout: 60000, + attestation: 'direct', + }, + }) as Promise<PublicKeyCredential & { response: AuthenticatorAttestationResponse; } | null>; + + const credential = await os.promiseDialog( + webAuthnCreation, + null, + () => {}, // ユーザーã®ã‚ャンセルã¯rejectãªã®ã§ã‚¨ãƒ©ãƒ¼ãƒ€ã‚¤ã‚¢ãƒã‚°ã‚’出ã•ãªã„ + i18n.ts._2fa.tapSecurityKey, + ); + if (!credential) return; + + await os.apiWithDialog('i/2fa/key-done', { + password: password.result, + name: name.result, + challengeId: challenge.challengeId, + // we convert each 16 bits to a string to serialise + clientDataJSON: stringify(credential.response.clientDataJSON), + attestationObject: hexify(credential.response.attestationObject), }); } -async function updatePasswordLessLogin() { - await os.api('i/2fa/password-less', { - value: !!usePasswordLessLogin.value, +async function updatePasswordLessLogin(value: boolean) { + await os.apiWithDialog('i/2fa/password-less', { + value, }); } </script> diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue index 3f1f2820f08b761fd9905a951294b4fa07f8f1ba..bc0179b3aae5d2bc656c46df66dfac483817e2fe 100644 --- a/packages/frontend/src/pages/settings/deck.vue +++ b/packages/frontend/src/pages/settings/deck.vue @@ -13,14 +13,10 @@ </template> <script lang="ts" setup> -import { computed, watch } from 'vue'; +import { computed } from 'vue'; import MkSwitch from '@/components/MkSwitch.vue'; -import FormLink from '@/components/form/link.vue'; import MkRadios from '@/components/MkRadios.vue'; -import MkInput from '@/components/MkInput.vue'; import { deckStore } from '@/ui/deck/deck-store'; -import * as os from '@/os'; -import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 4099a3c11da124304688bb6d01200f11c488e9c1..a23bdfe69ec316449fbd5966c4f62e5d20afd74e 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -30,7 +30,7 @@ <FormLink @click="chooseUploadFolder()"> {{ i18n.ts.uploadFolder }} <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> - <template #suffixIcon><i class="fas fa-folder-open"></i></template> + <template #suffixIcon><i class="ti ti-folder"></i></template> </FormLink> <MkSwitch v-model="keepOriginalUploading"> <template #label>{{ i18n.ts.keepOriginalUploading }}</template> diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index 57b07b1cc1c7bb8ff104c4732f599ae38013c76a..1734dcfe4235bbb4385705221c836488ed5a5328 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -34,9 +34,6 @@ <MkSwitch v-model="emailNotification_receiveFollowRequest"> {{ i18n.ts._notification._types.receiveFollowRequest }} </MkSwitch> - <MkSwitch v-model="emailNotification_groupInvited"> - {{ i18n.ts._notification._types.groupInvited }} - </MkSwitch> </div> </FormSection> </div> @@ -78,7 +75,6 @@ const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply') const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote')); const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow')); const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest')); -const emailNotification_groupInvited = ref($i!.emailNotificationTypes.includes('groupInvited')); const saveNotificationSettings = () => { os.api('i/update', { @@ -88,12 +84,11 @@ const saveNotificationSettings = () => { ...[emailNotification_quote.value ? 'quote' : null], ...[emailNotification_follow.value ? 'follow' : null], ...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null], - ...[emailNotification_groupInvited.value ? 'groupInvited' : null], ].filter(x => x != null), }); }; -watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => { +watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest], () => { saveNotificationSettings(); }); diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 8631f3e341bf04391c03704672d2054c6220b4af..e6752460a8dde7f34d21e50f9a334fc1c2452546 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -22,16 +22,15 @@ </template> <script setup lang="ts"> -import { computed, defineAsyncComponent, inject, nextTick, onActivated, onMounted, onUnmounted, provide, ref, shallowRef, watch } from 'vue'; +import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; import { i18n } from '@/i18n'; import MkInfo from '@/components/MkInfo.vue'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; -import { scroll } from '@/scripts/scroll'; import { signout, $i } from '@/account'; import { unisonReload } from '@/scripts/unison-reload'; import { instance } from '@/instance'; import { useRouter } from '@/router'; -import { definePageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import * as os from '@/os'; import { miLocalStorage } from '@/local-storage'; import { fetchCustomEmojis } from '@/custom-emojis'; diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 48579aa0692e7f5589340040ac2f63f22c1415d7..a08308f0cef5caf3f37409badc9f4b1a9622d7f8 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -34,7 +34,6 @@ import MkTab from '@/components/MkTab.vue'; import FormInfo from '@/components/MkInfo.vue'; import FormLink from '@/components/form/link.vue'; import { userPage } from '@/filters/user'; -import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index db32ee862c58acdcb78cc0c1a3cff65e607f0210..f64202fff2af4437c04f7d8c7c56bf18f8c17147 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -5,7 +5,6 @@ <div class="_gaps_m"> <FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink> <FormLink @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink> - <FormLink @click="readAllMessagingMessages">{{ i18n.ts.markAsReadAllTalkMessages }}</FormLink> </div> </FormSection> <FormSection> @@ -29,7 +28,6 @@ <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; import { notificationTypes } from 'misskey-js'; -import MkButton from '@/components/MkButton.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -47,10 +45,6 @@ async function readAllUnreadNotes() { await os.api('i/read-all-unread-notes'); } -async function readAllMessagingMessages() { - await os.api('i/read-all-messaging-messages'); -} - async function readAllNotifications() { await os.api('notifications/mark-all-as-read'); } diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 3647e90ce756f41f4729ed71f6e86ef0312d00ef..41563c441f8bca237594dd979792bd0eb2643ae0 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -78,7 +78,6 @@ import MkSelect from '@/components/MkSelect.vue'; import FormSplit from '@/components/form/split.vue'; import MkFolder from '@/components/MkFolder.vue'; import FormSlot from '@/components/form/slot.vue'; -import { host } from '@/config'; import { selectFile } from '@/scripts/select-file'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -125,11 +124,11 @@ function saveFields() { function save() { os.apiWithDialog('i/update', { - name: profile.name || null, - description: profile.description || null, - location: profile.location || null, - birthday: profile.birthday || null, - lang: profile.lang || null, + name: profile.name ?? null, + description: profile.description ?? null, + location: profile.location ?? null, + birthday: profile.birthday ?? null, + lang: profile.lang ?? null, isBot: !!profile.isBot, isCat: !!profile.isCat, showTimelineReplies: !!profile.showTimelineReplies, diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue index c8b47b8299eb3ff68c2f9999bd70e30b13248235..ed913731d36b41afe81c4076eb8c50e5bfa15a62 100644 --- a/packages/frontend/src/pages/settings/reaction.vue +++ b/packages/frontend/src/pages/settings/reaction.vue @@ -57,7 +57,6 @@ <script lang="ts" setup> import { defineAsyncComponent, watch } from 'vue'; import Sortable from 'vuedraggable'; -import MkInput from '@/components/MkInput.vue'; import MkRadios from '@/components/MkRadios.vue'; import FromSlot from '@/components/form/slot.vue'; import MkButton from '@/components/MkButton.vue'; diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue index b09c4ffd2f5e225eefb83f528ef5135078092945..0cc2df09c5e6c0e8524c3b579c7b7e08a7ebf5b8 100644 --- a/packages/frontend/src/pages/settings/security.vue +++ b/packages/frontend/src/pages/settings/security.vue @@ -5,11 +5,8 @@ <MkButton primary @click="change()">{{ i18n.ts.changePassword }}</MkButton> </FormSection> - <FormSection> - <template #label>{{ i18n.ts.twoStepAuthentication }}</template> - <X2fa/> - </FormSection> - + <X2fa/> + <FormSection> <template #label>{{ i18n.ts.signinHistory }}</template> <MkPagination :pagination="pagination" disable-auto-load> @@ -56,18 +53,21 @@ async function change() { const { canceled: canceled1, result: currentPassword } = await os.inputText({ title: i18n.ts.currentPassword, type: 'password', + autocomplete: 'current-password', }); if (canceled1) return; const { canceled: canceled2, result: newPassword } = await os.inputText({ title: i18n.ts.newPassword, type: 'password', + autocomplete: 'new-password', }); if (canceled2) return; const { canceled: canceled3, result: newPassword2 } = await os.inputText({ title: i18n.ts.newPasswordRetype, type: 'password', + autocomplete: 'new-password', }); if (canceled3) return; @@ -109,7 +109,7 @@ definePageMetadata({ <style lang="scss" scoped> .timnmucd { - padding: 16px; + padding: 12px; &:first-child { border-top-left-radius: 6px; diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index f9e301aef9595334200951884fb3af543ca89755..006a2377d461fde59760e8755c475d177624423e 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -25,12 +25,9 @@ import { computed, ref } from 'vue'; import XSound from './sounds.sound.vue'; import MkRange from '@/components/MkRange.vue'; import MkButton from '@/components/MkButton.vue'; -import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkFolder from '@/components/MkFolder.vue'; -import * as os from '@/os'; import { ColdDeviceStorage } from '@/store'; -import { playFile } from '@/scripts/sound'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; @@ -43,7 +40,7 @@ const masterVolume = computed({ }, }); -const volumeIcon = computed(() => masterVolume.value === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up'); +const volumeIcon = computed(() => masterVolume.value === 0 ? 'ti ti-volume-3' : 'ti ti-volume'); const sounds = ref({ note: ColdDeviceStorage.get('sound_note'), diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue index eee65e0e95871cdda8ffb1f72b7835da84219dac..81ff873e9e55f50c7265f97669e203c9290d0252 100644 --- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue @@ -81,14 +81,13 @@ </template> <script lang="ts" setup> -import { computed, reactive, ref, watch } from 'vue'; +import { reactive, watch } from 'vue'; import MkSelect from '@/components/MkSelect.vue'; import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import MkRange from '@/components/MkRange.vue'; -import * as os from '@/os'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { deepClone } from '@/scripts/clone'; diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue index ab081964c243ce11e28c13a9212426ec6bb224bf..cb46858c5aff5e9cc526d5184879b8d41bb9f93c 100644 --- a/packages/frontend/src/pages/settings/statusbar.vue +++ b/packages/frontend/src/pages/settings/statusbar.vue @@ -10,15 +10,13 @@ </template> <script lang="ts" setup> -import { computed, onMounted, ref, watch } from 'vue'; +import { onMounted } from 'vue'; import { v4 as uuid } from 'uuid'; import XStatusbar from './statusbar.statusbar.vue'; -import MkRadios from '@/components/MkRadios.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { defaultStore } from '@/store'; -import { unisonReload } from '@/scripts/unison-reload'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue index 01c31688ccefb22a3ac9c98f446ea24021fcdb8f..e10f65b0afbe09ad8f9b3013ad057659d6ae246f 100644 --- a/packages/frontend/src/pages/settings/webhook.vue +++ b/packages/frontend/src/pages/settings/webhook.vue @@ -30,9 +30,6 @@ import { } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import FormSection from '@/components/form/section.vue'; import FormLink from '@/components/form/link.vue'; -import { userPage } from '@/filters/user'; -import * as os from '@/os'; -import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; const pagination = { diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index 5d6d01d2aed0fc7aa72ab2adcb28beb63d0b3fcc..511052c4242395afc593e62303860e992bed3190 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -2,14 +2,14 @@ <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :content-max="800"> - <XNotes class="" :pagination="pagination"/> + <MkNotes class="" :pagination="pagination"/> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> import { computed } from 'vue'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import { definePageMetadata } from '@/scripts/page-metadata'; const props = defineProps<{ diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index a07136115014f76c8ba90461bcd9fcc591ae350b..d982a76d03eb1fa9ef5220a921c047501dd083ab 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -8,7 +8,7 @@ <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div :class="$style.tl"> - <XTimeline + <MkTimeline ref="tlComponent" :key="src" :src="src" @@ -23,7 +23,8 @@ <script lang="ts" setup> import { defineAsyncComponent, computed, watch, provide } from 'vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import MkPostForm from '@/components/MkPostForm.vue'; import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; @@ -32,7 +33,6 @@ import { i18n } from '@/i18n'; import { instance } from '@/instance'; import { $i } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; -import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; provide('shouldOmitHeaderTitle', true); @@ -44,7 +44,7 @@ const keymap = { 't': focus, }; -const tlComponent = $shallowRef<InstanceType<typeof XTimeline>>(); +const tlComponent = $shallowRef<InstanceType<typeof MkTimeline>>(); const rootEl = $shallowRef<HTMLElement>(); let queue = $ref(0); @@ -83,7 +83,9 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> { } async function chooseChannel(ev: MouseEvent): Promise<void> { - const channels = await os.api('channels/followed'); + const channels = await os.api('channels/followed', { + limit: 100, + }); const items = channels.map(channel => ({ type: 'link' as const, text: channel.name, diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index 7ba8a3d16b250a81c2bb5e19566a050b188c50c4..13a06286f6fb75f846ecd3c2779206684eef6542 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -112,7 +112,7 @@ <MkButton v-if="user.host == null && iAmModerator" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton> <div v-for="role in info.roles" :key="role.id" :class="$style.roleItem"> - <MkRolePreview :class="$style.role" :role="role"/> + <MkRolePreview :class="$style.role" :role="role" :for-moderation="true"/> <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button> <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button> </div> @@ -181,8 +181,6 @@ import MkSwitch from '@/components/MkSwitch.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; import MkButton from '@/components/MkButton.vue'; -import MkInput from '@/components/MkInput.vue'; -import FormSplit from '@/components/form/split.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkSelect from '@/components/MkSelect.vue'; @@ -190,14 +188,11 @@ import FormSuspense from '@/components/form/suspense.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os'; -import number from '@/filters/number'; -import bytes from '@/filters/bytes'; import { url } from '@/config'; import { userPage, acct } from '@/filters/user'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; import { iAmAdmin, iAmModerator } from '@/account'; -import { instance } from '@/instance'; import MkRolePreview from '@/components/MkRolePreview.vue'; const props = withDefaults(defineProps<{ diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 3f47edfd44147a81286bb2c0a09c343f0b2bd771..acf7ea9b2c2abf70ee60726ef8beb62b609a7c9b 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -4,7 +4,7 @@ <div ref="rootEl" class="eqqrhokj"> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div class="tl"> - <XTimeline + <MkTimeline ref="tlEl" :key="listId" class="tl" src="list" @@ -18,8 +18,8 @@ </template> <script lang="ts" setup> -import { computed, watch, inject } from 'vue'; -import XTimeline from '@/components/MkTimeline.vue'; +import { computed, watch } from 'vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import { scroll } from '@/scripts/scroll'; import * as os from '@/os'; import { useRouter } from '@/router'; @@ -34,7 +34,7 @@ const props = defineProps<{ let list = $ref(null); let queue = $ref(0); -let tlEl = $shallowRef<InstanceType<typeof XTimeline>>(); +let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>(); let rootEl = $shallowRef<HTMLElement>(); watch(() => props.listId, async () => { @@ -65,7 +65,7 @@ async function timetravel() { } const headerActions = $computed(() => list ? [{ - icon: 'fas fa-calendar-alt', + icon: 'ti ti-calendar-time', text: i18n.ts.jumpToSpecifiedDate, handler: timetravel, }, { diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue new file mode 100644 index 0000000000000000000000000000000000000000..fac7593e9cac082e76a60e99968028b959f979b2 --- /dev/null +++ b/packages/frontend/src/pages/user-tag.vue @@ -0,0 +1,38 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + + <MkSpacer :content-max="1200"> + <div class="_gaps_s"> + <MkUserList :pagination="tagUsers"/> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, watch } from 'vue'; +import * as os from '@/os'; +import MkUserList from '@/components/MkUserList.vue'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +const props = defineProps<{ + tag: string; +}>(); + +const tagUsers = $computed(() => ({ + endpoint: 'hashtags/users' as const, + limit: 30, + params: { + tag: props.tag, + origin: 'combined', + sort: '+follower', + }, +})); + +definePageMetadata(computed(() => ({ + title: props.tag, + icon: 'ti ti-user-search', +}))); +</script> + diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue index 615613b7fc22e9ebc2a11c80a5174b38d843a8e1..1b3a6e24b3cf1d2d760c47db114b5952ab0116a2 100644 --- a/packages/frontend/src/pages/user/achievements.vue +++ b/packages/frontend/src/pages/user/achievements.vue @@ -5,10 +5,9 @@ </template> <script lang="ts" setup> -import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue'; +import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue'; import * as misskey from 'misskey-js'; import MkAchievements from '@/components/MkAchievements.vue'; -import { i18n } from '@/i18n'; import { claimAchievement } from '@/scripts/achievements'; import { $i } from '@/account'; diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue index 500995e3924408a2682d3c14fb8cd858c14ec758..54360024f3b90ceea7e4d4f3f2af77f8301bc149 100644 --- a/packages/frontend/src/pages/user/activity.following.vue +++ b/packages/frontend/src/pages/user/activity.following.vue @@ -9,17 +9,14 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; +import { onMounted } from 'vue'; import { Chart, ChartDataset } from 'chart.js'; -import tinycolor from 'tinycolor2'; import * as misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; -import { satisfies } from 'compare-versions'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; -import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; import { chartLegend } from '@/scripts/chart-legend'; import MkChartLegend from '@/components/MkChartLegend.vue'; diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue index 202201afb5e1e2bc88fc4d6ebd07e2564fb87feb..2dcb754c9b7bb5a388addceb92351ca0f72aa3b9 100644 --- a/packages/frontend/src/pages/user/activity.heatmap.vue +++ b/packages/frontend/src/pages/user/activity.heatmap.vue @@ -8,14 +8,12 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; +import { onMounted, nextTick, watch } from 'vue'; import { Chart } from 'chart.js'; -import tinycolor from 'tinycolor2'; import * as misskey from 'misskey-js'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; -import { chartVLine } from '@/scripts/chart-vline'; import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue index 8763997f8ee57798ccf8ed2457fc236d88e08dc8..7dd02ad6d44353989b7a1939797f1ef0caf411bd 100644 --- a/packages/frontend/src/pages/user/activity.notes.vue +++ b/packages/frontend/src/pages/user/activity.notes.vue @@ -9,17 +9,14 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; +import { onMounted } from 'vue'; import { Chart, ChartDataset } from 'chart.js'; -import tinycolor from 'tinycolor2'; import * as misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; -import { satisfies } from 'compare-versions'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; -import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; import { chartLegend } from '@/scripts/chart-legend'; import MkChartLegend from '@/components/MkChartLegend.vue'; diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue index d23f89a31e522cb29e818c459e985a15a4e455a2..6a7506e3886db7bc5e8c68aaf175fc9350e21e63 100644 --- a/packages/frontend/src/pages/user/activity.pv.vue +++ b/packages/frontend/src/pages/user/activity.pv.vue @@ -9,16 +9,14 @@ </template> <script lang="ts" setup> -import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; +import { onMounted } from 'vue'; import { Chart, ChartDataset } from 'chart.js'; -import tinycolor from 'tinycolor2'; import * as misskey from 'misskey-js'; import gradient from 'chartjs-plugin-gradient'; import * as os from '@/os'; import { defaultStore } from '@/store'; import { useChartTooltip } from '@/scripts/use-chart-tooltip'; import { chartVLine } from '@/scripts/chart-vline'; -import { alpha } from '@/scripts/color'; import { initChart } from '@/scripts/init-chart'; import { chartLegend } from '@/scripts/chart-legend'; import MkChartLegend from '@/components/MkChartLegend.vue'; diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue index 6d7c7e77229c9b5527e915f4573efb7dea44bbba..cd538ad61fdd6679fea36f5840e75348b4a2a81a 100644 --- a/packages/frontend/src/pages/user/activity.vue +++ b/packages/frontend/src/pages/user/activity.vue @@ -22,7 +22,6 @@ </template> <script lang="ts" setup> -import { computed } from 'vue'; import * as misskey from 'misskey-js'; import XHeatmap from './activity.heatmap.vue'; import XPv from './activity.pv.vue'; diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue index 88599287846735ee74c770e6fcb951cdb2355875..20573e67e946514d93a3d059977c04139524012a 100644 --- a/packages/frontend/src/pages/user/followers.vue +++ b/packages/frontend/src/pages/user/followers.vue @@ -14,7 +14,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue'; +import { computed, watch } from 'vue'; import * as Acct from 'misskey-js/built/acct'; import * as misskey from 'misskey-js'; import XFollowList from './follow-list.vue'; diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue index 51015905c651dc3a70b18f9a96c8dfce564c545c..3825f138cfcf9819194073c36a87e4cb68cd2661 100644 --- a/packages/frontend/src/pages/user/following.vue +++ b/packages/frontend/src/pages/user/following.vue @@ -14,7 +14,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue'; +import { computed, watch } from 'vue'; import * as Acct from 'misskey-js/built/acct'; import * as misskey from 'misskey-js'; import XFollowList from './follow-list.vue'; diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 6d942d43915bc56b49814c917dea2297b806b145..441b19440cd2ce3087c6c4ac8491a98c5dd051f3 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -100,7 +100,7 @@ <XPhotos :key="user.id" :user="user"/> <XActivity :key="user.id" :user="user"/> </template> - <XNotes :no-gap="true" :pagination="pagination"/> + <MkNotes :class="$style.tl" :no-gap="true" :pagination="pagination"/> </div> </div> <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> @@ -112,28 +112,25 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue'; +import { defineAsyncComponent, computed, onMounted, onUnmounted } from 'vue'; import calcAge from 's-age'; import * as misskey from 'misskey-js'; import XNote from '@/components/MkNote.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; -import MkContainer from '@/components/MkContainer.vue'; -import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkRemoteCaution from '@/components/MkRemoteCaution.vue'; -import MkTab from '@/components/MkTab.vue'; import MkOmit from '@/components/MkOmit.vue'; import MkInfo from '@/components/MkInfo.vue'; import { getScrollPosition } from '@/scripts/scroll'; import { getUserMenu } from '@/scripts/get-user-menu'; import number from '@/filters/number'; -import { userPage, acct as getAcct } from '@/filters/user'; +import { userPage } from '@/filters/user'; import * as os from '@/os'; import { useRouter } from '@/router'; import { i18n } from '@/i18n'; import { $i } from '@/account'; import { dateString } from '@/filters/date'; import { confetti } from '@/scripts/confetti'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; const XPhotos = defineAsyncComponent(() => import('./index.photos.vue')); const XActivity = defineAsyncComponent(() => import('./index.activity.vue')); @@ -522,3 +519,11 @@ onUnmounted(() => { } } </style> + +<style lang="scss" module> +.tl { + background: var(--bg); + border-radius: var(--radius); + overflow: clip; +} +</style> diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index 34e16c707d20cc9ac66306b607d3f67db9ab801c..d8fc253910b350f87864c8d1c939dd84793c547d 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -8,7 +8,7 @@ <option value="files">{{ i18n.ts.withFiles }}</option> </MkTab> </template> - <XNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/> + <MkNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/> </MkStickyContainer> </MkSpacer> </template> @@ -16,9 +16,8 @@ <script lang="ts" setup> import { ref, computed } from 'vue'; import * as misskey from 'misskey-js'; -import XNotes from '@/components/MkNotes.vue'; +import MkNotes from '@/components/MkNotes.vue'; import MkTab from '@/components/MkTab.vue'; -import * as os from '@/os'; import { i18n } from '@/i18n'; const props = defineProps<{ @@ -32,7 +31,7 @@ const pagination = { limit: 10, params: computed(() => ({ userId: props.user.id, - includeReplies: include.value === 'replies', + includeReplies: include.value === 'replies' || include.value === 'files', withFiles: include.value === 'files', })), }; diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue index 29aef21859b1413bd1c3413e999061ab69352d39..03a226cc095c5565e1a677f715357edebcd23da5 100644 --- a/packages/frontend/src/pages/user/index.vue +++ b/packages/frontend/src/pages/user/index.vue @@ -26,7 +26,6 @@ import * as Acct from 'misskey-js/built/acct'; import * as misskey from 'misskey-js'; import { acct as getAcct } from '@/filters/user'; import * as os from '@/os'; -import { useRouter } from '@/router'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; import { $i } from '@/account'; diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue index bfa54d39f26a7a09ace8a53f5ead7600bfe9d6fc..f62a6461c5cc76b082df00f03cf5f6fce89db174 100644 --- a/packages/frontend/src/pages/welcome.entrance.a.vue +++ b/packages/frontend/src/pages/welcome.entrance.a.vue @@ -1,18 +1,18 @@ <template> <div v-if="meta" class="rsqzvsbo"> - <div class="top"> - <MkFeaturedPhotos class="bg"/> - <XTimeline class="tl"/> - <div class="shape1"></div> - <div class="shape2"></div> - <img src="/client-assets/misskey.svg" class="misskey"/> - <div class="emojis"> - <MkEmoji :normal="true" :no-style="true" emoji="ðŸ‘"/> - <MkEmoji :normal="true" :no-style="true" emoji="â¤"/> - <MkEmoji :normal="true" :no-style="true" emoji="😆"/> - <MkEmoji :normal="true" :no-style="true" emoji="🎉"/> - <MkEmoji :normal="true" :no-style="true" emoji="ðŸ®"/> - </div> + <MkFeaturedPhotos class="bg"/> + <XTimeline class="tl"/> + <div class="shape1"></div> + <div class="shape2"></div> + <img src="/client-assets/misskey.svg" class="misskey"/> + <div class="emojis"> + <MkEmoji :normal="true" :no-style="true" emoji="ðŸ‘"/> + <MkEmoji :normal="true" :no-style="true" emoji="â¤"/> + <MkEmoji :normal="true" :no-style="true" emoji="😆"/> + <MkEmoji :normal="true" :no-style="true" emoji="🎉"/> + <MkEmoji :normal="true" :no-style="true" emoji="ðŸ®"/> + </div> + <div class="contents"> <div class="main"> <img :src="$instance.iconUrl || $instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/> <button class="_button _acrylic menu" @click="showMenu"><i class="ti ti-dots"></i></button> @@ -26,66 +26,55 @@ <!-- eslint-disable-next-line vue/no-v-html --> <div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div> </div> - <div class="action"> - <MkButton inline rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.signup }}</MkButton> - <MkButton inline rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton> + <div class="action _gaps_s"> + <MkButton full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton> + <MkButton full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton> + <MkButton full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton> </div> </div> </div> - <div v-if="instances" class="federation"> - <MarqueeText :duration="40"> - <MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window"> - <!--<MkInstanceCardMini :instance="instance"/>--> - <img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/> - <span class="name _monospace">{{ instance.host }}</span> - </MkA> - </MarqueeText> + <div v-if="instance.policies.ltlAvailable" class="tl"> + <div class="title">{{ i18n.ts.letsLookAtTimeline }}</div> + <div class="body"> + <MkTimeline src="local"/> + </div> </div> </div> + <div v-if="instances && instances.length > 0" class="federation"> + <MarqueeText :duration="40"> + <MkA v-for="instance in instances" :key="instance.id" :class="$style.federationInstance" :to="`/instance-info/${instance.host}`" behavior="window"> + <!--<MkInstanceCardMini :instance="instance"/>--> + <img v-if="instance.iconUrl" class="icon" :src="instance.iconUrl" alt=""/> + <span class="name _monospace">{{ instance.host }}</span> + </MkA> + </MarqueeText> + </div> </div> </template> <script lang="ts" setup> import { } from 'vue'; -import { toUnicode } from 'punycode/'; +import { Instance } from 'misskey-js/built/entities'; import XTimeline from './welcome.timeline.vue'; import MarqueeText from '@/components/MkMarquee.vue'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import MkButton from '@/components/MkButton.vue'; -import XNote from '@/components/MkNote.vue'; import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue'; -import { host, instanceName } from '@/config'; +import MkTimeline from '@/components/MkTimeline.vue'; +import { instanceName } from '@/config'; import * as os from '@/os'; -import number from '@/filters/number'; import { i18n } from '@/i18n'; +import { instance } from '@/instance'; -let meta = $ref(); -let stats = $ref(); -let tags = $ref(); -let onlineUsersCount = $ref(); -let instances = $ref(); +let meta = $ref<Instance>(); +let instances = $ref<any[]>(); os.api('meta', { detail: true }).then(_meta => { meta = _meta; }); -os.api('stats').then(_stats => { - stats = _stats; -}); - -os.api('get-online-users-count').then(res => { - onlineUsersCount = res.count; -}); - -os.api('hashtags/list', { - sort: '+mentionedLocalUsers', - limit: 8, -}).then(_tags => { - tags = _tags; -}); - -os.api('federation/instances', { +os.apiGet('federation/instances', { sort: '+pubSub', limit: 20, }).then(_instances => { @@ -125,99 +114,103 @@ function showMenu(ev) { }, }], ev.currentTarget ?? ev.target); } + +function exploreOtherServers() { + // TODO: 言語をよã—ãªã« + window.open('https://join.misskey.page/ja-JP/instances', '_blank'); +} </script> <style lang="scss" scoped> .rsqzvsbo { - > .top { - display: flex; - text-align: center; - min-height: 100vh; - box-sizing: border-box; - padding: 16px; - - > .bg { - position: absolute; - top: 0; - right: 0; - width: 80%; // 100%ã‹ã‚‰shapeã®å¹…を引ã„ã¦ã„ã‚‹ - height: 100%; - } + > .bg { + position: fixed; + top: 0; + right: 0; + width: 80vw; // 100%ã‹ã‚‰shapeã®å¹…を引ã„ã¦ã„ã‚‹ + height: 100vh; + } - > .tl { - position: absolute; - top: 0; - bottom: 0; - right: 64px; - margin: auto; - width: 500px; - height: calc(100% - 128px); - overflow: hidden; - -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); - mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); - - @media (max-width: 1200px) { - display: none; - } + > .tl { + position: fixed; + top: 0; + bottom: 0; + right: 64px; + margin: auto; + padding: 128px 0; + width: 500px; + height: calc(100% - 256px); + overflow: hidden; + -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); + mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%); + + @media (max-width: 1200px) { + display: none; } + } - > .shape1 { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: var(--accent); - clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%); - } - > .shape2 { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: var(--accent); - clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%); - opacity: 0.5; + > .shape1 { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: var(--accent); + clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%); + } + > .shape2 { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: var(--accent); + clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%); + opacity: 0.5; + } + + > .misskey { + position: fixed; + top: 42px; + left: 42px; + width: 140px; + + @media (max-width: 450px) { + width: 130px; } + } - > .misskey { - position: absolute; - top: 42px; - left: 42px; - width: 140px; + > .emojis { + position: fixed; + bottom: 32px; + left: 35px; - @media (max-width: 450px) { - width: 130px; - } + > * { + margin-right: 8px; } - > .emojis { - position: absolute; - bottom: 32px; - left: 35px; + @media (max-width: 1200px) { + display: none; + } + } - > * { - margin-right: 8px; - } + > .contents { + position: relative; + width: min(430px, calc(100% - 32px)); + margin-left: 128px; + padding: 150px 0 100px 0; - @media (max-width: 1200px) { - display: none; - } + @media (max-width: 1200px) { + margin: auto; } > .main { position: relative; - width: min(480px, 100%); - margin: auto auto auto 128px; background: var(--panel); border-radius: var(--radius); box-shadow: 0 12px 32px rgb(0 0 0 / 25%); - - @media (max-width: 1200px) { - margin: auto; - } - + text-align: center; + > .icon { width: 85px; margin-top: -47px; @@ -266,25 +259,44 @@ function showMenu(ev) { } } - > .federation { - position: absolute; - bottom: 16px; - left: 0; - right: 0; - margin: auto; - background: var(--acrylicPanel); - -webkit-backdrop-filter: var(--blur, blur(15px)); - backdrop-filter: var(--blur, blur(15px)); - border-radius: 999px; + > .tl { + position: relative; + background: var(--panel); + border-radius: var(--radius); overflow: clip; - width: 800px; - padding: 8px 0; + box-shadow: 0 12px 32px rgb(0 0 0 / 25%); + margin-top: 16px; + + > .title { + padding: 12px 16px; + border-bottom: solid 1px var(--divider); + } - @media (max-width: 900px) { - display: none; + > .body { + height: 350px; + overflow: auto; } } } + + > .federation { + position: fixed; + bottom: 16px; + left: 0; + right: 0; + margin: auto; + background: var(--acrylicPanel); + -webkit-backdrop-filter: var(--blur, blur(15px)); + backdrop-filter: var(--blur, blur(15px)); + border-radius: 999px; + overflow: clip; + width: 800px; + padding: 8px 0; + + @media (max-width: 900px) { + display: none; + } + } } </style> diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue index ed3c5a4af79c485e4851f2ddffae738ea314e3d1..c34d43dc1c2d28d6bd480b9d7b931771ddd0a220 100644 --- a/packages/frontend/src/pages/welcome.timeline.vue +++ b/packages/frontend/src/pages/welcome.timeline.vue @@ -1,62 +1,54 @@ <template> -<div class="civpbkhh"> - <div ref="scroll" class="scrollbox" v-bind:class="{ scroll: isScrolling }"> - <div v-for="note in notes" class="note"> - <div class="content _panel"> - <div class="body"> +<div :class="$style.root"> + <div ref="scrollEl" :class="[$style.scrollbox, { [$style.scroll]: isScrolling }]"> + <div v-for="note in notes" :key="note.id" :class="$style.note"> + <div class="_panel" :class="$style.content"> + <div :class="$style.body"> <MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i"/> + <Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" /> <MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> </div> - <div v-if="note.files.length > 0" class="richcontent"> - <MkMediaList :media-list="note.files"/> + <div v-if="note.files.length > 0" :class="$style.richcontent"> + <MkMediaList :media-list="note.files" /> </div> <div v-if="note.poll"> - <MkPoll :note="note" :readOnly="true"/> + <MkPoll :note="note" :readOnly="true" /> </div> </div> - <MkReactionsViewer ref="reactionsViewer" :note="note"/> + <MkReactionsViewer ref="reactionsViewer" :note="note" /> </div> </div> </div> </template> -<script lang="ts"> -import { defineComponent } from 'vue'; +<script lang="ts" setup> import MkReactionsViewer from '@/components/MkReactionsViewer.vue'; import MkMediaList from '@/components/MkMediaList.vue'; import MkPoll from '@/components/MkPoll.vue'; import * as os from '@/os'; +import { Note } from 'misskey-js/built/entities'; +import { onUpdated } from 'vue'; +import { getScrollContainer } from '@/scripts/scroll'; -export default defineComponent({ - components: { - MkReactionsViewer, - MkMediaList, - MkPoll, - }, +let notes = $ref<Note[]>([]); +let isScrolling = $ref(false); +let scrollEl = $ref<HTMLElement>(); - data() { - return { - notes: [], - isScrolling: false, - }; - }, - - created() { - os.api('notes/featured').then(notes => { - this.notes = notes; - }); - }, +os.apiGet('notes/featured').then(_notes => { + notes = _notes; +}); - updated() { - if (this.$refs.scroll.clientHeight > window.innerHeight) { - this.isScrolling = true; - } - }, +onUpdated(() => { + if (!scrollEl) return; + const container = getScrollContainer(scrollEl); + const containerHeight = container ? container.clientHeight : window.innerHeight; + if (scrollEl.offsetHeight > containerHeight) { + isScrolling = true; + } }); </script> -<style lang="scss" scoped> +<style lang="scss" module> @keyframes scroll { 0% { transform: translate3d(0, 0, 0); @@ -72,28 +64,28 @@ export default defineComponent({ } } -.civpbkhh { +.root { text-align: right; +} - > .scrollbox { - &.scroll { - animation: scroll 45s linear infinite; - } +.scrollbox { + &.scroll { + animation: scroll 45s linear infinite; + } +} - > .note { - margin: 16px 0 16px auto; +.note { + margin: 16px 0 16px auto; +} - > .content { - padding: 16px; - margin: 0 0 0 auto; - max-width: max-content; - border-radius: 16px; +.content { + padding: 16px; + margin: 0 0 0 auto; + max-width: max-content; + border-radius: 16px; +} - > .richcontent { - min-width: 250px; - } - } - } - } +.richcontent { + min-width: 250px; } </style> diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts index 2ca89b7351f82767f54a723acfc86b8087ccc25a..2616a8a1d5f1d26206eb90f176c9bf789e135e5f 100644 --- a/packages/frontend/src/pizzax.ts +++ b/packages/frontend/src/pizzax.ts @@ -48,8 +48,8 @@ export class Storage<T extends StateDef> { // 簡易的ã«ã‚ューイングã—ã¦å 有ãƒãƒƒã‚¯ã¨ã™ã‚‹ private currentIdbJob: Promise<any> = Promise.resolve(); private addIdbSetJob<T>(job: () => Promise<T>) { - const promise = this.currentIdbJob.then(job, e => { - console.error('Pizzax failed to save data to idb!', e); + const promise = this.currentIdbJob.then(job, err => { + console.error('Pizzax failed to save data to idb!', err); return job(); }); this.currentIdbJob = promise; @@ -130,22 +130,22 @@ export class Storage<T extends StateDef> { await defaultStore.ready; api('i/registry/get-all', { scope: ['client', this.key] }) - .then(kvs => { - const cache: Partial<T> = {}; - for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { - if (v.where === 'account') { - if (Object.prototype.hasOwnProperty.call(kvs, k)) { - this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k]; - cache[k] = (kvs as Partial<T>)[k]; - } else { - this.reactiveState[k].value = this.state[k] = v.default; + .then(kvs => { + const cache: Partial<T> = {}; + for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { + if (v.where === 'account') { + if (Object.prototype.hasOwnProperty.call(kvs, k)) { + this.reactiveState[k].value = this.state[k] = (kvs as Partial<T>)[k]; + cache[k] = (kvs as Partial<T>)[k]; + } else { + this.reactiveState[k].value = this.state[k] = v.default; + } } } - } - return set(this.registryCacheKeyName, cache); - }) - .then(() => resolve()); + return set(this.registryCacheKeyName, cache); + }) + .then(() => resolve()); }, 1); } else { resolve(); diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 900426268967c91b09e924133982d869962d19e5..9521e01910074b6174aa64e45e4a391a12a67182 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -3,7 +3,6 @@ import { Router } from '@/nirax'; import { $i, iAmModerator } from '@/account'; import MkLoading from '@/pages/_loading_.vue'; import MkError from '@/pages/_error_.vue'; -import { ui } from '@/config'; const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({ loader: loader, @@ -199,8 +198,11 @@ export const routes = [{ component: page(() => import('./pages/theme-editor.vue')), loginRequired: true, }, { - path: '/explore/tags/:tag', - component: page(() => import('./pages/explore.vue')), + path: '/roles/:role', + component: page(() => import('./pages/role.vue')), +}, { + path: '/user-tags/:tag', + component: page(() => import('./pages/user-tag.vue')), }, { path: '/explore', component: page(() => import('./pages/explore.vue')), @@ -420,19 +422,6 @@ export const routes = [{ path: '/my/achievements', component: page(() => import('./pages/achievements.vue')), loginRequired: true, -}, { - name: 'messaging', - path: '/my/messaging', - component: page(() => import('./pages/messaging/index.vue')), - loginRequired: true, -}, { - path: '/my/messaging/:userAcct', - component: page(() => import('./pages/messaging/messaging-room.vue')), - loginRequired: true, -}, { - path: '/my/messaging/group/:groupId', - component: page(() => import('./pages/messaging/messaging-room.vue')), - loginRequired: true, }, { path: '/my/drive/folder/:folder', component: page(() => import('./pages/drive.vue')), diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts index fb73c0b4b7057a90331b0c27bf26ce121309c3a3..6b8041d78e38ba71277df0686a8080f326dfe79e 100644 --- a/packages/frontend/src/scripts/aiscript/ui.ts +++ b/packages/frontend/src/scripts/aiscript/ui.ts @@ -1,4 +1,4 @@ -import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; +import { utils, values } from '@syuilo/aiscript'; import { v4 as uuid } from 'uuid'; import { ref, Ref } from 'vue'; diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index b5d2251d28bc66985d6d801c2a0a0ef75dc46295..9da7447bfd3b282c9e5c89a4a95bdba334f5d6e4 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -1,6 +1,5 @@ -import { defineAsyncComponent, Ref, inject } from 'vue'; +import { defineAsyncComponent, Ref } from 'vue'; import * as misskey from 'misskey-js'; -import { pleaseLogin } from './please-login'; import { claimAchievement } from './achievements'; import { $i } from '@/account'; import { i18n } from '@/i18n'; @@ -9,7 +8,6 @@ import * as os from '@/os'; import copyToClipboard from '@/scripts/copy-to-clipboard'; import { url } from '@/config'; import { noteActions } from '@/store'; -import { notePage } from '@/filters/note'; import { miLocalStorage } from '@/local-storage'; export function getNoteMenu(props: { @@ -202,7 +200,7 @@ export function getNoteMenu(props: { props.translating.value = true; const res = await os.api('notes/translate', { noteId: appearNote.id, - targetLang: miLocalStorage.getItem('lang') || navigator.language, + targetLang: miLocalStorage.getItem('lang') ?? navigator.language, }); props.translating.value = false; props.translation.value = res; @@ -242,7 +240,7 @@ export function getNoteMenu(props: { icon: 'ti ti-external-link', text: i18n.ts.showOnRemote, action: () => { - window.open(appearNote.url || appearNote.uri, '_blank'); + window.open(appearNote.url ?? appearNote.uri, '_blank'); }, } : undefined, { @@ -292,7 +290,7 @@ export function getNoteMenu(props: { ...($i.isModerator || $i.isAdmin ? [ null, { - icon: 'fas fa-bullhorn', + icon: 'ti ti-speakerphone', text: i18n.ts.promote, action: promote }] @@ -304,7 +302,7 @@ export function getNoteMenu(props: { icon: 'ti ti-exclamation-circle', text: i18n.ts.reportAbuse, action: () => { - const u = appearNote.url || appearNote.uri || `${url}/notes/${appearNote.id}`; + const u = appearNote.url ?? appearNote.uri ?? `${url}/notes/${appearNote.id}`; os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: appearNote.user, initialComment: `Note: ${u}\n-----\n`, @@ -346,7 +344,7 @@ export function getNoteMenu(props: { icon: 'ti ti-external-link', text: i18n.ts.showOnRemote, action: () => { - window.open(appearNote.url || appearNote.uri, '_blank'); + window.open(appearNote.url ?? appearNote.uri, '_blank'); }, } : undefined] .filter(x => x !== undefined); diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 941d9a0db9b1867c76875c4a7a779deef5bc8697..557b257f62487ef3c72ab9d153c4c5f9248b54cf 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -1,4 +1,3 @@ -import * as Acct from 'misskey-js/built/acct'; import { defineAsyncComponent } from 'vue'; import { i18n } from '@/i18n'; import copyToClipboard from '@/scripts/copy-to-clipboard'; @@ -35,28 +34,6 @@ export function getUserMenu(user, router: Router = mainRouter) { }); } - async function inviteGroup() { - const groups = await os.api('users/groups/owned'); - if (groups.length === 0) { - os.alert({ - type: 'error', - text: i18n.ts.youHaveNoGroups, - }); - return; - } - const { canceled, result: groupId } = await os.select({ - title: i18n.ts.group, - items: groups.map(group => ({ - value: group.id, text: group.name, - })), - }); - if (canceled) return; - os.apiWithDialog('users/groups/invite', { - groupId: groupId, - userId: user.id, - }); - } - async function toggleMute() { if (user.isMuted) { os.apiWithDialog('mute/delete', { @@ -156,20 +133,11 @@ export function getUserMenu(user, router: Router = mainRouter) { action: () => { os.post({ specified: user }); }, - }, meId !== user.id ? { - type: 'link', - icon: 'ti ti-messages', - text: i18n.ts.startMessaging, - to: '/my/messaging/' + Acct.toString(user), - } : undefined, null, { + }, null, { icon: 'ti ti-list', text: i18n.ts.addToList, action: pushList, - }, meId !== user.id ? { - icon: 'ti ti-users', - text: i18n.ts.inviteToGroup, - action: inviteGroup, - } : undefined] as any; + }] as any; if ($i && meId !== user.id) { menu = menu.concat([null, { diff --git a/packages/frontend/src/scripts/get-user-name.ts b/packages/frontend/src/scripts/get-user-name.ts index d499ea02037ba131d797a74e611b733ff5984833..4daf203e06f1a8c5c8528e3af72a0aab55cde132 100644 --- a/packages/frontend/src/scripts/get-user-name.ts +++ b/packages/frontend/src/scripts/get-user-name.ts @@ -1,3 +1,3 @@ export default function(user: { name?: string | null, username: string }): string { - return user.name || user.username; + return user.name === '' ? user.username : user.name ?? user.username; } diff --git a/packages/frontend/src/scripts/hpml/evaluator.ts b/packages/frontend/src/scripts/hpml/evaluator.ts index d4090ea15cf7c85edf18e6139ad8a71143cda9fe..7bddd3f62d016ff6ee0544a8da718be798be0614 100644 --- a/packages/frontend/src/scripts/hpml/evaluator.ts +++ b/packages/frontend/src/scripts/hpml/evaluator.ts @@ -1,11 +1,10 @@ import autobind from 'autobind-decorator'; -import { markRaw, ref, Ref, unref } from 'vue'; +import { ref, Ref, unref } from 'vue'; import { collectPageVars } from '../collect-page-vars'; -import { initHpmlLib, initAiLib } from './lib'; +import { initHpmlLib } from './lib'; import { Expr, isLiteralValue, Variable } from './expr'; import { PageVar, envVarsDef, Fn, HpmlScope, HpmlError } from '.'; import { version } from '@/config'; -import * as os from '@/os'; /** * Hpml evaluator diff --git a/packages/frontend/src/scripts/hpml/index.ts b/packages/frontend/src/scripts/hpml/index.ts index 9a55a5c28616352691471bc993f0f8b85129486f..587c6a36c8320b3c49d144945b213a1a3fc73923 100644 --- a/packages/frontend/src/scripts/hpml/index.ts +++ b/packages/frontend/src/scripts/hpml/index.ts @@ -15,12 +15,12 @@ export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null; export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = { text: { out: 'string', category: 'value', icon: 'ti ti-quote' }, - multiLineText: { out: 'string', category: 'value', icon: 'fas fa-align-left' }, - textList: { out: 'stringArray', category: 'value', icon: 'fas fa-list' }, - number: { out: 'number', category: 'value', icon: 'fas fa-sort-numeric-up' }, - ref: { out: null, category: 'value', icon: 'fas fa-magic' }, - aiScriptVar: { out: null, category: 'value', icon: 'fas fa-magic' }, - fn: { out: 'function', category: 'value', icon: 'fas fa-square-root-alt' }, + multiLineText: { out: 'string', category: 'value', icon: 'ti ti-align-left' }, + textList: { out: 'stringArray', category: 'value', icon: 'ti ti-list' }, + number: { out: 'number', category: 'value', icon: 'ti ti-list-numbers' }, + ref: { out: null, category: 'value', icon: 'ti ti-wand' }, + aiScriptVar: { out: null, category: 'value', icon: 'ti ti-wand' }, + fn: { out: 'function', category: 'value', icon: 'ti ti-math-function' }, }; export const blockDefs = [ @@ -58,7 +58,7 @@ export class HpmlScope { constructor(layerdStates: HpmlScope['layerdStates'], name?: HpmlScope['name']) { this.layerdStates = layerdStates; - this.name = name || 'anonymous'; + this.name = name ?? 'anonymous'; } @autobind diff --git a/packages/frontend/src/scripts/hpml/lib.ts b/packages/frontend/src/scripts/hpml/lib.ts index 02d663b31bc348137e4c83d479557e704daec45a..88db82dd272946371fb5be80d77bfea4d3b42d96 100644 --- a/packages/frontend/src/scripts/hpml/lib.ts +++ b/packages/frontend/src/scripts/hpml/lib.ts @@ -1,4 +1,3 @@ -import tinycolor from 'tinycolor2'; import seedrandom from 'seedrandom'; import { Hpml } from './evaluator'; import { Expr } from './expr'; @@ -130,42 +129,42 @@ export function initAiLib(hpml: Hpml) { export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = { if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: 'ti ti-share' }, - for: { in: ['number', 'function'], out: null, category: 'flow', icon: 'fas fa-recycle' }, - not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' }, - or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' }, - and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' }, + for: { in: ['number', 'function'], out: null, category: 'flow', icon: 'ti ti-recycle' }, + not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: 'ti ti-flag' }, + or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'ti ti-flag' }, + and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'ti ti-flag' }, add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-plus' }, subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-minus' }, multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-x' }, - divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide' }, - mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide' }, - round: { in: ['number'], out: 'number', category: 'operation', icon: 'fas fa-calculator' }, - eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-equals' }, - notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-not-equal' }, - gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than' }, - lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than' }, - gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than-equal' }, - ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than-equal' }, + divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-divide' }, + mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'ti ti-divide' }, + round: { in: ['number'], out: 'number', category: 'operation', icon: 'ti ti-calculator' }, + eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'ti ti-equal' }, + notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'ti ti-equal-not' }, + gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-greater' }, + lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-lower' }, + gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-equal-greater' }, + ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'ti ti-math-equal-lower' }, strLen: { in: ['string'], out: 'number', category: 'text', icon: 'ti ti-quote' }, strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: 'ti ti-quote' }, strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: 'ti ti-quote' }, strReverse: { in: ['string'], out: 'string', category: 'text', icon: 'ti ti-quote' }, join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: 'ti ti-quote' }, - stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: 'fas fa-exchange-alt' }, - numberToString: { in: ['number'], out: 'string', category: 'convert', icon: 'fas fa-exchange-alt' }, - splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: 'fas fa-exchange-alt' }, - pick: { in: [null, 'number'], out: null, category: 'list', icon: 'fas fa-indent' }, - listLen: { in: [null], out: 'number', category: 'list', icon: 'fas fa-indent' }, - rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' }, - dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' }, - seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' }, - random: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' }, - dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' }, - seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' }, - randomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice' }, - dailyRandomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice' }, - seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: 'fas fa-dice' }, - DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: 'fas fa-dice' }, // dailyRandomPickWithProbabilityMapping + stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: 'ti ti-arrows-right-left' }, + numberToString: { in: ['number'], out: 'string', category: 'convert', icon: 'ti ti-arrows-right-left' }, + splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: 'ti ti-arrows-right-left' }, + pick: { in: [null, 'number'], out: null, category: 'list', icon: 'ti ti-indent-increase' }, + listLen: { in: [null], out: 'number', category: 'list', icon: 'ti ti-indent-increase' }, + rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'ti ti-dice' }, + dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'ti ti-dice' }, + seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: 'ti ti-dice' }, + random: { in: ['number'], out: 'boolean', category: 'random', icon: 'ti ti-dice' }, + dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: 'ti ti-dice' }, + seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: 'ti ti-dice' }, + randomPick: { in: [0], out: 0, category: 'random', icon: 'ti ti-dice' }, + dailyRandomPick: { in: [0], out: 0, category: 'random', icon: 'ti ti-dice' }, + seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: 'ti ti-dice' }, + DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: 'ti ti-dice' }, // dailyRandomPickWithProbabilityMapping }; export function initHpmlLib(expr: Expr, scope: HpmlScope, randomSeed: string, visitor?: any) { diff --git a/packages/frontend/src/scripts/hpml/type-checker.ts b/packages/frontend/src/scripts/hpml/type-checker.ts index 24c9ed8bcb219ebc4d50b6f68114debb61002b37..692826fc903f53a1932ec81b92e31bf16495c60b 100644 --- a/packages/frontend/src/scripts/hpml/type-checker.ts +++ b/packages/frontend/src/scripts/hpml/type-checker.ts @@ -63,7 +63,7 @@ export class HpmlTypeChecker { @autobind public getExpectedType(v: Expr, slot: number): Type { - const def = funcDefs[v.type || '']; + const def = funcDefs[v.type ?? '']; if (def == null) { throw new Error('Unknown type: ' + v.type); } @@ -107,7 +107,7 @@ export class HpmlTypeChecker { return pageVar.type; } - const envVar = envVarsDef[v.value || '']; + const envVar = envVarsDef[v.value ?? '']; if (envVar !== undefined) { return envVar; } diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts index e84eebf103420c137737709fa1dfb18bc59cb869..cb4500220215d53a73b1a581706e0746c5ee5ffd 100644 --- a/packages/frontend/src/scripts/popup-position.ts +++ b/packages/frontend/src/scripts/popup-position.ts @@ -1,4 +1,3 @@ -import { Ref } from 'vue'; export function calcPopupPosition(el: HTMLElement, props: { anchorElement: HTMLElement | null; diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts index e3d9dc00c25c18c1827b7e031feba25fe880c2d8..a002f02b5a176e8195d1e7d8bf5f75e172d2de08 100644 --- a/packages/frontend/src/scripts/scroll.ts +++ b/packages/frontend/src/scripts/scroll.ts @@ -10,7 +10,7 @@ export function getScrollContainer(el: HTMLElement | null): HTMLElement | null { } } -export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top: number = 0) { +export function getStickyTop(el: HTMLElement, container: HTMLElement | null = null, top = 0) { if (!el.parentElement) return top; const data = el.dataset.stickyContainerHeaderHeight; const newTop = data ? Number(data) + top : top; @@ -23,14 +23,14 @@ export function getScrollPosition(el: HTMLElement | null): number { return container == null ? window.scrollY : container.scrollTop; } -export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) { +export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { // ã¨ã‚Šã‚ãˆãšè©•ä¾¡ã—ã¦ã¿ã‚‹ if (isTopVisible(el)) { cb(); if (once) return null; } - const container = getScrollContainer(el) || window; + const container = getScrollContainer(el) ?? window; const onScroll = ev => { if (!document.body.contains(el)) return; @@ -45,7 +45,7 @@ export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance: numbe return removeListener; } -export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: number = 1, once: boolean = false) { +export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) { const container = getScrollContainer(el); // ã¨ã‚Šã‚ãˆãšè©•ä¾¡ã—ã¦ã¿ã‚‹ @@ -54,7 +54,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance: nu if (once) return null; } - const containerOrWindow = container || window; + const containerOrWindow = container ?? window; const onScroll = ev => { if (!document.body.contains(el)) return; if (isBottomVisible(el, 1, container)) { @@ -104,12 +104,12 @@ export function scrollToBottom( } else { window.scroll({ top: (el.scrollHeight - window.innerHeight + getStickyTop(el, container) + (window.innerWidth <= 500 ? 96 : 0)) || 0, - ...options + ...options, }); } } -export function isTopVisible(el: HTMLElement, tolerance: number = 1): boolean { +export function isTopVisible(el: HTMLElement, tolerance = 1): boolean { const scrollTop = getScrollPosition(el); return scrollTop <= tolerance; } @@ -124,6 +124,6 @@ export function getBodyScrollHeight() { return Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, - document.body.clientHeight, document.documentElement.clientHeight + document.body.clientHeight, document.documentElement.clientHeight, ); } diff --git a/packages/frontend/src/scripts/search.ts b/packages/frontend/src/scripts/search.ts index 64914d3d65a31a35dc95e035fda2f1b3716f4b2c..69f1586b77c7bc68f82e04b7feed20375432ab44 100644 --- a/packages/frontend/src/scripts/search.ts +++ b/packages/frontend/src/scripts/search.ts @@ -35,7 +35,7 @@ export async function search() { // TODO //v.$root.$emit('warp', date); os.alert({ - icon: 'fas fa-history', + icon: 'ti ti-history', iconOnly: true, autoClose: true, }); return; diff --git a/packages/frontend/src/scripts/use-leave-guard.ts b/packages/frontend/src/scripts/use-leave-guard.ts index a93b84d1feff18436c5584c44f7a6e9c1a1f4f46..146b012471e9e3d477239f3101bc8c15d0dd3cea 100644 --- a/packages/frontend/src/scripts/use-leave-guard.ts +++ b/packages/frontend/src/scripts/use-leave-guard.ts @@ -1,6 +1,4 @@ -import { inject, onUnmounted, Ref } from 'vue'; -import { i18n } from '@/i18n'; -import * as os from '@/os'; +import { Ref } from 'vue'; export function useLeaveGuard(enabled: Ref<boolean>) { /* TODO diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 46e55900cd9a43df20a5d5b382500aeb8c919bac..54c159ed6bb0e2bb76bcd0dfeb161a7211db163a 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -1,6 +1,5 @@ import { markRaw, ref } from 'vue'; import { Storage } from './pizzax'; -import { Theme } from './scripts/theme'; interface PostFormAction { title: string, diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts index aa1244665b2713875de16467ee26e4f607f477e9..580c7da007b3fd5182b6df94384b638d22f90619 100644 --- a/packages/frontend/src/theme-store.ts +++ b/packages/frontend/src/theme-store.ts @@ -1,13 +1,13 @@ -import { api } from '@/os'; -import { $i } from '@/account'; import { Theme } from './scripts/theme'; import { miLocalStorage } from './local-storage'; +import { api } from '@/os'; +import { $i } from '@/account'; const lsCacheKey = $i ? `themes:${$i.id}` as const : null; export function getThemes(): Theme[] { if ($i == null) return []; - return JSON.parse(miLocalStorage.getItem(lsCacheKey!) || '[]'); + return JSON.parse(miLocalStorage.getItem(lsCacheKey!) ?? '[]'); } export async function fetchThemes(): Promise<void> { diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index 342b3f2dbf8511ad594f4b36ff371441f4e7716e..976345f9ee3ac6ce1b7579fe53d230e6f8a87adb 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -30,11 +30,11 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, nextTick } from 'vue'; +import { defineAsyncComponent } from 'vue'; import * as misskey from 'misskey-js'; import { swInject } from './sw-inject'; import XNotification from './notification.vue'; -import { popup, popups, pendingApiRequestsCount } from '@/os'; +import { popups, pendingApiRequestsCount } from '@/os'; import { uploads } from '@/scripts/upload'; import * as sound from '@/scripts/sound'; import { $i } from '@/account'; diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 357db5599f1d6243bbf406b28403af5002a66eeb..935aceea7ce82cdd1908f70fd5ffa4ab954ed258 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -43,7 +43,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; +import { computed, defineAsyncComponent, toRef } from 'vue'; import { openInstanceMenu } from './common'; import * as os from '@/os'; import { navbarItemDef } from '@/navbar'; diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue index 70d683d755a57fd8e3a9bf365413a5b935b08b82..fe95460ba4b894be41b61065a3c67e544d1c753d 100644 --- a/packages/frontend/src/ui/_common_/statusbar-federation.vue +++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue @@ -20,13 +20,11 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; +import { ref } from 'vue'; import * as misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; -import { getNoteSummary } from '@/scripts/get-note-summary'; -import { notePage } from '@/filters/note'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy'; const props = defineProps<{ diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue index e59ace8876e10b26461695610f1a073a55eddd42..44b6b278eac309855d069477652415521f005232 100644 --- a/packages/frontend/src/ui/_common_/statusbar-rss.vue +++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue @@ -16,9 +16,8 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; +import { ref } from 'vue'; import MarqueeText from '@/components/MkMarquee.vue'; -import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; import { shuffle } from '@/scripts/shuffle'; diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue index 6fec81de36c3bb79c1c4d7ee4706ce7cda8a943c..16df69d968de38d13e4833edadfa1a7549efe458 100644 --- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue +++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue @@ -20,7 +20,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; +import { ref, watch } from 'vue'; import * as misskey from 'misskey-js'; import MarqueeText from '@/components/MkMarquee.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/ui/_common_/statusbars.vue b/packages/frontend/src/ui/_common_/statusbars.vue index 114ca5be8c0ec2d0570f38bb0fb66c43c3d1d501..f84695c15f8562ed804b752487476a47e1ac9dd5 100644 --- a/packages/frontend/src/ui/_common_/statusbars.vue +++ b/packages/frontend/src/ui/_common_/statusbars.vue @@ -18,8 +18,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; -import * as os from '@/os'; +import { defineAsyncComponent } from 'vue'; import { defaultStore } from '@/store'; const XRss = defineAsyncComponent(() => import('./statusbar-rss.vue')); const XFederation = defineAsyncComponent(() => import('./statusbar-federation.vue')); diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index 02dafcc3b6bf52c2f8a7b4474af44ec98a23d112..a359463d9ba6a3df79cb4a8703fe3894a40403fd 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -41,14 +41,14 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, markRaw, ComputedRef, ref, onMounted, provide } from 'vue'; +import { defineAsyncComponent, ComputedRef, onMounted, provide } from 'vue'; import XSidebar from './classic.sidebar.vue'; import XCommon from './_common_/common.vue'; import { instanceName } from '@/config'; import { StickySidebar } from '@/scripts/sticky-sidebar'; import * as os from '@/os'; import { mainRouter } from '@/router'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { miLocalStorage } from '@/local-storage'; @@ -105,7 +105,7 @@ function onContextmenu(ev: MouseEvent) { type: 'label', text: path, }, { - icon: fullView ? 'fas fa-compress' : 'fas fa-expand', + icon: fullView ? 'ti ti-minimize' : 'ti ti-maximize', text: fullView ? i18n.ts.quitFullView : i18n.ts.fullView, action: () => { fullView = !fullView; @@ -124,8 +124,8 @@ function onAiClick(ev) { } if (window.innerWidth < 1024) { - const currentUI = miLocalStorage.getItem('ui') - miLocalStorage.setItem('ui_temp', currentUI || 'default'); + const currentUI = miLocalStorage.getItem('ui'); + miLocalStorage.setItem('ui_temp', currentUI ?? 'default'); miLocalStorage.setItem('ui', 'default'); location.reload(); } diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index b09721dec96bb7bf5db0ec3687c1698416e4b1f4..4e933595916619e7c6cf22401aca391c46c2e248 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -4,7 +4,7 @@ <div :class="$style.main"> <XStatusBars/> - <div ref="columnsEl" :class="[$style.columns, deckStore.reactiveState.columnAlign.value]" @contextmenu.self.prevent="onContextmenu"> + <div ref="columnsEl" :class="[$style.columns, deckStore.reactiveState.columnAlign.value, { [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu"> <template v-for="ids in layout"> <!-- sectionを利用ã—ã¦ã„ã‚‹ã®ã¯ã€deck.vueå´ã§columnã«å¯¾ã—ã¦first-of-typeを効ã‹ã›ã‚‹ãŸã‚ --> <section @@ -83,7 +83,7 @@ </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, onMounted, provide, ref, watch } from 'vue'; +import { computed, defineAsyncComponent, ref, watch } from 'vue'; import { v4 as uuid } from 'uuid'; import XCommon from './_common_/common.vue'; import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store'; @@ -115,6 +115,7 @@ window.addEventListener('resize', () => { isMobile.value = window.innerWidth <= 500; }); +const snapScroll = isMobile; const drawerMenuShowing = ref(false); const route = 'TODO'; @@ -297,9 +298,14 @@ async function deleteProfile() { margin-right: auto; } } + + &.snapScroll { + scroll-snap-type: x mandatory; + } } .column { + scroll-snap-align: start; flex-shrink: 0; border-right: solid var(--deckDividerThickness) var(--deckDivider); diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue index 4c69c8e8e8ac85c3170db748667790ac72943e86..76a8b6e760b98e42da80b1b513ad06361b73be4d 100644 --- a/packages/frontend/src/ui/deck/antenna-column.vue +++ b/packages/frontend/src/ui/deck/antenna-column.vue @@ -4,7 +4,7 @@ <i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span> </template> - <XTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/> + <MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @after="() => emit('loaded')"/> </XColumn> </template> @@ -12,7 +12,7 @@ import { onMounted } from 'vue'; import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -26,7 +26,7 @@ const emit = defineEmits<{ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void; }>(); -let timeline = $shallowRef<InstanceType<typeof XTimeline>>(); +let timeline = $shallowRef<InstanceType<typeof MkTimeline>>(); onMounted(() => { if (props.column.antennaId == null) { diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index 5a84237c80b8caf6346e6d9fe8a83fe71eeb82a6..4c6b41e42e64b8762c9a064661abc5e0b3d94e82 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -8,7 +8,7 @@ <div style="padding: 8px; text-align: center;"> <MkButton primary gradate rounded inline @click="post"><i class="ti ti-pencil"></i></MkButton> </div> - <XTimeline ref="timeline" src="channel" :channel="column.channelId" @after="() => emit('loaded')"/> + <MkTimeline ref="timeline" src="channel" :channel="column.channelId" @after="() => emit('loaded')"/> </template> </XColumn> </template> @@ -17,7 +17,7 @@ import { } from 'vue'; import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -32,14 +32,16 @@ const emit = defineEmits<{ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void; }>(); -let timeline = $shallowRef<InstanceType<typeof XTimeline>>(); +let timeline = $shallowRef<InstanceType<typeof MkTimeline>>(); if (props.column.channelId == null) { setChannel(); } async function setChannel() { - const channels = await os.api('channels/followed'); + const channels = await os.api('channels/followed', { + limit: 100, + }); const { canceled, result: channel } = await os.select({ title: i18n.ts.selectChannel, items: channels.map(x => ({ diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue index 339421a13ef91ffd44bc776b3f95d790aa7f8aa5..38ee37de27512377fc2072526242edcfe2b70617 100644 --- a/packages/frontend/src/ui/deck/column.vue +++ b/packages/frontend/src/ui/deck/column.vue @@ -29,8 +29,8 @@ </template> <script lang="ts" setup> -import { onBeforeUnmount, onMounted, provide, Ref, watch } from 'vue'; -import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column, deckStore } from './deck-store'; +import { onBeforeUnmount, onMounted, provide, watch } from 'vue'; +import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { MenuItem } from '@/types/menu'; diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index 80c202a2ef57faf0dbba1c70cb89ff10ceaed5fc..1420ad8b30ab23827cd8163f6e28db2825f0a1b9 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -2,7 +2,6 @@ import { throttle } from 'throttle-debounce'; import { markRaw } from 'vue'; import { notificationTypes } from 'misskey-js'; import { Storage } from '../../pizzax'; -import { i18n } from '@/i18n'; import { api } from '@/os'; import { deepClone } from '@/scripts/clone'; diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue index 75b018cacdb624701c0e62177160bd524cd5aafc..15b76c4d92bcf2f4f5bd7e9632c5f02959d0f6bb 100644 --- a/packages/frontend/src/ui/deck/direct-column.vue +++ b/packages/frontend/src/ui/deck/direct-column.vue @@ -2,15 +2,15 @@ <XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)"> <template #header><i class="ti ti-mail" style="margin-right: 8px;"></i>{{ column.name }}</template> - <XNotes :pagination="pagination"/> + <MkNotes :pagination="pagination"/> </XColumn> </template> <script lang="ts" setup> import { } from 'vue'; import XColumn from './column.vue'; -import XNotes from '@/components/MkNotes.vue'; import { Column } from './deck-store'; +import MkNotes from '@/components/MkNotes.vue'; defineProps<{ column: Column; diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue index 58633e56723ce4f780265021257d1e522551d024..352c1d246a4a3e54b0de58f1d7efef8da41528a6 100644 --- a/packages/frontend/src/ui/deck/list-column.vue +++ b/packages/frontend/src/ui/deck/list-column.vue @@ -4,7 +4,7 @@ <i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span> </template> - <XTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/> + <MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" @after="() => emit('loaded')"/> </XColumn> </template> @@ -12,7 +12,7 @@ import { } from 'vue'; import XColumn from './column.vue'; import { updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; @@ -26,7 +26,7 @@ const emit = defineEmits<{ (ev: 'parent-focus', direction: 'up' | 'down' | 'left' | 'right'): void; }>(); -let timeline = $shallowRef<InstanceType<typeof XTimeline>>(); +let timeline = $shallowRef<InstanceType<typeof MkTimeline>>(); if (props.column.listId == null) { setList(); diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue index 0c66172397e2e4e440a412a10c34f008bf581dfe..f3826a8d31eb09b23cf70a6e482b2ff1409b8ed3 100644 --- a/packages/frontend/src/ui/deck/main-column.vue +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -18,7 +18,7 @@ import { deckStore, Column } from '@/ui/deck/deck-store'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { mainRouter } from '@/router'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; defineProps<{ column: Column; diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue index 16962956a051d5e0f23a5428819427c39ce72a59..852d7a8f7e2f1f932585cfcf4a30374f700ac8c0 100644 --- a/packages/frontend/src/ui/deck/mentions-column.vue +++ b/packages/frontend/src/ui/deck/mentions-column.vue @@ -2,15 +2,15 @@ <XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)"> <template #header><i class="ti ti-at" style="margin-right: 8px;"></i>{{ column.name }}</template> - <XNotes :pagination="pagination"/> + <MkNotes :pagination="pagination"/> </XColumn> </template> <script lang="ts" setup> import { } from 'vue'; import XColumn from './column.vue'; -import XNotes from '@/components/MkNotes.vue'; import { Column } from './deck-store'; +import MkNotes from '@/components/MkNotes.vue'; defineProps<{ column: Column; diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 8bde7e4ce3a01a7a195f871dec413f726f095a7e..a947e27e57e7248c89a086ceda5e57c68ddf62af 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -15,7 +15,7 @@ </p> <p :class="$style.disabledDescription">{{ $t('disabled-timeline.description') }}</p> </div> - <XTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')"/> + <MkTimeline v-else-if="column.tl" ref="timeline" :key="column.tl" :src="column.tl" @after="() => emit('loaded')"/> </XColumn> </template> @@ -23,10 +23,9 @@ import { onMounted } from 'vue'; import XColumn from './column.vue'; import { removeColumn, updateColumn, Column } from './deck-store'; -import XTimeline from '@/components/MkTimeline.vue'; +import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os'; import { $i } from '@/account'; -import { instance } from '@/instance'; import { i18n } from '@/i18n'; const props = defineProps<{ diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 65a1ce0fcea748e2f0f8e242721b4a8ca714ba35..beae799f5c6dd2e474138418099bfc08de41c516 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -84,7 +84,7 @@ </template> <script lang="ts" setup> -import { defineAsyncComponent, provide, onMounted, computed, ref, watch, ComputedRef } from 'vue'; +import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef } from 'vue'; import XCommon from './_common_/common.vue'; import { instanceName } from '@/config'; import { StickySidebar } from '@/scripts/sticky-sidebar'; @@ -94,9 +94,8 @@ import { defaultStore } from '@/store'; import { navbarItemDef } from '@/navbar'; import { i18n } from '@/i18n'; import { $i } from '@/account'; -import { Router } from '@/nirax'; import { mainRouter } from '@/router'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { deviceKind } from '@/scripts/device-kind'; import { miLocalStorage } from '@/local-storage'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue index 797e2aa6c3fa45643db422c2076d149fdf8a2b49..6c96440ebd94820fe994850418caea8222781749 100644 --- a/packages/frontend/src/ui/visitor.vue +++ b/packages/frontend/src/ui/visitor.vue @@ -4,7 +4,7 @@ </template> <script lang="ts"> -import { defineComponent, defineAsyncComponent } from 'vue'; +import { defineComponent } from 'vue'; //import DesignA from './visitor/a.vue'; import DesignB from './visitor/b.vue'; import XCommon from './_common_/common.vue'; diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue index 058a9482fad36c641cfd61cb6e80b74d51f8ab37..163f038b43a2d9281221c7fb69a4d9fa114ef3b0 100644 --- a/packages/frontend/src/ui/visitor/b.vue +++ b/packages/frontend/src/ui/visitor/b.vue @@ -11,7 +11,10 @@ <div class="contents"> <XHeader v-if="!root" class="header"/> - <main style="container-type: inline-size;"> + <main v-if="!root" style="container-type: inline-size;"> + <RouterView/> + </main> + <main v-else> <RouterView/> </main> <div v-if="!root" class="powered-by"> @@ -58,13 +61,11 @@ import { host, instanceName } from '@/config'; import { search } from '@/scripts/search'; import * as os from '@/os'; import { instance } from '@/instance'; -import MkPagination from '@/components/MkPagination.vue'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; -import MkButton from '@/components/MkButton.vue'; import { ColdDeviceStorage, defaultStore } from '@/store'; import { mainRouter } from '@/router'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; const DESKTOP_THRESHOLD = 1100; @@ -83,7 +84,7 @@ const announcements = { limit: 10, }; -const isTimelineAvailable = instance.policies.ltlAvailable || instance.policies.gtlAvailable; +const isTimelineAvailable = $ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable); let showMenu = $ref(false); let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD); diff --git a/packages/frontend/src/ui/visitor/kanban.vue b/packages/frontend/src/ui/visitor/kanban.vue index 51e47f277df896566fb2956c12b18d46662bc63b..05ded834eecb91aa9199cafe6df8c6eeeb52980d 100644 --- a/packages/frontend/src/ui/visitor/kanban.vue +++ b/packages/frontend/src/ui/visitor/kanban.vue @@ -38,7 +38,7 @@ </template> <script lang="ts"> -import { defineComponent, defineAsyncComponent } from 'vue'; +import { defineComponent } from 'vue'; import { host, instanceName } from '@/config'; import * as os from '@/os'; import MkPagination from '@/components/MkPagination.vue'; diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue index d8fda1f7c308b09b3b4390f842189fc82fcfee09..628390e3f7bd2750c9d144a63bcdc243595c8aa8 100644 --- a/packages/frontend/src/ui/zen.vue +++ b/packages/frontend/src/ui/zen.vue @@ -10,7 +10,7 @@ import { provide, ComputedRef } from 'vue'; import XCommon from './_common_/common.vue'; import { mainRouter } from '@/router'; -import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; import { instanceName } from '@/config'; let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue index da1cab6f88852fbaaee58bdf2c93e8b028954588..7acf2140cf9559f589b6727ac4c0d7290bf6eade 100644 --- a/packages/frontend/src/widgets/WidgetActivity.vue +++ b/packages/frontend/src/widgets/WidgetActivity.vue @@ -15,8 +15,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import XCalendar from './WidgetActivity.calendar.vue'; import XChart from './WidgetActivity.chart.vue'; import { GetFormResultType } from '@/scripts/form'; diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue index 1bb3089d161ffe4731a8139baac950e652f5bef7..cb055a56f654d9e7a54d8c22089de95dc989a314 100644 --- a/packages/frontend/src/widgets/WidgetAichan.vue +++ b/packages/frontend/src/widgets/WidgetAichan.vue @@ -5,8 +5,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { onMounted, onUnmounted, shallowRef } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; const name = 'ai'; diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue index 9e489227ff513b789ed6f5a79b942cf882cfad00..33218a110b7f6f279ff64e34ce4412bb141b6d1d 100644 --- a/packages/frontend/src/widgets/WidgetAiscript.vue +++ b/packages/frontend/src/widgets/WidgetAiscript.vue @@ -14,9 +14,9 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref, watch } from 'vue'; +import { ref } from 'vue'; import { Interpreter, Parser, utils } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue index 9a2b60eb05c6c383b37e6e81cd7ebf7f48fdf792..455a6e6ea56444f50e5261fb3a60e8013202a56e 100644 --- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue +++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue @@ -8,16 +8,16 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, Ref, ref, watch } from 'vue'; -import { Interpreter, Parser, utils, values } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { onMounted, Ref, ref, watch } from 'vue'; +import { Interpreter, Parser } from '@syuilo/aiscript'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import { createAiScriptEnv } from '@/scripts/aiscript/api'; import { $i } from '@/account'; import MkAsUi from '@/components/MkAsUi.vue'; import MkContainer from '@/components/MkContainer.vue'; -import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; +import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui'; const name = 'aiscriptApp'; diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue index 6c2c366aa2a6383d1dcbc4e0c881c8d17e0f6e0c..462f1e5a5ddf55962479840955fa21ada2ca486a 100644 --- a/packages/frontend/src/widgets/WidgetButton.vue +++ b/packages/frontend/src/widgets/WidgetButton.vue @@ -7,9 +7,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref, watch } from 'vue'; -import { Interpreter, Parser, utils } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { Interpreter, Parser } from '@syuilo/aiscript'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import { createAiScriptEnv } from '@/scripts/aiscript/api'; diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue index fe31e10215fa436fd923ec71a7b2e14709a94f0e..083d8588af6af9447d4ad5e45f3745f8423b761a 100644 --- a/packages/frontend/src/widgets/WidgetCalendar.vue +++ b/packages/frontend/src/widgets/WidgetCalendar.vue @@ -33,8 +33,8 @@ </template> <script lang="ts" setup> -import { onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { i18n } from '@/i18n'; import { useInterval } from '@/scripts/use-interval'; diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index 66c3fbd9d264aff8e794e936a9a8fe32c0c2d598..981788a3c53161f2a7889f6addc9df4fc8712eac 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -7,10 +7,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, Ref, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; -import { $i } from '@/account'; import MkContainer from '@/components/MkContainer.vue'; import MkClickerGame from '@/components/MkClickerGame.vue'; diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue index 7f9c96b93b2577d10263ef21e10205a360c45bd9..ecbb03b570381a5615c2b51d2c62a50e3d7347dd 100644 --- a/packages/frontend/src/widgets/WidgetClock.vue +++ b/packages/frontend/src/widgets/WidgetClock.vue @@ -19,7 +19,7 @@ <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import MkAnalogClock from '@/components/MkAnalogClock.vue'; diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue index 0f9c46f1c700b88a5061dce83d65d735cef60926..1780a1c8d20ec2d121021e15ab0021309e31d43f 100644 --- a/packages/frontend/src/widgets/WidgetDigitalClock.vue +++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue @@ -9,8 +9,7 @@ </template> <script lang="ts" setup> -import { onUnmounted, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { timezones } from '@/scripts/timezones'; import MkDigitalClock from '@/components/MkDigitalClock.vue'; diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue index 806c5d1801d3e7c96ddaf859adb858c1b631dfb1..a8095acf6575840fa1a16ebf64bb3bb34d2a2ce7 100644 --- a/packages/frontend/src/widgets/WidgetFederation.vue +++ b/packages/frontend/src/widgets/WidgetFederation.vue @@ -20,8 +20,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue index 1068c5ac4b03c6b57316d9fef9c0dc8201dbc38a..b1578076559cbd8def3c84dc6606df7aa509d2e9 100644 --- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue +++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue @@ -14,7 +14,7 @@ <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import MkTagCloud from '@/components/MkTagCloud.vue'; diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index 990802d847704eef1692fd6b07f72c65ea938426..3a3b071b7d581d82d5e56cf9f1c888f96d0eb57c 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -15,8 +15,7 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, Ref, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { host } from '@/config'; diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue index e19cbb185a6ca7d46ee5d42f869370255fc92221..69912e21f7edefd127d7fae20e5a67eb73acfb50 100644 --- a/packages/frontend/src/widgets/WidgetJobQueue.vue +++ b/packages/frontend/src/widgets/WidgetJobQueue.vue @@ -46,13 +46,12 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, reactive, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { onUnmounted, reactive } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { stream } from '@/stream'; import number from '@/filters/number'; import * as sound from '@/scripts/sound'; -import * as os from '@/os'; import { deepClone } from '@/scripts/clone'; const name = 'jobQueue'; diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue index 689dbe0e6ff74c0b24f808c0bd5502e73c57381a..149d20af47e72ddef8477022cb9221b4edfe0153 100644 --- a/packages/frontend/src/widgets/WidgetMemo.vue +++ b/packages/frontend/src/widgets/WidgetMemo.vue @@ -11,10 +11,9 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref, watch } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; -import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue index e4ae37950cdd3c2127e5e8ef9bc8094af01e715d..63400b09d0ae4d32644e459eb631a9bf36a9f7d0 100644 --- a/packages/frontend/src/widgets/WidgetNotifications.vue +++ b/packages/frontend/src/widgets/WidgetNotifications.vue @@ -12,7 +12,7 @@ <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import XNotifications from '@/components/MkNotifications.vue'; diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue index 705be1c8a7e0c7514300a971d90ce2900fee9ed0..a096cc8fe8f7dacb5a18a604acfaf7d7d6f68c99 100644 --- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue +++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue @@ -7,8 +7,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue index 2d6f0a5ec7411fa358cb4d527e54bc9e3e186538..8746ababbb92bb3d9da6087a0885ca99b0195a1e 100644 --- a/packages/frontend/src/widgets/WidgetPhotos.vue +++ b/packages/frontend/src/widgets/WidgetPhotos.vue @@ -17,8 +17,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, reactive, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { onUnmounted, ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { stream } from '@/stream'; import { getStaticImageUrl } from '@/scripts/media-proxy'; diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue index f8bebcbf96a398f326e67fb6a4dd44df80fcb762..9953bca65f811b99be3a543af6b33584830b84b0 100644 --- a/packages/frontend/src/widgets/WidgetPostForm.vue +++ b/packages/frontend/src/widgets/WidgetPostForm.vue @@ -4,7 +4,7 @@ <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkPostForm from '@/components/MkPostForm.vue'; diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue index 7e8ac429e59a04d3bf28a0558419886c18622ffa..819663a3662e36a7fddd4bcd29a435dc88765841 100644 --- a/packages/frontend/src/widgets/WidgetProfile.vue +++ b/packages/frontend/src/widgets/WidgetProfile.vue @@ -17,8 +17,7 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, Ref, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import { $i } from '@/account'; import { userPage } from '@/filters/user'; diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index e212003f3407369d905beb0c3da90cc8d2427338..965bb891532f7b9161c15c0c2eee3c75968ad748 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -19,7 +19,7 @@ <script lang="ts" setup> import { ref, watch, computed } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import { url as base } from '@/config'; diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue index a7488f4ca2082f87fcdc80f9be2b2f37e9b524a4..b0408f0d7f76365a00c7564d2f5a40c5067ebaaf 100644 --- a/packages/frontend/src/widgets/WidgetRssTicker.vue +++ b/packages/frontend/src/widgets/WidgetRssTicker.vue @@ -23,7 +23,7 @@ <script lang="ts" setup> import { ref, watch, computed } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import MarqueeText from '@/components/MkMarquee.vue'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue index c2ad23b31141f5fbb3b4aa058bf070143c6dddc7..ffb77b281a1cef991e85871f35c930fe077b588c 100644 --- a/packages/frontend/src/widgets/WidgetSlideshow.vue +++ b/packages/frontend/src/widgets/WidgetSlideshow.vue @@ -12,8 +12,8 @@ </template> <script lang="ts" setup> -import { nextTick, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { onMounted, ref, shallowRef } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index e6abf561b9a50acbc18a572531eb71df8946ad91..d6be6532a61b36572806cd657999203592fc36d9 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -16,19 +16,18 @@ </template> <div> - <XTimeline :key="widgetProps.src === 'list' ? `list:${widgetProps.list.id}` : widgetProps.src === 'antenna' ? `antenna:${widgetProps.antenna.id}` : widgetProps.src" :src="widgetProps.src" :list="widgetProps.list ? widgetProps.list.id : null" :antenna="widgetProps.antenna ? widgetProps.antenna.id : null"/> + <MkTimeline :key="widgetProps.src === 'list' ? `list:${widgetProps.list.id}` : widgetProps.src === 'antenna' ? `antenna:${widgetProps.antenna.id}` : widgetProps.src" :src="widgetProps.src" :list="widgetProps.list ? widgetProps.list.id : null" :antenna="widgetProps.antenna ? widgetProps.antenna.id : null"/> </div> </MkContainer> </template> <script lang="ts" setup> -import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import * as os from '@/os'; import MkContainer from '@/components/MkContainer.vue'; -import XTimeline from '@/components/MkTimeline.vue'; -import { $i } from '@/account'; +import MkTimeline from '@/components/MkTimeline.vue'; import { i18n } from '@/i18n'; const name = 'timeline'; diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue index 76651c1d0f609579f77b044c3d78650266c4ff85..1423ae076cf27397ceff58db3bac1b69ea2316c7 100644 --- a/packages/frontend/src/widgets/WidgetTrends.vue +++ b/packages/frontend/src/widgets/WidgetTrends.vue @@ -19,8 +19,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue index cf85ac782c4df09d081b8f5e131818c9d7b3c297..22162d2b2c920b139cb9a4632ee8082560a86f90 100644 --- a/packages/frontend/src/widgets/WidgetUnixClock.vue +++ b/packages/frontend/src/widgets/WidgetUnixClock.vue @@ -12,7 +12,7 @@ <script lang="ts" setup> import { onUnmounted, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; const name = 'unixClock'; diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue index cc49c92550f351386740e418eec24a6babc0b481..b8811d2fed4202ed57fd012def800e5e6776ea12 100644 --- a/packages/frontend/src/widgets/WidgetUserList.vue +++ b/packages/frontend/src/widgets/WidgetUserList.vue @@ -19,8 +19,7 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from './widget'; import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/MkContainer.vue'; import * as os from '@/os'; diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue index f79858db26aeaaf07f5f42fab1853ae41e7c0f77..72c88d9a0092bcd3051560000b210d5877c21875 100644 --- a/packages/frontend/src/widgets/server-metric/index.vue +++ b/packages/frontend/src/widgets/server-metric/index.vue @@ -15,8 +15,8 @@ </template> <script lang="ts" setup> -import { onMounted, onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from '../widget'; +import { onUnmounted, ref } from 'vue'; +import { useWidgetPropsManager, Widget, WidgetComponentExpose } from '../widget'; import XCpuMemory from './cpu-mem.vue'; import XNet from './net.vue'; import XCpu from './cpu.vue'; diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue index 6018eb4265dde1693a3a60c71fc318ca733c2a49..d6a5d021adac1b31fea387b053b6acaea983cbf9 100644 --- a/packages/frontend/src/widgets/server-metric/mem.vue +++ b/packages/frontend/src/widgets/server-metric/mem.vue @@ -2,7 +2,7 @@ <div class="zlxnikvl"> <XPie class="pie" :value="usage"/> <div> - <p><i class="fas fa-memory"></i>RAM</p> + <p><i class="ti ti-section"></i>RAM</p> <p>Total: {{ bytes(total, 1) }}</p> <p>Used: {{ bytes(used, 1) }}</p> <p>Free: {{ bytes(free, 1) }}</p> diff --git a/packages/frontend/vite.json5.ts b/packages/frontend/vite.json5.ts index 0a37fbff44e156fe2d00fae5e045a24057f91a6d..87b67c2142414a94763808ebd83e06cdab3d588c 100644 --- a/packages/frontend/vite.json5.ts +++ b/packages/frontend/vite.json5.ts @@ -5,6 +5,13 @@ import { Plugin } from 'rollup'; import { createFilter, dataToEsm } from '@rollup/pluginutils'; import { RollupJsonOptions } from '@rollup/plugin-json'; +// json5 extends SyntaxError with additional fields (without subclassing) +// https://github.com/json5/json5/blob/de344f0619bda1465a6e25c76f1c0c3dda8108d9/lib/parse.js#L1111-L1112 +interface Json5SyntaxError extends SyntaxError { + lineNumber: number; + columnNumber: number; +} + export default function json5(options: RollupJsonOptions = {}): Plugin { const filter = createFilter(options.include, options.exclude); const indent = 'indent' in options ? options.indent : '\t'; @@ -28,9 +35,12 @@ export default function json5(options: RollupJsonOptions = {}): Plugin { map: { mappings: '' }, }; } catch (err) { - const message = 'Could not parse JSON file'; - const position = parseInt(/[\d]/.exec(err.message)[0], 10); - this.warn({ message, id, position }); + if (!(err instanceof SyntaxError)) { + throw err; + } + const message = 'Could not parse JSON5 file'; + const { lineNumber, columnNumber } = err as Json5SyntaxError; + this.warn({ message, id, loc: { line: lineNumber, column: columnNumber } }); return null; } }, diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 6d38a9fb9f436536b0c0d7298cb46d0f97c8e4fa..7c979a93dcd3b4988fdffb78540e942fe24dd4ff 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -77,6 +77,17 @@ module.exports = { '@typescript-eslint/prefer-nullish-coalescing': [ 'error', ], + '@typescript-eslint/naming-convention': [ + 'error', + { + "selector": "typeLike", + "format": ["PascalCase"] + }, + { + "selector": "typeParameter", + "format": [] + } + ], 'import/no-unresolved': ['off'], 'import/no-default-export': ['warn'], 'import/order': ['warn', { diff --git a/packages/sw/package.json b/packages/sw/package.json index 8650f9597b7a1f35c8ce3188134e4be654adcf3b..7fab90a243bb48344fa3ffeb741b794a56d878cd 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -4,7 +4,9 @@ "scripts": { "watch": "node build.js watch", "build": "node build.js", - "lint": "tsc --noEmit && eslint --quiet src/**/*.ts" + "typecheck": "tsc --noEmit", + "eslint": "eslint --quiet src/**/*.ts", + "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { "esbuild": "0.14.42", @@ -12,9 +14,9 @@ "misskey-js": "0.0.15" }, "devDependencies": { - "@typescript-eslint/parser": "5.51.0", + "@typescript-eslint/parser": "5.52.0", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.62", - "eslint": "8.33.0", + "eslint": "8.34.0", "eslint-plugin-import": "2.27.5", "typescript": "4.9.5" } diff --git a/packages/sw/src/@types/global.d.ts b/packages/sw/src/@types/global.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..5aaef9412ca36ead24f99ccb0dfb1f3833987ada --- /dev/null +++ b/packages/sw/src/@types/global.d.ts @@ -0,0 +1,7 @@ +type FIXME = any; + +declare const _LANGS_: string[][]; +declare const _VERSION_: string; +declare const _ENV_: string; +declare const _DEV_: boolean; +declare const _PERF_PREFIX_: string; diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 6744687fcc568385b8a0e6d2ed5ec83ea8f0cf17..da92b37d198650194c989f22c1f4b4625422fe7d 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -10,6 +10,12 @@ import { getAccountFromId } from '@/scripts/get-account-from-id'; import { char2fileName } from '@/scripts/twemoji-base'; import * as url from '@/scripts/url'; +const closeNotificationsByTags = async (tags: string[]) => { + for (const n of (await Promise.all(tags.map(tag => globalThis.registration.getNotifications({ tag })))).flat()) { + n.close(); + } +}; + const iconUrl = (name: badgeNames) => `/static-assets/tabler-badges/${name}.png`; /* How to add a new badge: * 1. Find the icon and download png from https://tabler-icons.io/ @@ -23,14 +29,14 @@ export async function createNotification<K extends keyof pushNotificationDataMap const n = await composeNotification(data); if (n) { - return self.registration.showNotification(...n); + return globalThis.registration.showNotification(...n); } else { console.error('Could not compose notification', data); return createEmptyNotification(); } } -async function composeNotification<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]): Promise<[string, NotificationOptions] | null> { +async function composeNotification(data: pushNotificationDataMap[keyof pushNotificationDataMap]): Promise<[string, NotificationOptions] | null> { if (!swLang.i18n) swLang.fetchLocale(); const i18n = await swLang.i18n as I18n<any>; const { t } = i18n; @@ -66,7 +72,7 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(data case 'mention': return [t('_notification.youGotMention', { name: getUserName(data.body.user) }), { - body: data.body.note.text || '', + body: data.body.note.text ?? '', icon: data.body.user.avatarUrl, badge: iconUrl('at'), data, @@ -80,7 +86,7 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(data case 'reply': return [t('_notification.youGotReply', { name: getUserName(data.body.user) }), { - body: data.body.note.text || '', + body: data.body.note.text ?? '', icon: data.body.user.avatarUrl, badge: iconUrl('arrow-back-up'), data, @@ -94,7 +100,7 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(data case 'renote': return [t('_notification.youRenoted', { name: getUserName(data.body.user) }), { - body: data.body.note.text || '', + body: data.body.note.text ?? '', icon: data.body.user.avatarUrl, badge: iconUrl('repeat'), data, @@ -108,7 +114,7 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(data case 'quote': return [t('_notification.youGotQuote', { name: getUserName(data.body.user) }), { - body: data.body.note.text || '', + body: data.body.note.text ?? '', icon: data.body.user.avatarUrl, badge: iconUrl('quote'), data, @@ -161,8 +167,9 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(data badge = iconUrl('plus'); } + const tag = `reaction:${data.body.note.id}`; return [`${reaction} ${getUserName(data.body.user)}`, { - body: data.body.note.text || '', + body: data.body.note.text ?? '', icon: data.body.user.avatarUrl, badge, data, @@ -209,53 +216,19 @@ async function composeNotification<K extends keyof pushNotificationDataMap>(data data, }]; - case 'groupInvited': - return [t('_notification.youWereInvitedToGroup', { userName: getUserName(data.body.user) }), { - body: data.body.invitation.group.name, - badge: iconUrl('users'), - data, - actions: [ - { - action: 'accept', - title: t('accept'), - }, - { - action: 'reject', - title: t('reject'), - }, - ], - }]; - case 'app': - return [data.body.header || data.body.body, { - body: data.body.header && data.body.body, - icon: data.body.icon, + return [data.body.header ?? data.body.body, { + body: data.body.header ? data.body.body : '', + icon: data.body.icon ?? undefined, data, }]; default: return null; } - case 'unreadMessagingMessage': - if (data.body.groupId === null) { - return [t('_notification.youGotMessagingMessageFromUser', { name: getUserName(data.body.user) }), { - icon: data.body.user.avatarUrl, - badge: iconUrl('messages'), - tag: `messaging:user:${data.body.userId}`, - data, - renotify: true, - }]; - } - return [t('_notification.youGotMessagingMessageFromGroup', { name: data.body.group.name }), { - icon: data.body.user.avatarUrl, - badge: iconUrl('messages'), - tag: `messaging:group:${data.body.groupId}`, - data, - renotify: true, - }]; case 'unreadAntennaNote': return [t('_notification.unreadAntennaNote', { name: data.body.antenna.name }), { - body: `${getUserName(data.body.note.user)}: ${data.body.note.text || ''}`, + body: `${getUserName(data.body.note.user)}: ${data.body.note.text ?? ''}`, icon: data.body.note.user.avatarUrl, badge: iconUrl('antenna'), tag: `antenna:${data.body.antenna.id}`, @@ -272,8 +245,8 @@ export async function createEmptyNotification() { if (!swLang.i18n) swLang.fetchLocale(); const i18n = await swLang.i18n as I18n<any>; const { t } = i18n; - - await self.registration.showNotification( + + await globalThis.registration.showNotification( t('_notification.emptyPushNotificationMessage'), { silent: true, @@ -282,16 +255,11 @@ export async function createEmptyNotification() { }, ); - res(); - setTimeout(async () => { - for (const n of - [ - ...(await self.registration.getNotifications({ tag: 'user_visible_auto_notification' })), - ...(await self.registration.getNotifications({ tag: 'read_notification' })), - ] - ) { - n.close(); + try { + await closeNotificationsByTags(['user_visible_auto_notification', 'read_notification']); + } finally { + res(); } }, 1000); }); diff --git a/packages/sw/src/scripts/get-user-name.ts b/packages/sw/src/scripts/get-user-name.ts index d499ea02037ba131d797a74e611b733ff5984833..4daf203e06f1a8c5c8528e3af72a0aab55cde132 100644 --- a/packages/sw/src/scripts/get-user-name.ts +++ b/packages/sw/src/scripts/get-user-name.ts @@ -1,3 +1,3 @@ export default function(user: { name?: string | null, username: string }): string { - return user.name || user.username; + return user.name === '' ? user.username : user.name ?? user.username; } diff --git a/packages/sw/src/scripts/notification-read.ts b/packages/sw/src/scripts/notification-read.ts index 5ad748b849e93f24a69c21a65c10173dcc6852b2..3b1dde0cd563a0c23d078dff5d4df36ae9162297 100644 --- a/packages/sw/src/scripts/notification-read.ts +++ b/packages/sw/src/scripts/notification-read.ts @@ -28,7 +28,7 @@ class SwNotificationReadManager { } // プッシュ通知ã®æ—¢èªã‚’サーãƒãƒ¼ã«é€ä¿¡ - public async read<K extends keyof pushNotificationDataMap>(data: pushNotificationDataMap[K]) { + public async read(data: pushNotificationDataMap[keyof pushNotificationDataMap]) { if (data.type !== 'notification' || !(data.userId in this.accounts)) return; const account = this.accounts[data.userId]; diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index 719e3ea9fae013e632f628b0bb7670eb7fdd96f3..8936a7763afe37d18e3f7123285879f88ffe593c 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -32,18 +32,10 @@ export function openAntenna(antennaId: string, loginId: string) { return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId }); } -export async function openChat(body: any, loginId: string) { - if (body.groupId === null) { - return openClient('push', `/my/messaging/${getAcct(body.user)}`, loginId, { body }); - } else { - return openClient('push', `/my/messaging/group/${body.groupId}`, loginId, { body }); - } -} - // post-formã®ã‚ªãƒ—ションã‹ã‚‰æŠ•ç¨¿ãƒ•ã‚©ãƒ¼ãƒ ã‚’é–‹ã export async function openPost(options: any, loginId: string) { // クエリを作æˆã—ã¦ãŠã - let url = `/share?`; + let url = '/share?'; if (options.initialText) url += `text=${options.initialText}&`; if (options.reply) url += `replyId=${options.reply.id}&`; if (options.renote) url += `renoteId=${options.renote.id}&`; @@ -59,12 +51,12 @@ export async function openClient(order: swMessageOrderType, url: string, loginId return client; } - return self.clients.openWindow(getUrlWithLoginId(url, loginId)); + return globalThis.clients.openWindow(getUrlWithLoginId(url, loginId)); } export async function findClient() { - const clients = await self.clients.matchAll({ - type: 'window' + const clients = await globalThis.clients.matchAll({ + type: 'window', }); for (const c of clients) { if (c.url.indexOf('?zen') < 0) return c; diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index d47563939a6bf37e17739c9dc2a8e3dab756caab..6f4c487354f42e36a0579b2ddff2d06c47125436 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -5,23 +5,23 @@ import { pushNotificationDataMap } from '@/types'; import * as swos from '@/scripts/operations'; import { acct as getAcct } from '@/filters/user'; -self.addEventListener('install', ev => { - ev.waitUntil(self.skipWaiting()); +globalThis.addEventListener('install', ev => { + //ev.waitUntil(globalThis.skipWaiting()); }); -self.addEventListener('activate', ev => { +globalThis.addEventListener('activate', ev => { ev.waitUntil( caches.keys() .then(cacheNames => Promise.all( cacheNames .filter((v) => v !== swLang.cacheName) - .map(name => caches.delete(name)) + .map(name => caches.delete(name)), )) - .then(() => self.clients.claim()) + .then(() => globalThis.clients.claim()), ); }); -self.addEventListener('fetch', ev => { +globalThis.addEventListener('fetch', ev => { let isHTMLRequest = false; if (ev.request.headers.get('sec-fetch-dest') === 'document') { isHTMLRequest = true; @@ -34,87 +34,68 @@ self.addEventListener('fetch', ev => { if (!isHTMLRequest) return; ev.respondWith( fetch(ev.request) - .catch(() => new Response(`Offline. Service Worker @${_VERSION_}`, { status: 200 })) + .catch(() => new Response(`Offline. Service Worker @${_VERSION_}`, { status: 200 })), ); }); -self.addEventListener('push', ev => { +globalThis.addEventListener('push', ev => { // クライアントå–å¾— - ev.waitUntil(self.clients.matchAll({ + ev.waitUntil(globalThis.clients.matchAll({ includeUncontrolled: true, - type: 'window' - }).then(async <K extends keyof pushNotificationDataMap>(clients: readonly WindowClient[]) => { - const data: pushNotificationDataMap[K] = ev.data?.json(); + type: 'window', + }).then(async (clients: readonly WindowClient[]) => { + const data: pushNotificationDataMap[keyof pushNotificationDataMap] = ev.data?.json(); switch (data.type) { // case 'driveFileCreated': case 'notification': - case 'unreadMessagingMessage': case 'unreadAntennaNote': // 1日以上経éŽã—ã¦ã„ã‚‹å ´åˆã¯ç„¡è¦– if ((new Date()).getTime() - data.dateTime > 1000 * 60 * 60 * 24) break; - // クライアントãŒã‚ã£ãŸã‚‰ã‚¹ãƒˆãƒªãƒ¼ãƒ ã«æŽ¥ç¶šã—ã¦ã„ã‚‹ã¨ã„ã†ã“ã¨ãªã®ã§é€šçŸ¥ã—ãªã„ - if (clients.length !== 0) break; - return createNotification(data); case 'readAllNotifications': - for (const n of await self.registration.getNotifications()) { + for (const n of await globalThis.registration.getNotifications()) { if (n?.data?.type === 'notification') n.close(); } break; - case 'readAllMessagingMessages': - for (const n of await self.registration.getNotifications()) { - if (n?.data?.type === 'unreadMessagingMessage') n.close(); - } - break; case 'readAllAntennas': - for (const n of await self.registration.getNotifications()) { + for (const n of await globalThis.registration.getNotifications()) { if (n?.data?.type === 'unreadAntennaNote') n.close(); } break; case 'readNotifications': - for (const n of await self.registration.getNotifications()) { - if (data.body?.notificationIds?.includes(n.data.body.id)) { + for (const n of await globalThis.registration.getNotifications()) { + if (data.body.notificationIds.includes(n.data.body.id)) { n.close(); } } break; - case 'readAllMessagingMessagesOfARoom': - for (const n of await self.registration.getNotifications()) { - if (n.data.type === 'unreadMessagingMessage' - && ('userId' in data.body - ? data.body.userId === n.data.body.userId - : data.body.groupId === n.data.body.groupId) - ) { - n.close(); - } - } - break; case 'readAntenna': - for (const n of await self.registration.getNotifications()) { - if (n?.data?.type === 'unreadAntennaNote' && data.body?.antennaId === n.data.body.antenna.id) { + for (const n of await globalThis.registration.getNotifications()) { + if (n?.data?.type === 'unreadAntennaNote' && data.body.antennaId === n.data.body.antenna.id) { n.close(); } } break; } - return createEmptyNotification(); + await createEmptyNotification(); + return; })); }); -self.addEventListener('notificationclick', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => { +globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => { ev.waitUntil((async () => { if (_DEV_) { console.log('notificationclick', ev.action, ev.notification.data); } - + const { action, notification } = ev; - const data: pushNotificationDataMap[K] = notification.data; + const data: pushNotificationDataMap[keyof pushNotificationDataMap] = notification.data; const { userId: loginId } = data; let client: WindowClient | null = null; - + switch (data.type) { case 'notification': switch (action) { @@ -135,9 +116,6 @@ self.addEventListener('notificationclick', <K extends keyof pushNotificationData case 'receiveFollowRequest': await swos.api('following/requests/accept', loginId, { userId: data.body.userId }); break; - case 'groupInvited': - await swos.api('users/groups/invitations/accept', loginId, { invitationId: data.body.invitation.id }); - break; } break; case 'reject': @@ -145,9 +123,6 @@ self.addEventListener('notificationclick', <K extends keyof pushNotificationData case 'receiveFollowRequest': await swos.api('following/requests/reject', loginId, { userId: data.body.userId }); break; - case 'groupInvited': - await swos.api('users/groups/invitations/reject', loginId, { invitationId: data.body.invitation.id }); - break; } break; case 'showFollowRequests': @@ -158,9 +133,6 @@ self.addEventListener('notificationclick', <K extends keyof pushNotificationData case 'receiveFollowRequest': client = await swos.openClient('push', '/my/follow-requests', loginId); break; - case 'groupInvited': - client = await swos.openClient('push', '/my/groups', loginId); - break; case 'reaction': client = await swos.openNote(data.body.note.id, loginId); break; @@ -174,48 +146,45 @@ self.addEventListener('notificationclick', <K extends keyof pushNotificationData } } break; - case 'unreadMessagingMessage': - client = await swos.openChat(data.body, loginId); - break; case 'unreadAntennaNote': client = await swos.openAntenna(data.body.antenna.id, loginId); } - + if (client) { client.focus(); } if (data.type === 'notification') { swNotificationRead.then(that => that.read(data)); } - + notification.close(); })()); }); -self.addEventListener('notificationclose', <K extends keyof pushNotificationDataMap>(ev: ServiceWorkerGlobalScopeEventMap['notificationclose']) => { - const data: pushNotificationDataMap[K] = ev.notification.data; +globalThis.addEventListener('notificationclose', (ev: ServiceWorkerGlobalScopeEventMap['notificationclose']) => { + const data: pushNotificationDataMap[keyof pushNotificationDataMap] = ev.notification.data; if (data.type === 'notification') { swNotificationRead.then(that => that.read(data)); } }); -self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => { +globalThis.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => { ev.waitUntil((async () => { switch (ev.data) { case 'clear': // Cache Storage全削除 await caches.keys() .then(cacheNames => Promise.all( - cacheNames.map(name => caches.delete(name)) + cacheNames.map(name => caches.delete(name)), )); return; // TODO } - + if (typeof ev.data === 'object') { // E.g. '[object Array]' → 'array' const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase(); - + if (otype === 'object') { if (ev.data.msg === 'initialize') { swLang.setLang(ev.data.lang); diff --git a/packages/sw/src/types.ts b/packages/sw/src/types.ts index 3b35de407969bcda6855912753b073f99d691dbb..5b53ddecac317f32707475d3888cb5f0d890f5df 100644 --- a/packages/sw/src/types.ts +++ b/packages/sw/src/types.ts @@ -13,15 +13,12 @@ export type SwMessage = { // Defined also @/core/PushNotificationService.ts#L12 type pushNotificationDataSourceMap = { notification: Misskey.entities.Notification; - unreadMessagingMessage: Misskey.entities.MessagingMessage; unreadAntennaNote: { antenna: { id: string, name: string }; note: Misskey.entities.Note; }; readNotifications: { notificationIds: string[] }; readAllNotifications: undefined; - readAllMessagingMessages: undefined; - readAllMessagingMessagesOfARoom: { userId: string } | { groupId: string }; readAntenna: { antennaId: string }; readAllAntennas: undefined; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d255b7a42cadf0cb00392ec6ad78c680e662678..e10bc0748796e3f0d5854669d4cf1f7837f30130 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,11 +11,11 @@ importers: '@tensorflow/tfjs-core': 4.2.0 '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 - '@typescript-eslint/eslint-plugin': 5.51.0 - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/eslint-plugin': 5.52.0 + '@typescript-eslint/parser': 5.52.0 cross-env: 7.0.3 - cypress: 12.5.1 - eslint: 8.33.0 + cypress: 12.6.0 + eslint: 8.34.0 execa: 5.1.1 gulp: 4.0.2 gulp-cssnano: 2.1.3 @@ -23,7 +23,7 @@ importers: gulp-replace: 1.1.4 gulp-terser: 2.1.0 js-yaml: 4.1.0 - start-server-and-test: 1.15.3 + start-server-and-test: 1.15.4 typescript: 4.9.5 dependencies: execa: 5.1.1 @@ -39,35 +39,35 @@ importers: devDependencies: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 - '@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm cross-env: 7.0.3 - cypress: 12.5.1 - eslint: 8.33.0 - start-server-and-test: 1.15.3 + cypress: 12.6.0 + eslint: 8.34.0 + start-server-and-test: 1.15.4 packages/backend: specifiers: - '@bull-board/api': 4.11.1 - '@bull-board/fastify': 4.11.1 - '@bull-board/ui': 4.11.1 + '@bull-board/api': 4.12.1 + '@bull-board/fastify': 4.12.1 + '@bull-board/ui': 4.12.1 '@discordapp/twemoji': 14.0.2 '@fastify/accepts': 4.1.0 '@fastify/cookie': 8.3.0 '@fastify/cors': 8.2.0 '@fastify/http-proxy': 8.4.0 - '@fastify/multipart': 7.4.0 - '@fastify/static': 6.8.0 + '@fastify/multipart': 7.4.1 + '@fastify/static': 6.9.0 '@fastify/view': 7.4.1 - '@jest/globals': 29.4.2 - '@nestjs/common': 9.3.7 - '@nestjs/core': 9.3.7 - '@nestjs/testing': 9.3.7 + '@jest/globals': 29.4.3 + '@nestjs/common': 9.3.9 + '@nestjs/core': 9.3.9 + '@nestjs/testing': 9.3.9 '@peertube/http-signature': 1.7.0 '@redocly/openapi-core': 1.0.0-beta.123 '@sinonjs/fake-timers': 10.0.2 - '@swc/cli': 0.1.61 - '@swc/core': 1.3.34 + '@swc/cli': 0.1.62 + '@swc/core': 1.3.35 '@swc/jest': 0.2.24 '@tensorflow/tfjs': 4.2.0 '@tensorflow/tfjs-node': 4.2.0 @@ -87,7 +87,7 @@ importers: '@types/jsonld': 1.5.8 '@types/jsrsasign': 10.5.5 '@types/mime-types': 2.1.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/node-fetch': 3.0.3 '@types/nodemailer': 6.4.7 '@types/oauth': 0.9.1 @@ -103,7 +103,6 @@ importers: '@types/semver': 7.3.13 '@types/sharp': 0.31.1 '@types/sinonjs__fake-timers': 8.1.2 - '@types/speakeasy': 2.0.7 '@types/tinycolor2': 1.4.3 '@types/tmp': 0.2.3 '@types/unzipper': 0.10.5 @@ -112,16 +111,16 @@ importers: '@types/web-push': 3.3.2 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0 - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/eslint-plugin': 5.52.0 + '@typescript-eslint/parser': 5.52.0 accepts: 1.3.8 ajv: 8.12.0 archiver: 5.3.1 autwh: 0.1.0 - aws-sdk: 2.1295.0 + aws-sdk: 2.1318.0 bcryptjs: 2.4.3 - blurhash: 2.0.4 - bull: 4.10.3 + blurhash: 2.0.5 + bull: 4.10.4 cacheable-lookup: 6.1.0 cbor: 8.1.0 chalk: 5.2.0 @@ -134,21 +133,22 @@ importers: date-fns: 2.29.3 deep-email-validator: 0.1.21 escape-regexp: 0.0.1 - eslint: 8.33.0 + eslint: 8.34.0 eslint-plugin-import: 2.27.5 execa: 6.1.0 - fastify: 4.12.0 + fastify: 4.13.0 feed: 4.2.2 - file-type: 18.2.0 + file-type: 18.2.1 fluent-ffmpeg: 2.1.2 form-data: 4.0.0 got: 12.5.3 + happy-dom: ^8.7.0 hpagent: 1.2.0 ioredis: 4.28.5 ip-cidr: 3.1.0 is-svg: 4.3.2 - jest: 29.4.2 - jest-mock: 29.4.2 + jest: 29.4.3 + jest-mock: 29.4.3 js-yaml: 4.1.0 jsdom: 21.1.0 json5: 2.2.3 @@ -164,6 +164,7 @@ importers: nsfwjs: 2.4.2 oauth: 0.10.0 os-utils: 0.0.14 + otpauth: ^9.0.2 parse5: 7.1.2 pg: 8.9.0 private-ip: 3.0.0 @@ -183,15 +184,14 @@ importers: rss-parser: 3.12.0 rxjs: 7.8.0 s-age: 1.1.2 - sanitize-html: 2.9.0 + sanitize-html: 2.10.0 seedrandom: 3.0.5 semver: 7.3.8 sharp: 0.31.3 - speakeasy: 2.0.0 strict-event-emitter-types: 2.0.0 stringz: 2.1.0 - summaly: 2.7.0 - systeminformation: 5.17.8 + summaly: github:misskey-dev/summaly + systeminformation: 5.17.9 tinycolor2: 1.6.0 tmp: 0.2.1 tsc-alias: 1.8.2 @@ -205,33 +205,33 @@ importers: vary: 1.1.2 web-push: 3.5.0 websocket: 1.0.34 - ws: 8.12.0 + ws: 8.12.1 xev: 3.0.2 dependencies: - '@bull-board/api': 4.11.1 - '@bull-board/fastify': 4.11.1 - '@bull-board/ui': 4.11.1 + '@bull-board/api': 4.12.1 + '@bull-board/fastify': 4.12.1 + '@bull-board/ui': 4.12.1 '@discordapp/twemoji': 14.0.2 '@fastify/accepts': 4.1.0 '@fastify/cookie': 8.3.0 '@fastify/cors': 8.2.0 '@fastify/http-proxy': 8.4.0 - '@fastify/multipart': 7.4.0 - '@fastify/static': 6.8.0 + '@fastify/multipart': 7.4.1 + '@fastify/static': 6.9.0 '@fastify/view': 7.4.1 - '@nestjs/common': 9.3.7_mnr6j2del53muneqly5h4y27ai - '@nestjs/core': 9.3.7_ojttuksvundfk3amlyo674va74 - '@nestjs/testing': 9.3.7_zmctrozi6xxh3beaccl6ebop6u + '@nestjs/common': 9.3.9_mnr6j2del53muneqly5h4y27ai + '@nestjs/core': 9.3.9_jrq2rdgfp2sx67wmylmrqliwxe + '@nestjs/testing': 9.3.9_77foi4w27ghy47yutmnzv7krjy '@peertube/http-signature': 1.7.0 '@sinonjs/fake-timers': 10.0.2 accepts: 1.3.8 ajv: 8.12.0 archiver: 5.3.1 autwh: 0.1.0 - aws-sdk: 2.1295.0 + aws-sdk: 2.1318.0 bcryptjs: 2.4.3 - blurhash: 2.0.4 - bull: 4.10.3 + blurhash: 2.0.5 + bull: 4.10.4 cacheable-lookup: 6.1.0 cbor: 8.1.0 chalk: 5.2.0 @@ -243,12 +243,13 @@ importers: date-fns: 2.29.3 deep-email-validator: 0.1.21 escape-regexp: 0.0.1 - fastify: 4.12.0 + fastify: 4.13.0 feed: 4.2.2 - file-type: 18.2.0 + file-type: 18.2.1 fluent-ffmpeg: 2.1.2 form-data: 4.0.0 got: 12.5.3 + happy-dom: 8.7.0 hpagent: 1.2.0 ioredis: 4.28.5 ip-cidr: 3.1.0 @@ -268,6 +269,7 @@ importers: nsfwjs: 2.4.2_@tensorflow+tfjs@4.2.0 oauth: 0.10.0 os-utils: 0.0.14 + otpauth: 9.0.2 parse5: 7.1.2 pg: 8.9.0 private-ip: 3.0.0 @@ -287,15 +289,14 @@ importers: rss-parser: 3.12.0 rxjs: 7.8.0 s-age: 1.1.2 - sanitize-html: 2.9.0 + sanitize-html: 2.10.0 seedrandom: 3.0.5 semver: 7.3.8 sharp: 0.31.3 - speakeasy: 2.0.0 strict-event-emitter-types: 2.0.0 stringz: 2.1.0 - summaly: 2.7.0 - systeminformation: 5.17.8 + summaly: github.com/misskey-dev/summaly/51f3870e1ff5e0b22102e804112b10cb72f3c494 + systeminformation: 5.17.9 tinycolor2: 1.6.0 tmp: 0.2.1 tsc-alias: 1.8.2 @@ -309,17 +310,17 @@ importers: vary: 1.1.2 web-push: 3.5.0 websocket: 1.0.34 - ws: 8.12.0 + ws: 8.12.1 xev: 3.0.2 optionalDependencies: '@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5 '@tensorflow/tfjs-node': 4.2.0_seedrandom@3.0.5 devDependencies: - '@jest/globals': 29.4.2 + '@jest/globals': 29.4.3 '@redocly/openapi-core': 1.0.0-beta.123 - '@swc/cli': 0.1.61_nh7rjdlljrxbrljvydz3oxxjt4 - '@swc/core': 1.3.34 - '@swc/jest': 0.2.24_@swc+core@1.3.34 + '@swc/cli': 0.1.62_r2avoeggowhtjk5lmq5oy455q4 + '@swc/core': 1.3.35 + '@swc/jest': 0.2.24_@swc+core@1.3.35 '@types/accepts': 1.3.5 '@types/archiver': 5.3.1 '@types/bcryptjs': 2.4.2 @@ -336,7 +337,7 @@ importers: '@types/jsonld': 1.5.8 '@types/jsrsasign': 10.5.5 '@types/mime-types': 2.1.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/node-fetch': 3.0.3 '@types/nodemailer': 6.4.7 '@types/oauth': 0.9.1 @@ -352,7 +353,6 @@ importers: '@types/semver': 7.3.13 '@types/sharp': 0.31.1 '@types/sinonjs__fake-timers': 8.1.2 - '@types/speakeasy': 2.0.7 '@types/tinycolor2': 1.4.3 '@types/tmp': 0.2.3 '@types/unzipper': 0.10.5 @@ -361,14 +361,14 @@ importers: '@types/web-push': 3.3.2 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm cross-env: 7.0.3 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_yzj2n2b43wonjwaifya6xmk2zy + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4 execa: 6.1.0 - jest: 29.4.2_@types+node@18.13.0 - jest-mock: 29.4.2 + jest: 29.4.3_@types+node@18.14.0 + jest-mock: 29.4.3 packages/frontend: specifiers: @@ -383,7 +383,7 @@ importers: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 '@types/matter-js': 0.18.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/punycode': 2.1.0 '@types/sanitize-html': 2.8.0 '@types/seedrandom': 3.0.4 @@ -392,18 +392,18 @@ importers: '@types/uuid': 9.0.0 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0 - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/eslint-plugin': 5.52.0 + '@typescript-eslint/parser': 5.52.0 '@vitejs/plugin-vue': 4.0.0 '@vue/compiler-sfc': 3.2.47 '@vue/runtime-core': 3.2.47 autobind-decorator: 2.4.0 autosize: 5.0.2 - blurhash: 2.0.4 + blurhash: 2.0.5 broadcast-channel: 4.20.2 browser-image-resizer: git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3 canvas-confetti: 1.6.0 - chart.js: 4.2.0 + chart.js: 4.2.1 chartjs-adapter-date-fns: 3.0.0 chartjs-chart-matrix: 2.0.1 chartjs-plugin-gradient: 0.6.1 @@ -411,10 +411,10 @@ importers: compare-versions: 5.0.1 cropperjs: 2.0.0-beta.2 cross-env: 7.0.3 - cypress: 12.5.1 + cypress: 12.6.0 date-fns: 2.29.3 escape-regexp: 0.0.1 - eslint: 8.33.0 + eslint: 8.34.0 eslint-plugin-import: 2.27.5 eslint-plugin-vue: 9.9.0 eventemitter3: 5.0.0 @@ -423,7 +423,7 @@ importers: insert-text-at-cursor: 0.3.0 is-file-animated: 1.0.2 json5: 2.2.3 - matter-js: 0.18.0 + matter-js: 0.19.0 mfm-js: 0.23.3 misskey-js: 0.0.15 photoswipe: 5.3.5 @@ -431,14 +431,13 @@ importers: punycode: 2.3.0 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.14.0 + rollup: 3.17.2 s-age: 1.1.2 - sanitize-html: 2.9.0 - sass: 1.58.0 + sanitize-html: 2.10.0 + sass: 1.58.3 seedrandom: 3.0.5 - start-server-and-test: 1.15.3 + start-server-and-test: 1.15.4 strict-event-emitter-types: 2.0.0 - stringz: 2.1.0 syuilo-password-strength: 0.0.1 textarea-caret: 3.1.0 three: 0.149.0 @@ -450,33 +449,33 @@ importers: typescript: 4.9.5 uuid: 9.0.0 vanilla-tilt: 1.8.0 - vite: 4.1.1 + vite: 4.1.2 vue: 3.2.47 vue-eslint-parser: 9.1.0 vue-plyr: 7.0.0 vue-prism-editor: 2.0.0-alpha.2 - vue-tsc: 1.0.24 + vue-tsc: 1.1.4 vuedraggable: next dependencies: '@discordapp/twemoji': 14.0.2 - '@rollup/plugin-alias': 4.0.3_rollup@3.14.0 - '@rollup/plugin-json': 6.0.0_rollup@3.14.0 - '@rollup/pluginutils': 5.0.2_rollup@3.14.0 + '@rollup/plugin-alias': 4.0.3_rollup@3.17.2 + '@rollup/plugin-json': 6.0.0_rollup@3.17.2 + '@rollup/pluginutils': 5.0.2_rollup@3.17.2 '@syuilo/aiscript': 0.12.4 '@tabler/icons-webfont': 2.2.0 - '@vitejs/plugin-vue': 4.0.0_vite@4.1.1+vue@3.2.47 + '@vitejs/plugin-vue': 4.0.0_vite@4.1.2+vue@3.2.47 '@vue/compiler-sfc': 3.2.47 autobind-decorator: 2.4.0 autosize: 5.0.2 - blurhash: 2.0.4 + blurhash: 2.0.5 broadcast-channel: 4.20.2 browser-image-resizer: github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a canvas-confetti: 1.6.0 - chart.js: 4.2.0 - chartjs-adapter-date-fns: 3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla - chartjs-chart-matrix: 2.0.1_chart.js@4.2.0 - chartjs-plugin-gradient: 0.6.1_chart.js@4.2.0 - chartjs-plugin-zoom: 2.0.0_chart.js@4.2.0 + chart.js: 4.2.1 + chartjs-adapter-date-fns: 3.0.0_rid3rx6orfiwps7kg2r43n6mvu + chartjs-chart-matrix: 2.0.1_chart.js@4.2.1 + chartjs-plugin-gradient: 0.6.1_chart.js@4.2.1 + chartjs-plugin-zoom: 2.0.0_chart.js@4.2.1 compare-versions: 5.0.1 cropperjs: 2.0.0-beta.2 date-fns: 2.29.3 @@ -487,7 +486,7 @@ importers: insert-text-at-cursor: 0.3.0 is-file-animated: 1.0.2 json5: 2.2.3 - matter-js: 0.18.0 + matter-js: 0.19.0 mfm-js: 0.23.3 misskey-js: 0.0.15 photoswipe: 5.3.5 @@ -495,13 +494,12 @@ importers: punycode: 2.3.0 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.14.0 + rollup: 3.17.2 s-age: 1.1.2 - sanitize-html: 2.9.0 - sass: 1.58.0 + sanitize-html: 2.10.0 + sass: 1.58.3 seedrandom: 3.0.5 strict-event-emitter-types: 2.0.0 - stringz: 2.1.0 syuilo-password-strength: 0.0.1 textarea-caret: 3.1.0 three: 0.149.0 @@ -513,7 +511,7 @@ importers: typescript: 4.9.5 uuid: 9.0.0 vanilla-tilt: 1.8.0 - vite: 4.1.1_gyrp4zacqcjjrmgvdzgac5epyy + vite: 4.1.2_hlkwzk2izwsolfmdrejei4vrty vue: 3.2.47 vue-plyr: 7.0.0 vue-prism-editor: 2.0.0-alpha.2_vue@3.2.47 @@ -524,7 +522,7 @@ importers: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 '@types/matter-js': 0.18.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/punycode': 2.1.0 '@types/sanitize-html': 2.8.0 '@types/seedrandom': 3.0.4 @@ -533,24 +531,24 @@ importers: '@types/uuid': 9.0.0 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.51.0_b635kmla6dsb4frxfihkw4m47e - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/eslint-plugin': 5.52.0_6cfvjsbua5ptj65675bqcn6oza + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm '@vue/runtime-core': 3.2.47 cross-env: 7.0.3 - cypress: 12.5.1 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_yzj2n2b43wonjwaifya6xmk2zy - eslint-plugin-vue: 9.9.0_eslint@8.33.0 - start-server-and-test: 1.15.3 - vue-eslint-parser: 9.1.0_eslint@8.33.0 - vue-tsc: 1.0.24_typescript@4.9.5 + cypress: 12.6.0 + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4 + eslint-plugin-vue: 9.9.0_eslint@8.34.0 + start-server-and-test: 1.15.4 + vue-eslint-parser: 9.1.0_eslint@8.34.0 + vue-tsc: 1.1.4_typescript@4.9.5 packages/sw: specifiers: - '@typescript-eslint/parser': 5.51.0 + '@typescript-eslint/parser': 5.52.0 '@typescript/lib-webworker': npm:@types/serviceworker@0.0.62 esbuild: 0.14.42 - eslint: 8.33.0 + eslint: 8.34.0 eslint-plugin-import: 2.27.5 idb-keyval: 6.2.0 misskey-js: 0.0.15 @@ -560,10 +558,10 @@ importers: idb-keyval: 6.2.0 misskey-js: 0.0.15 devDependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm '@typescript/lib-webworker': /@types/serviceworker/0.0.62 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_yzj2n2b43wonjwaifya6xmk2zy + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4 typescript: 4.9.5 packages: @@ -911,26 +909,26 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@bull-board/api/4.11.1: - resolution: {integrity: sha512-x+CKpLKZ4yJ1CETvoX9cIOMM1ZfsXr6N9pOsRj8/lh0Mo/rbQWbfdlZR1jiALMAKu1RzJnffZcn6uKeFoXMRBQ==} + /@bull-board/api/4.12.1: + resolution: {integrity: sha512-cJk7LhphNZHwbN4yON5rYiDgob3D7cwFGrxf6LlnR1w/a6zyap1x/o74MfV/0T6NpUpvUSf7ccbj3knU6DNYVw==} dependencies: redis-info: 3.1.0 dev: false - /@bull-board/fastify/4.11.1: - resolution: {integrity: sha512-xBTgSZKeLIwjLWsdW3vOJTubO6ZfiOKQSjtZ7n9ZUoiO4gg4m0MWfTBDgBFSgIqB5FjgdsIO6aP7QGsHI1wmng==} + /@bull-board/fastify/4.12.1: + resolution: {integrity: sha512-MAecvp+xTePdEHm4uCooojAlj2isdznfC9XuCjjGHHNCczjm/Wx14cDhKLKwkOFp5ZLhqgtOEulT5YVfVCu+EQ==} dependencies: - '@bull-board/api': 4.11.1 - '@bull-board/ui': 4.11.1 - '@fastify/static': 6.8.0 + '@bull-board/api': 4.12.1 + '@bull-board/ui': 4.12.1 + '@fastify/static': 6.9.0 '@fastify/view': 7.4.1 ejs: 3.1.8 dev: false - /@bull-board/ui/4.11.1: - resolution: {integrity: sha512-jILv2JN8cfgtHRHYk9GNpQh3rI2zaiX6bEfa1yyFRjLKcY4g409MTOOkZvkXlN5I4mBs+V3aob8cmvS2ixESDA==} + /@bull-board/ui/4.12.1: + resolution: {integrity: sha512-nNTjxitLEre+UdZjwMHwlKKGxpg3OV/Sf7dNOlhJYoRjO7aYPb63M+BfGVrYgupk3V73Q0qvl1emtsnF/qz/Wg==} dependencies: - '@bull-board/api': 4.11.1 + '@bull-board/api': 4.12.1 dev: false /@chainsafe/is-ip/2.0.1: @@ -1359,14 +1357,14 @@ packages: resolution: {integrity: sha512-H8nwsmawFtKKRE6uhh1BtF1gQi/l147SmLsDGxB0HdYTHzjXz6uSQO3lEVmY7unKMzbArRjdoJQkEGpScszdSw==} dependencies: '@fastify/reply-from': 8.3.1 - ws: 8.12.0 + ws: 8.12.1 transitivePeerDependencies: - bufferutil - utf-8-validate dev: false - /@fastify/multipart/7.4.0: - resolution: {integrity: sha512-jl8KCMOjzniAMnF2/VdYFhGB03Oqtl24plxcnsKpdnRLu/ihVz4cNPz9bPn8mLUQW4r3dUlh6emINtZdJczkbg==} + /@fastify/multipart/7.4.1: + resolution: {integrity: sha512-PgBJIg/1krx1nWIXGZSUFQMPHXeFv559TmpaswzmNkK6V/yn1xi4nkATqBetv7s5XbXhxzc75uaJgXWdUefxvQ==} dependencies: '@fastify/busboy': 1.1.0 '@fastify/deepmerge': 1.3.0 @@ -1400,14 +1398,14 @@ packages: mime: 3.0.0 dev: false - /@fastify/static/6.8.0: - resolution: {integrity: sha512-MNQp7KM0NIC+722OPN3MholnfvM+Vg2ao4OwbWWNJhAJEWOKGe4fJsEjIh3OkN0z5ymhklc7EXGCG0zDaIU5ZQ==} + /@fastify/static/6.9.0: + resolution: {integrity: sha512-9SBVNJi2+KTnfiW1WjiVXDsmUxliNI54OF1eOiaop264dh8FwXSuLmO62JXvx7+VD0vQXEqsyRbFCYUJ9aJxng==} dependencies: '@fastify/accept-negotiator': 1.0.0 '@fastify/send': 2.0.1 content-disposition: 0.5.4 fastify-plugin: 4.5.0 - glob: 8.0.3 + glob: 8.1.0 p-limit: 3.1.0 readable-stream: 4.3.0 dev: false @@ -1472,20 +1470,20 @@ packages: engines: {node: '>=8'} dev: true - /@jest/console/29.4.2: - resolution: {integrity: sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg==} + /@jest/console/29.4.3: + resolution: {integrity: sha512-W/o/34+wQuXlgqlPYTansOSiBnuxrTv61dEVkA6HNmpcgHLUjfaUbdqt6oVvOzaawwo9IdW9QOtMgQ1ScSZC4A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 - jest-message-util: 29.4.2 - jest-util: 29.4.2 + jest-message-util: 29.4.3 + jest-util: 29.4.3 slash: 3.0.0 dev: true - /@jest/core/29.4.2: - resolution: {integrity: sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ==} + /@jest/core/29.4.3: + resolution: {integrity: sha512-56QvBq60fS4SPZCuM7T+7scNrkGIe7Mr6PVIXUpu48ouvRaWOFqRPV91eifvFM0ay2HmfswXiGf97NGUN5KofQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -1493,32 +1491,32 @@ packages: node-notifier: optional: true dependencies: - '@jest/console': 29.4.2 - '@jest/reporters': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/console': 29.4.3 + '@jest/reporters': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 graceful-fs: 4.2.10 - jest-changed-files: 29.4.2 - jest-config: 29.4.2_@types+node@18.13.0 - jest-haste-map: 29.4.2 - jest-message-util: 29.4.2 - jest-regex-util: 29.4.2 - jest-resolve: 29.4.2 - jest-resolve-dependencies: 29.4.2 - jest-runner: 29.4.2 - jest-runtime: 29.4.2 - jest-snapshot: 29.4.2 - jest-util: 29.4.2 - jest-validate: 29.4.2 - jest-watcher: 29.4.2 + jest-changed-files: 29.4.3 + jest-config: 29.4.3_@types+node@18.14.0 + jest-haste-map: 29.4.3 + jest-message-util: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.4.3 + jest-resolve-dependencies: 29.4.3 + jest-runner: 29.4.3 + jest-runtime: 29.4.3 + jest-snapshot: 29.4.3 + jest-util: 29.4.3 + jest-validate: 29.4.3 + jest-watcher: 29.4.3 micromatch: 4.0.5 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 strip-ansi: 6.0.1 transitivePeerDependencies: @@ -1533,14 +1531,14 @@ packages: '@jest/types': 27.5.1 dev: true - /@jest/environment/29.4.2: - resolution: {integrity: sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ==} + /@jest/environment/29.4.3: + resolution: {integrity: sha512-dq5S6408IxIa+lr54zeqce+QgI+CJT4nmmA+1yzFgtcsGK8c/EyiUb9XQOgz3BMKrRDfKseeOaxj2eO8LlD3lA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/fake-timers': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - jest-mock: 29.4.2 + '@jest/fake-timers': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + jest-mock: 29.4.3 dev: true /@jest/expect-utils/29.3.1: @@ -1550,49 +1548,49 @@ packages: jest-get-type: 29.4.2 dev: true - /@jest/expect-utils/29.4.2: - resolution: {integrity: sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA==} + /@jest/expect-utils/29.4.3: + resolution: {integrity: sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.4.2 + jest-get-type: 29.4.3 dev: true - /@jest/expect/29.4.2: - resolution: {integrity: sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ==} + /@jest/expect/29.4.3: + resolution: {integrity: sha512-iktRU/YsxEtumI9zsPctYUk7ptpC+AVLLk1Ax3AsA4g1C+8OOnKDkIQBDHtD5hA/+VtgMd5AWI5gNlcAlt2vxQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - expect: 29.4.2 - jest-snapshot: 29.4.2 + expect: 29.4.3 + jest-snapshot: 29.4.3 transitivePeerDependencies: - supports-color dev: true - /@jest/fake-timers/29.4.2: - resolution: {integrity: sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ==} + /@jest/fake-timers/29.4.3: + resolution: {integrity: sha512-4Hote2MGcCTWSD2gwl0dwbCpBRHhE6olYEuTj8FMowdg3oQWNKr2YuxenPQYZ7+PfqPY1k98wKDU4Z+Hvd4Tiw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@sinonjs/fake-timers': 10.0.2 - '@types/node': 18.13.0 - jest-message-util: 29.4.2 - jest-mock: 29.4.2 - jest-util: 29.4.2 + '@types/node': 18.14.0 + jest-message-util: 29.4.3 + jest-mock: 29.4.3 + jest-util: 29.4.3 dev: true - /@jest/globals/29.4.2: - resolution: {integrity: sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg==} + /@jest/globals/29.4.3: + resolution: {integrity: sha512-8BQ/5EzfOLG7AaMcDh7yFCbfRLtsc+09E1RQmRBI4D6QQk4m6NSK/MXo+3bJrBN0yU8A2/VIcqhvsOLFmziioA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/expect': 29.4.2 - '@jest/types': 29.4.2 - jest-mock: 29.4.2 + '@jest/environment': 29.4.3 + '@jest/expect': 29.4.3 + '@jest/types': 29.4.3 + jest-mock: 29.4.3 transitivePeerDependencies: - supports-color dev: true - /@jest/reporters/29.4.2: - resolution: {integrity: sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw==} + /@jest/reporters/29.4.3: + resolution: {integrity: sha512-sr2I7BmOjJhyqj9ANC6CTLsL4emMoka7HkQpcoMRlhCbQJjz2zsRzw0BDPiPyEFDXAbxKgGFYuQZiSJ1Y6YoTg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -1601,12 +1599,12 @@ packages: optional: true dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 + '@jest/console': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 '@jridgewell/trace-mapping': 0.3.17 - '@types/node': 18.13.0 + '@types/node': 18.14.0 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -1617,9 +1615,9 @@ packages: istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 - jest-message-util: 29.4.2 - jest-util: 29.4.2 - jest-worker: 29.4.2 + jest-message-util: 29.4.3 + jest-util: 29.4.3 + jest-worker: 29.4.3 slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 @@ -1635,8 +1633,15 @@ packages: '@sinclair/typebox': 0.25.21 dev: true - /@jest/source-map/29.4.2: - resolution: {integrity: sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q==} + /@jest/schemas/29.4.3: + resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.25.21 + dev: true + + /@jest/source-map/29.4.3: + resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jridgewell/trace-mapping': 0.3.17 @@ -1644,41 +1649,41 @@ packages: graceful-fs: 4.2.10 dev: true - /@jest/test-result/29.4.2: - resolution: {integrity: sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA==} + /@jest/test-result/29.4.3: + resolution: {integrity: sha512-Oi4u9NfBolMq9MASPwuWTlC5WvmNRwI4S8YrQg5R5Gi47DYlBe3sh7ILTqi/LGrK1XUE4XY9KZcQJTH1WJCLLA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 29.4.2 - '@jest/types': 29.4.2 + '@jest/console': 29.4.3 + '@jest/types': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 collect-v8-coverage: 1.0.1 dev: true - /@jest/test-sequencer/29.4.2: - resolution: {integrity: sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg==} + /@jest/test-sequencer/29.4.3: + resolution: {integrity: sha512-yi/t2nES4GB4G0mjLc0RInCq/cNr9dNwJxcGg8sslajua5Kb4kmozAc+qPLzplhBgfw1vLItbjyHzUN92UXicw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 29.4.2 + '@jest/test-result': 29.4.3 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 + jest-haste-map: 29.4.3 slash: 3.0.0 dev: true - /@jest/transform/29.4.2: - resolution: {integrity: sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ==} + /@jest/transform/29.4.3: + resolution: {integrity: sha512-8u0+fBGWolDshsFgPQJESkDa72da/EVwvL+II0trN2DR66wMwiQ9/CihaGfHdlLGFzbBZwMykFtxuwFdZqlKwg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.20.12 - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@jridgewell/trace-mapping': 0.3.17 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 - jest-regex-util: 29.4.2 - jest-util: 29.4.2 + jest-haste-map: 29.4.3 + jest-regex-util: 29.4.3 + jest-util: 29.4.3 micromatch: 4.0.5 pirates: 4.0.5 slash: 3.0.0 @@ -1693,19 +1698,19 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true - /@jest/types/29.4.2: - resolution: {integrity: sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==} + /@jest/types/29.4.3: + resolution: {integrity: sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.2 + '@jest/schemas': 29.4.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/yargs': 17.0.19 chalk: 4.1.2 dev: true @@ -1839,8 +1844,8 @@ packages: requiresBuild: true optional: true - /@nestjs/common/9.3.7_mnr6j2del53muneqly5h4y27ai: - resolution: {integrity: sha512-7hwY2lIkmB+K8wPSP9T8auzKPSlO15Gzujl3/ZxLX9SBt6B7N5Niv5E+5AzOcfL+h2X8JGysMG620hiGbUsT9A==} + /@nestjs/common/9.3.9_mnr6j2del53muneqly5h4y27ai: + resolution: {integrity: sha512-GshTD9Xz+wD2em6NyzU4NXw5IXMUmapgDgD+iuj6XL0258hvDwODmNk37mBBnZvTZlqER+krvIUKnS34etqF/A==} peerDependencies: cache-manager: <=5 class-transformer: '*' @@ -1862,8 +1867,8 @@ packages: uid: 2.0.1 dev: false - /@nestjs/core/9.3.7_ojttuksvundfk3amlyo674va74: - resolution: {integrity: sha512-FXGVivZiujZl1aJF6jdPpg1XnLKp7kDhVGhWhJtnpv2IW/cz/YQHD2uMz/o+GZ9TCZxsGlxg79jbcuJITG11iQ==} + /@nestjs/core/9.3.9_jrq2rdgfp2sx67wmylmrqliwxe: + resolution: {integrity: sha512-9g1A1G9eirLXEpH21rc6dKb08zHc2+adhCRz8NW39hbejcsxxD72FApJzt4QBQAKvu862ixt/tdpStnFT7lOSw==} requiresBuild: true peerDependencies: '@nestjs/common': ^9.0.0 @@ -1880,7 +1885,7 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 9.3.7_mnr6j2del53muneqly5h4y27ai + '@nestjs/common': 9.3.9_mnr6j2del53muneqly5h4y27ai '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -1893,8 +1898,8 @@ packages: - encoding dev: false - /@nestjs/testing/9.3.7_zmctrozi6xxh3beaccl6ebop6u: - resolution: {integrity: sha512-tNJuNYfDXMJzTgQXRwnQtLdd7PIIqrSsZ//TP/wnqPnbUpTkWMCSK5exWfPWvdyS8P3Fy2wy5hNj8Rvp9+KvYw==} + /@nestjs/testing/9.3.9_77foi4w27ghy47yutmnzv7krjy: + resolution: {integrity: sha512-+mPvSVvSC2SAkYgZZv1mOI2xsdGc1pmq7/sem7iin/JDoFtlvoGSK+pfZHD3IV3EpYtq1v/8/5gi+UFH9yZnDg==} peerDependencies: '@nestjs/common': ^9.0.0 '@nestjs/core': ^9.0.0 @@ -1906,8 +1911,8 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 9.3.7_mnr6j2del53muneqly5h4y27ai - '@nestjs/core': 9.3.7_ojttuksvundfk3amlyo674va74 + '@nestjs/common': 9.3.9_mnr6j2del53muneqly5h4y27ai + '@nestjs/core': 9.3.9_jrq2rdgfp2sx67wmylmrqliwxe tslib: 2.5.0 dev: false @@ -2061,7 +2066,7 @@ packages: - encoding dev: true - /@rollup/plugin-alias/4.0.3_rollup@3.14.0: + /@rollup/plugin-alias/4.0.3_rollup@3.17.2: resolution: {integrity: sha512-ZuDWE1q4PQDhvm/zc5Prun8sBpLJy41DMptYrS6MhAy9s9kL/doN1613BWfEchGVfKxzliJ3BjbOPizXX38DbQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2070,11 +2075,11 @@ packages: rollup: optional: true dependencies: - rollup: 3.14.0 + rollup: 3.17.2 slash: 4.0.0 dev: false - /@rollup/plugin-json/6.0.0_rollup@3.14.0: + /@rollup/plugin-json/6.0.0_rollup@3.17.2: resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2083,11 +2088,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2_rollup@3.14.0 - rollup: 3.14.0 + '@rollup/pluginutils': 5.0.2_rollup@3.17.2 + rollup: 3.17.2 dev: false - /@rollup/pluginutils/5.0.2_rollup@3.14.0: + /@rollup/pluginutils/5.0.2_rollup@3.17.2: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2099,7 +2104,7 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.14.0 + rollup: 3.17.2 dev: false /@sideway/address/4.1.4: @@ -2123,6 +2128,7 @@ packages: /@sindresorhus/is/4.6.0: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} + dev: true /@sindresorhus/is/5.3.0: resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==} @@ -2143,8 +2149,8 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@swc/cli/0.1.61_nh7rjdlljrxbrljvydz3oxxjt4: - resolution: {integrity: sha512-HeYMJ+8gKfJzM9xgcZqTpAHJYAJVGSljBSmWRUx2B6UiGraLsLjEcqxITwi6/t6Af+QboBMiQX5Wwll89oPK7g==} + /@swc/cli/0.1.62_r2avoeggowhtjk5lmq5oy455q4: + resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==} engines: {node: '>= 12.13'} hasBin: true peerDependencies: @@ -2155,7 +2161,7 @@ packages: optional: true dependencies: '@mole-inc/bin-wrapper': 8.0.1 - '@swc/core': 1.3.34 + '@swc/core': 1.3.35 chokidar: 3.5.3 commander: 7.2.0 fast-glob: 3.2.12 @@ -2164,8 +2170,8 @@ packages: source-map: 0.7.4 dev: true - /@swc/core-darwin-arm64/1.3.34: - resolution: {integrity: sha512-m7+kybVLO9uo/TiGBlf/ISmpmm27I/NrFEBGOVBF2xNOs5BY1XHHM6ddbPPckQa38C19nWeAzdJPwGzJw+qO3A==} + /@swc/core-darwin-arm64/1.3.35: + resolution: {integrity: sha512-zQUFkHx4gZpu0uo2IspvPnKsz8bsdXd5bC33xwjtoAI1cpLerDyqo4v2zIahEp+FdKZjyVsLHtfJiQiA1Qka3A==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -2173,8 +2179,8 @@ packages: dev: true optional: true - /@swc/core-darwin-x64/1.3.34: - resolution: {integrity: sha512-arH7LtcOhuC1wy88qgbCO/E5NnBThbxv9HhjScDfoUPRunyvT9whEvSK0eXCXxGvDAiAtXIrW3blIrteOsQaOQ==} + /@swc/core-darwin-x64/1.3.35: + resolution: {integrity: sha512-oOSkSGWtALovaw22lNevKD434OQTPf8X+dVPvPMrJXJpJ34dWDlFWpLntoc+arvKLNZ7LQmTuk8rR1hkrAY7cw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -2182,8 +2188,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm-gnueabihf/1.3.34: - resolution: {integrity: sha512-+pvjXsXTBzSxEL3U9869y3Am/3yo6kQfU6VGVAebgLv+pjM+mIHywbgo1Uxw+pgpTuD38BsrtYcaPNeBICN/wA==} + /@swc/core-linux-arm-gnueabihf/1.3.35: + resolution: {integrity: sha512-Yie8k00O6O8BCATS/xeKStquV4OYSskUGRDXBQVDw1FrE23PHaSeHCgg4q6iNZjJzXCOJbaTCKnYoIDn9DMf7A==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -2191,8 +2197,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-gnu/1.3.34: - resolution: {integrity: sha512-hOV1n1j+mDAgp19J+aeAnW4itMTWbaPbSbhEvLsNbVB00LoL6q6pUkWvCi+UbrejV6BIyyE9t7F5fU26SdKR8A==} + /@swc/core-linux-arm64-gnu/1.3.35: + resolution: {integrity: sha512-Zlv3WHa/4x2p51HSvjUWXHfSe1Gl2prqImUZJc8NZOlj75BFzVuR0auhQ+LbwvIQ3gaA1LODX9lyS9wXL3yjxA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -2200,8 +2206,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-musl/1.3.34: - resolution: {integrity: sha512-r2/Hegp1DRSzG+kg36Tujdn+WX+gO/2wQpVj/g6RPxIPdjy53OOf+UwvJ23Ecn5ZbyJcgKhhTN6H6/ZNHQPqjQ==} + /@swc/core-linux-arm64-musl/1.3.35: + resolution: {integrity: sha512-u6tCYsrSyZ8U+4jLMA/O82veBfLy2aUpn51WxQaeH7wqZGy9TGSJXoO8vWxARQ6b72vjsnKDJHP4MD8hFwcctg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -2209,8 +2215,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-gnu/1.3.34: - resolution: {integrity: sha512-jPxxAo7XlAT7bdIi49PtYN/K1TAxvpVS7otteJLhThOPPTVblIDg63U94ivp3mVQJb3WFH4KNYatEXypyvXppQ==} + /@swc/core-linux-x64-gnu/1.3.35: + resolution: {integrity: sha512-Dtxf2IbeH7XlNhP1Qt2/MvUPkpEbn7hhGfpSRs4ot8D3Vf5QEX4S/QtC1OsFWuciiYgHAT1Ybjt4xZic9DSkmA==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -2218,8 +2224,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-musl/1.3.34: - resolution: {integrity: sha512-eJaUuhvnNtcwpK9Mil4hZTSYZqG519gX5AQQ2VZOhrWBEBJi+jM0RXAvWdESsaXpS7W0CRtbmEXqeUff6UEgpQ==} + /@swc/core-linux-x64-musl/1.3.35: + resolution: {integrity: sha512-4XavNJ60GprjpTiESCu5daJUnmErixPAqDitJSMu4TV32LNIE8G00S9pDLXinDTW1rgcGtQdq1NLkNRmwwovtg==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -2227,8 +2233,8 @@ packages: dev: true optional: true - /@swc/core-win32-arm64-msvc/1.3.34: - resolution: {integrity: sha512-KFdeC5bXDcxIQ/1J5Pjj8BOblRFjh89TTJxujxAhKdoD1k0NV9BKEZACja2cTBz0hWD4cYlBX0cESVdL2rkm3w==} + /@swc/core-win32-arm64-msvc/1.3.35: + resolution: {integrity: sha512-dNGfKCUSX2M4qVyaS80Lyos0FkXyHRCvrdQ2Y4Hrg3FVokiuw3yY6fLohpUfQ5ws3n2A39dh7jGDeh34+l0sGA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -2236,8 +2242,8 @@ packages: dev: true optional: true - /@swc/core-win32-ia32-msvc/1.3.34: - resolution: {integrity: sha512-MgWkAQDiWIHfJL5b5aoogenGIt3qcqBSvwLnDQqSWEhkodZjHyCWpQFuaa7Y6ER3pKUIZ5kR8O9aAkDmF39awQ==} + /@swc/core-win32-ia32-msvc/1.3.35: + resolution: {integrity: sha512-ChuPSrDR+JBf7S7dEKPicnG8A3bM0uWPsW2vG+V2wH4iNfNxKVemESHosmYVeEZXqMpomNMvLyeHep1rjRsc0Q==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -2245,8 +2251,8 @@ packages: dev: true optional: true - /@swc/core-win32-x64-msvc/1.3.34: - resolution: {integrity: sha512-UhaikgVRYBZZdMI7Zo4/eUyYLnjGrC6QAn9aggt1+PiFIM9tXpX8aONUL3LoLkhQhd+6iWygfQ298RRxjKAKuw==} + /@swc/core-win32-x64-msvc/1.3.35: + resolution: {integrity: sha512-/RvphT4WfuGfIK84Ha0dovdPrKB1bW/mc+dtdmhv2E3EGkNc5FoueNwYmXWRimxnU7X0X7IkcRhyKB4G5DeAmg==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -2254,31 +2260,31 @@ packages: dev: true optional: true - /@swc/core/1.3.34: - resolution: {integrity: sha512-kaOCGRpciMEs2FpCUFaPJSNHgggFteOGZToM88uL5k/CEy0nU/6wzl8kUO5J+rI/8/8vN7qyhM1Ajhyj3WCSsw==} + /@swc/core/1.3.35: + resolution: {integrity: sha512-KmiBin0XSVzJhzX19zTiCqmLslZ40Cl7zqskJcTDeIrRhfgKdiAsxzYUanJgMJIRjYtl9Kcg1V/Ip2o2wL8v3w==} engines: {node: '>=10'} requiresBuild: true optionalDependencies: - '@swc/core-darwin-arm64': 1.3.34 - '@swc/core-darwin-x64': 1.3.34 - '@swc/core-linux-arm-gnueabihf': 1.3.34 - '@swc/core-linux-arm64-gnu': 1.3.34 - '@swc/core-linux-arm64-musl': 1.3.34 - '@swc/core-linux-x64-gnu': 1.3.34 - '@swc/core-linux-x64-musl': 1.3.34 - '@swc/core-win32-arm64-msvc': 1.3.34 - '@swc/core-win32-ia32-msvc': 1.3.34 - '@swc/core-win32-x64-msvc': 1.3.34 - dev: true - - /@swc/jest/0.2.24_@swc+core@1.3.34: + '@swc/core-darwin-arm64': 1.3.35 + '@swc/core-darwin-x64': 1.3.35 + '@swc/core-linux-arm-gnueabihf': 1.3.35 + '@swc/core-linux-arm64-gnu': 1.3.35 + '@swc/core-linux-arm64-musl': 1.3.35 + '@swc/core-linux-x64-gnu': 1.3.35 + '@swc/core-linux-x64-musl': 1.3.35 + '@swc/core-win32-arm64-msvc': 1.3.35 + '@swc/core-win32-ia32-msvc': 1.3.35 + '@swc/core-win32-x64-msvc': 1.3.35 + dev: true + + /@swc/jest/0.2.24_@swc+core@1.3.35: resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.34 + '@swc/core': 1.3.35 jsonc-parser: 3.2.0 dev: true @@ -2296,6 +2302,7 @@ packages: engines: {node: '>=10'} dependencies: defer-to-connect: 2.0.1 + dev: true /@szmarczak/http-timer/5.0.1: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} @@ -2438,7 +2445,7 @@ packages: /@types/accepts/1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/archiver/5.3.1: @@ -2484,7 +2491,7 @@ packages: resolution: {integrity: sha512-RkYW8K2H3J76HT6twmHYbzJ0GtLDDotpLP9ah9gtiA7zfF6peBH1l5fEiK0oeIZ3/642M7Jcb9sPmor8Vf4w6g==} deprecated: This is a stub types definition. bull provides its own type definitions, so you do not need this installed. dependencies: - bull: 4.10.3 + bull: 4.10.4 transitivePeerDependencies: - supports-color dev: true @@ -2494,8 +2501,9 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/responselike': 1.0.0 + dev: true /@types/cbor/6.0.0: resolution: {integrity: sha512-mGQ1lbYOwVti5Xlarn1bTeBZqgY0kstsdjnkoEovgohYKdBjGejHyNGXHdMBeqyQazIv32Jjp33+5pBEaSRy2w==} @@ -2536,34 +2544,34 @@ packages: /@types/fluent-ffmpeg/2.1.20: resolution: {integrity: sha512-B+OvhCdJ3LgEq2PhvWNOiB/EfwnXLElfMCgc4Z1K5zXgSfo9I6uGKwR/lqmNPFQuebNnes7re3gqkV77SyypLg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/glob-stream/6.1.1: resolution: {integrity: sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==} dependencies: '@types/glob': 8.0.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/glob/8.0.0: resolution: {integrity: sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/glob/8.0.1: resolution: {integrity: sha512-8bVUjXZvJacUFkJXHdyZ9iH1Eaj5V7I8c4NdH5sQJsdXkqT4CA5Dhb4yb4VE/3asyx4L9ayZr1NIhTsWHczmMw==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/graceful-fs/4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/gulp-rename/2.0.1: @@ -2587,7 +2595,7 @@ packages: /@types/ioredis/4.28.10: resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/istanbul-lib-coverage/2.0.4: @@ -2620,7 +2628,7 @@ packages: /@types/jsdom/21.1.0: resolution: {integrity: sha512-leWreJOdnuIxq9Y70tBVm/bvTuh31DSlF/r4l7Cfi4uhVQqLHD0Q4v301GMisEMwwbMgF7ZKxuZ+Jbd4NcdmRw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -2644,7 +2652,8 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 + dev: true /@types/long/4.0.2: resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} @@ -2665,7 +2674,7 @@ packages: /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 form-data: 3.0.1 dev: false @@ -2683,19 +2692,19 @@ packages: /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} - /@types/node/18.13.0: - resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==} + /@types/node/18.14.0: + resolution: {integrity: sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==} /@types/nodemailer/6.4.7: resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/oauth/0.9.1: resolution: {integrity: sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/offscreencanvas/2019.3.0: @@ -2709,7 +2718,7 @@ packages: /@types/pg/8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 pg-protocol: 1.5.0 pg-types: 2.2.0 dev: true @@ -2729,7 +2738,7 @@ packages: /@types/qrcode/1.5.0: resolution: {integrity: sha512-x5ilHXRxUPIMfjtM+1vf/GPTRWZ81nqscursm5gMznJeK9M0YnZ1c3bEvRLQ0zSSgedLx1J6MGL231ObQGGhaA==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/random-seed/0.3.3: @@ -2754,7 +2763,8 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 + dev: true /@types/sanitize-html/2.8.0: resolution: {integrity: sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==} @@ -2781,7 +2791,7 @@ packages: /@types/sharp/0.31.1: resolution: {integrity: sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/sinonjs__fake-timers/8.1.1: @@ -2796,12 +2806,6 @@ packages: resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==} dev: true - /@types/speakeasy/2.0.7: - resolution: {integrity: sha512-JEcOhN2SQCoX86ZfiZEe8px84sVJtivBXMZfOVyARTYEj0hrwwbj1nF0FwEL3nJSoEV6uTbcdLllMKBgAYHWCQ==} - dependencies: - '@types/node': 18.13.0 - dev: true - /@types/stack-utils/2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true @@ -2829,7 +2833,7 @@ packages: /@types/undertaker/1.2.8: resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/undertaker-registry': 1.0.1 async-done: 1.3.2 dev: true @@ -2837,7 +2841,7 @@ packages: /@types/unzipper/0.10.5: resolution: {integrity: sha512-NrLJb29AdnBARpg9S/4ktfPEisbJ0AvaaAr3j7Q1tg8AgcEUsq2HqbNzvgLRoWyRtjzeLEv7vuL39u1mrNIyNA==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/uuid/9.0.0: @@ -2847,14 +2851,14 @@ packages: /@types/vary/1.1.0: resolution: {integrity: sha512-LQWqrIa0dvEOOH37lGksMEXbypRLUFqu6Gx0pmX7zIUisD2I/qaVgEX/vJ/PSVSW0Hk6yz1BNkFpqg6dZm3Wug==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/vinyl-fs/2.4.12: resolution: {integrity: sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==} dependencies: '@types/glob-stream': 6.1.1 - '@types/node': 18.13.0 + '@types/node': 18.14.0 '@types/vinyl': 2.0.7 dev: true @@ -2862,12 +2866,12 @@ packages: resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==} dependencies: '@types/expect': 1.20.4 - '@types/node': 18.13.0 + '@types/node': 18.14.0 /@types/web-push/3.3.2: resolution: {integrity: sha512-JxWGVL/m7mWTIg4mRYO+A6s0jPmBkr4iJr39DqJpRJAc+jrPiEe1/asmkwerzRon8ZZDxaZJpsxpv0Z18Wo9gw==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/webgl-ext/0.0.30: @@ -2877,13 +2881,13 @@ packages: /@types/websocket/1.0.5: resolution: {integrity: sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true /@types/yargs-parser/21.0.0: @@ -2906,12 +2910,12 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 dev: true optional: true - /@typescript-eslint/eslint-plugin/5.51.0_b635kmla6dsb4frxfihkw4m47e: - resolution: {integrity: sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==} + /@typescript-eslint/eslint-plugin/5.52.0_6cfvjsbua5ptj65675bqcn6oza: + resolution: {integrity: sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -2921,12 +2925,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 - '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/type-utils': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 - '@typescript-eslint/utils': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm + '@typescript-eslint/scope-manager': 5.52.0 + '@typescript-eslint/type-utils': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm + '@typescript-eslint/utils': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -2938,8 +2942,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.51.0_4vsywjlpuriuw3tl5oq6zy5a64: - resolution: {integrity: sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==} + /@typescript-eslint/parser/5.52.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2948,26 +2952,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 + '@typescript-eslint/scope-manager': 5.52.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/typescript-estree': 5.52.0_typescript@4.9.5 debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager/5.51.0: - resolution: {integrity: sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==} + /@typescript-eslint/scope-manager/5.52.0: + resolution: {integrity: sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/visitor-keys': 5.51.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/visitor-keys': 5.52.0 dev: true - /@typescript-eslint/type-utils/5.51.0_4vsywjlpuriuw3tl5oq6zy5a64: - resolution: {integrity: sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==} + /@typescript-eslint/type-utils/5.52.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -2976,23 +2980,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 - '@typescript-eslint/utils': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/typescript-estree': 5.52.0_typescript@4.9.5 + '@typescript-eslint/utils': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 tsutils: 3.21.0_typescript@4.9.5 typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.51.0: - resolution: {integrity: sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==} + /@typescript-eslint/types/5.52.0: + resolution: {integrity: sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.51.0_typescript@4.9.5: - resolution: {integrity: sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==} + /@typescript-eslint/typescript-estree/5.52.0_typescript@4.9.5: + resolution: {integrity: sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -3000,8 +3004,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/visitor-keys': 5.51.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/visitor-keys': 5.52.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -3012,82 +3016,82 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.51.0_4vsywjlpuriuw3tl5oq6zy5a64: - resolution: {integrity: sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==} + /@typescript-eslint/utils/5.52.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.51.0 - '@typescript-eslint/types': 5.51.0 - '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 - eslint: 8.33.0 + '@typescript-eslint/scope-manager': 5.52.0 + '@typescript-eslint/types': 5.52.0 + '@typescript-eslint/typescript-estree': 5.52.0_typescript@4.9.5 + eslint: 8.34.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint-utils: 3.0.0_eslint@8.34.0 semver: 7.3.8 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys/5.51.0: - resolution: {integrity: sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==} + /@typescript-eslint/visitor-keys/5.52.0: + resolution: {integrity: sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.51.0 + '@typescript-eslint/types': 5.52.0 eslint-visitor-keys: 3.3.0 dev: true - /@vitejs/plugin-vue/4.0.0_vite@4.1.1+vue@3.2.47: + /@vitejs/plugin-vue/4.0.0_vite@4.1.2+vue@3.2.47: resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.1.1_gyrp4zacqcjjrmgvdzgac5epyy + vite: 4.1.2_hlkwzk2izwsolfmdrejei4vrty vue: 3.2.47 dev: false - /@volar/language-core/1.0.24: - resolution: {integrity: sha512-vTN+alJiWwK0Pax6POqrmevbtFW2dXhjwWiW/MW4f48eDYPLdyURWcr8TixO7EN/nHsUBj2udT7igFKPtjyAKg==} + /@volar/language-core/1.2.0-alpha.16: + resolution: {integrity: sha512-aIktWe9Zg0M+u/RIXHCuL+IoLRHTrpsbTib8olrg4etlurHDXahoVhoEnH9wmlliray0iigIo2z5vwueYInp3g==} dependencies: - '@volar/source-map': 1.0.24 - muggle-string: 0.1.0 + '@volar/source-map': 1.2.0-alpha.16 dev: true - /@volar/source-map/1.0.24: - resolution: {integrity: sha512-Qsv/tkplx18pgBr8lKAbM1vcDqgkGKQzbChg6NW+v0CZc3G7FLmK+WrqEPzKlN7Cwdc6XVL559Nod8WKAfKr4A==} + /@volar/source-map/1.2.0-alpha.16: + resolution: {integrity: sha512-/AK3VqnFqONd221COI2ZnEvfgBulfoQkjA/ZjPOXpsOkWri99TLcfZY/NTQRytp7Hx6EP/1p1DDeyGuMCUYjgA==} dependencies: - muggle-string: 0.1.0 + muggle-string: 0.2.2 dev: true - /@volar/typescript/1.0.24: - resolution: {integrity: sha512-f8hCSk+PfKR1/RQHxZ79V1NpDImHoivqoizK+mstphm25tn/YJ/JnKNjZHB+o21fuW0yKlI26NV3jkVb2Cc/7A==} + /@volar/typescript/1.2.0-alpha.16: + resolution: {integrity: sha512-ltlTLHIkLxgmTVBZmOnhmnlNzEj2lpvlBmmaV2GWYTrBUMt0z1OgeCq0Utlj9HjjrGPhwWxZNkv86ZABgrMA3Q==} dependencies: - '@volar/language-core': 1.0.24 + '@volar/language-core': 1.2.0-alpha.16 dev: true - /@volar/vue-language-core/1.0.24: - resolution: {integrity: sha512-2NTJzSgrwKu6uYwPqLiTMuAzi7fAY3yFy5PJ255bGJc82If0Xr+cW8pC80vpjG0D/aVLmlwAdO4+Ya2BI8GdDg==} + /@volar/vue-language-core/1.1.4: + resolution: {integrity: sha512-2C2CwHvaT5AzNzDbYZQ85lNr4jACZARoZMZBLuU5+JyIwhWeAfxvyAeoE3VbgfgycN5t6X4uBqx/Wzh1QLgD8Q==} dependencies: - '@volar/language-core': 1.0.24 - '@volar/source-map': 1.0.24 + '@volar/language-core': 1.2.0-alpha.16 + '@volar/source-map': 1.2.0-alpha.16 '@vue/compiler-dom': 3.2.47 '@vue/compiler-sfc': 3.2.47 '@vue/reactivity': 3.2.47 '@vue/shared': 3.2.47 - minimatch: 5.1.2 + minimatch: 6.2.0 + muggle-string: 0.2.2 vue-template-compiler: 2.7.14 dev: true - /@volar/vue-typescript/1.0.24: - resolution: {integrity: sha512-9a25oHDvGaNC0okRS47uqJI6FxY4hUQZUsxeOUFHcqVxZEv8s17LPuP/pMMXyz7jPygrZubB/qXqHY5jEu/akA==} + /@volar/vue-typescript/1.1.4: + resolution: {integrity: sha512-x5i5TUUXb1PM0rM80Y8XUeMBUcoS3/TjR3WTxvvEUIol9uEOPp6uxxQQ67uSv7ocN6vB0LugJqS6FA7Z93oL0Q==} dependencies: - '@volar/typescript': 1.0.24 - '@volar/vue-language-core': 1.0.24 + '@volar/typescript': 1.2.0-alpha.16 + '@volar/vue-language-core': 1.1.4 dev: true /@vue/compiler-core/3.2.47: @@ -3691,8 +3695,8 @@ packages: - supports-color dev: false - /aws-sdk/2.1295.0: - resolution: {integrity: sha512-HVYoFCyfiL8gzL/c0lSRTg8tWBLfqAEDfwzGe338ww/LahpmC6C07S71SBBIvtGq3dpd7IwEobAbubZDijrA0Q==} + /aws-sdk/2.1318.0: + resolution: {integrity: sha512-xRCKqx4XWXUIpjDCVHmdOSINEVCIC5+yhmgUGR9A6VfxfPs59HbxKyd5LB+CmXhVbwVUM4SRWG5O+agQj+w7Eg==} engines: {node: '>= 10.0.0'} dependencies: buffer: 4.9.2 @@ -3730,17 +3734,17 @@ packages: - debug dev: true - /babel-jest/29.4.2_@babel+core@7.20.12: - resolution: {integrity: sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ==} + /babel-jest/29.4.3_@babel+core@7.20.12: + resolution: {integrity: sha512-o45Wyn32svZE+LnMVWv/Z4x0SwtLbh4FyGcYtR20kIWd+rdrDZ9Fzq8Ml3MYLD+mZvEdzCjZsCnYZ2jpJyQ+Nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: '@babel/core': 7.20.12 - '@jest/transform': 29.4.2 + '@jest/transform': 29.4.3 '@types/babel__core': 7.1.20 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.4.2_@babel+core@7.20.12 + babel-preset-jest: 29.4.3_@babel+core@7.20.12 chalk: 4.1.2 graceful-fs: 4.2.10 slash: 3.0.0 @@ -3761,8 +3765,8 @@ packages: - supports-color dev: true - /babel-plugin-jest-hoist/29.4.2: - resolution: {integrity: sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ==} + /babel-plugin-jest-hoist/29.4.3: + resolution: {integrity: sha512-mB6q2q3oahKphy5V7CpnNqZOCkxxZ9aokf1eh82Dy3jQmg4xvM1tGrh5y6BQUJh4a3Pj9+eLfwvAZ7VNKg7H8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.20.7 @@ -3791,14 +3795,14 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.20.12 dev: true - /babel-preset-jest/29.4.2_@babel+core@7.20.12: - resolution: {integrity: sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ==} + /babel-preset-jest/29.4.3_@babel+core@7.20.12: + resolution: {integrity: sha512-gWx6COtSuma6n9bw+8/F+2PCXrIgxV/D1TJFnp6OyBK2cxPWg0K9p/sriNYeifKjpUkMViWQ09DSWtzJQRETsw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.20.12 - babel-plugin-jest-hoist: 29.4.2 + babel-plugin-jest-hoist: 29.4.3 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 dev: true @@ -3844,10 +3848,6 @@ packages: pascalcase: 0.1.1 dev: false - /base32.js/0.0.1: - resolution: {integrity: sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==} - dev: false - /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3926,8 +3926,8 @@ packages: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} dev: true - /blurhash/2.0.4: - resolution: {integrity: sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==} + /blurhash/2.0.5: + resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} dev: false /bn.js/4.12.0: @@ -4067,8 +4067,8 @@ packages: node-gyp-build: 4.6.0 dev: false - /bull/4.10.3: - resolution: {integrity: sha512-pp403srpkn9tYi7Z3Mu0sozehZ7rEEFGNJnN+nLxQwml6MySzefC9bPeCYedZoCkXdZ6VbIB8uNkMZg+hN/dAg==} + /bull/4.10.4: + resolution: {integrity: sha512-o9m/7HjS/Or3vqRd59evBlWCXd9Lp+ALppKseoSKHaykK46SmRjAilX98PgmOz1yeVaurt8D5UtvEt4bUjM3eA==} engines: {node: '>=12'} dependencies: cron-parser: 4.7.1 @@ -4130,17 +4130,10 @@ packages: unset-value: 1.0.0 dev: false - /cache-content-type/1.0.1: - resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} - engines: {node: '>= 6.0.0'} - dependencies: - mime-types: 2.1.35 - ylru: 1.3.2 - dev: false - /cacheable-lookup/5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} + dev: true /cacheable-lookup/6.1.0: resolution: {integrity: sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==} @@ -4176,6 +4169,7 @@ packages: lowercase-keys: 2.0.0 normalize-url: 6.1.0 responselike: 2.0.1 + dev: true /cachedir/2.3.0: resolution: {integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==} @@ -4296,45 +4290,45 @@ packages: is-regex: 1.1.4 dev: false - /chart.js/4.2.0: - resolution: {integrity: sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==} + /chart.js/4.2.1: + resolution: {integrity: sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==} engines: {pnpm: ^7.0.0} dependencies: '@kurkle/color': 0.3.2 dev: false - /chartjs-adapter-date-fns/3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla: + /chartjs-adapter-date-fns/3.0.0_rid3rx6orfiwps7kg2r43n6mvu: resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} peerDependencies: chart.js: '>=2.8.0' date-fns: '>=2.0.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 date-fns: 2.29.3 dev: false - /chartjs-chart-matrix/2.0.1_chart.js@4.2.0: + /chartjs-chart-matrix/2.0.1_chart.js@4.2.1: resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==} peerDependencies: chart.js: '>=3.0.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 dev: false - /chartjs-plugin-gradient/0.6.1_chart.js@4.2.0: + /chartjs-plugin-gradient/0.6.1_chart.js@4.2.1: resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==} peerDependencies: chart.js: '>=2.6.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 dev: false - /chartjs-plugin-zoom/2.0.0_chart.js@4.2.0: + /chartjs-plugin-zoom/2.0.0_chart.js@4.2.1: resolution: {integrity: sha512-bqpi7DGy9a5hX7ThKl/xQaLzXvneSwhS0w/lNimZ8AJaoRVMKz5JfUoqwciJYV5ixKXJbgyvwC9HcJnyVsYmjg==} peerDependencies: chart.js: '>=3.2.0' dependencies: - chart.js: 4.2.0 + chart.js: 4.2.1 hammerjs: 2.0.8 dev: false @@ -4343,26 +4337,28 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /cheerio/0.22.0: - resolution: {integrity: sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==} - engines: {node: '>= 0.6'} + /cheerio-select/2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} dependencies: - css-select: 1.2.0 - dom-serializer: 0.1.1 - entities: 1.1.2 - htmlparser2: 3.10.1 - lodash.assignin: 4.2.0 - lodash.bind: 4.2.1 - lodash.defaults: 4.2.0 - lodash.filter: 4.6.0 - lodash.flatten: 4.4.0 - lodash.foreach: 4.5.0 - lodash.map: 4.6.0 - lodash.merge: 4.6.2 - lodash.pick: 4.4.0 - lodash.reduce: 4.6.0 - lodash.reject: 4.6.0 - lodash.some: 4.6.0 + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + dev: false + + /cheerio/1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.0.1 + htmlparser2: 8.0.1 + parse5: 7.1.2 + parse5-htmlparser2-tree-adapter: 7.0.0 dev: false /chokidar/3.5.3: @@ -4496,6 +4492,7 @@ packages: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 + dev: true /clone-stats/1.0.0: resolution: {integrity: sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==} @@ -4531,6 +4528,7 @@ packages: /co/4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true /coa/1.0.4: resolution: {integrity: sha512-KAGck/eNAmCL0dcT3BiuYwLbExK6lduR8DxM3C1TyDzaXhZHyZ8ooX5I5+na2e3dPFuibfxrGdorr0/Lr7RYCQ==} @@ -4719,11 +4717,6 @@ packages: dependencies: safe-buffer: 5.2.1 - /content-type/1.0.4: - resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} - engines: {node: '>= 0.6'} - dev: false - /convert-source-map/1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -4736,14 +4729,6 @@ packages: engines: {node: '>= 0.6'} dev: false - /cookies/0.8.0: - resolution: {integrity: sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==} - engines: {node: '>= 0.8'} - dependencies: - depd: 2.0.0 - keygrip: 1.1.0 - dev: false - /copy-descriptor/0.1.1: resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} @@ -4823,17 +4808,23 @@ packages: resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} dev: false - /css-select/1.2.0: - resolution: {integrity: sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==} + /css-select/5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} dependencies: boolbase: 1.0.0 - css-what: 2.1.3 - domutils: 1.5.1 - nth-check: 1.0.2 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.0.1 + nth-check: 2.1.1 dev: false - /css-what/2.1.3: - resolution: {integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==} + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + + /css.escape/1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} dev: false /cssesc/3.0.0: @@ -4921,8 +4912,8 @@ packages: uniq: 1.0.1 dev: false - /cypress/12.5.1: - resolution: {integrity: sha512-ZmCmJ3lsyeOpBfh410m5+AO2CO1AxAzFBt7k6/uVbNcrNZje1vdiwYTpj2ksPKg9mjr9lR6V8tmlDNMvr4H/YQ==} + /cypress/12.6.0: + resolution: {integrity: sha512-WdHSVaS1lumSd5XpVTslZd8ui9GIGphrzvXq9+3DtVhqjRZC5M70gu5SW/Y/SLPq3D1wiXGZoHC6HJ7ESVE2lw==} engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} hasBin: true requiresBuild: true @@ -5047,18 +5038,6 @@ packages: supports-color: 8.1.1 dev: true - /debug/4.3.3: - resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: false - /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -5121,10 +5100,6 @@ packages: - debug dev: false - /deep-equal/1.0.1: - resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} - dev: false - /deep-extend/0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -5213,11 +5188,6 @@ packages: engines: {node: '>= 0.8'} dev: false - /destroy/1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: false - /detect-file/1.0.0: resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} engines: {node: '>=0.10.0'} @@ -5233,8 +5203,8 @@ packages: engines: {node: '>=8'} dev: true - /diff-sequences/29.4.2: - resolution: {integrity: sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw==} + /diff-sequences/29.4.3: + resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true @@ -5270,13 +5240,6 @@ packages: resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==} dev: false - /dom-serializer/0.1.1: - resolution: {integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==} - dependencies: - domelementtype: 1.3.1 - entities: 1.1.2 - dev: false - /dom-serializer/2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: @@ -5284,10 +5247,6 @@ packages: domhandler: 5.0.3 entities: 4.4.0 - /domelementtype/1.3.1: - resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} - dev: false - /domelementtype/2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -5298,32 +5257,12 @@ packages: webidl-conversions: 7.0.0 dev: false - /domhandler/2.4.2: - resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} - dependencies: - domelementtype: 1.3.1 - dev: false - /domhandler/5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} dependencies: domelementtype: 2.3.0 - /domutils/1.5.1: - resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==} - dependencies: - dom-serializer: 0.1.1 - domelementtype: 1.3.1 - dev: false - - /domutils/1.7.0: - resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} - dependencies: - dom-serializer: 0.1.1 - domelementtype: 1.3.1 - dev: false - /domutils/3.0.1: resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} dependencies: @@ -5374,10 +5313,6 @@ packages: safe-buffer: 5.2.1 dev: false - /ee-first/1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: false - /ejs/3.1.8: resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} engines: {node: '>=0.10.0'} @@ -5401,11 +5336,6 @@ packages: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} dev: false - /encodeurl/1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - dev: false - /encoding/0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} requiresBuild: true @@ -5426,10 +5356,6 @@ packages: ansi-colors: 4.1.3 dev: true - /entities/1.1.2: - resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} - dev: false - /entities/2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} dev: false @@ -5834,7 +5760,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_fwto6vsnn2m6f5yglaeo6vhd5y: + /eslint-module-utils/2.7.4_npjqex3ey3rgd34fjcuucz7la4: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -5855,15 +5781,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 3.2.7 - eslint: 8.33.0 + eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import/2.27.5_yzj2n2b43wonjwaifya6xmk2zy: + /eslint-plugin-import/2.27.5_mcvs2y73sfmcxqzpjj5lr7a2m4: resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -5873,15 +5799,15 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.51.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.52.0_7kw3g6rralp5ps6mg3uyzz6azm array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.33.0 + eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_fwto6vsnn2m6f5yglaeo6vhd5y + eslint-module-utils: 2.7.4_npjqex3ey3rgd34fjcuucz7la4 has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -5896,19 +5822,19 @@ packages: - supports-color dev: true - /eslint-plugin-vue/9.9.0_eslint@8.33.0: + /eslint-plugin-vue/9.9.0_eslint@8.34.0: resolution: {integrity: sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.33.0 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint: 8.34.0 + eslint-utils: 3.0.0_eslint@8.34.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.11 semver: 7.3.8 - vue-eslint-parser: 9.1.0_eslint@8.33.0 + vue-eslint-parser: 9.1.0_eslint@8.34.0 xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -5930,13 +5856,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.33.0: + /eslint-utils/3.0.0_eslint@8.34.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.33.0 + eslint: 8.34.0 eslint-visitor-keys: 2.1.0 dev: true @@ -5950,8 +5876,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.33.0: - resolution: {integrity: sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==} + /eslint/8.34.0: + resolution: {integrity: sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: @@ -5966,7 +5892,7 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint-utils: 3.0.0_eslint@8.34.0 eslint-visitor-keys: 3.3.0 espree: 9.4.1 esquery: 1.4.0 @@ -6194,15 +6120,15 @@ packages: jest-util: 29.4.2 dev: true - /expect/29.4.2: - resolution: {integrity: sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ==} + /expect/29.4.3: + resolution: {integrity: sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/expect-utils': 29.4.2 - jest-get-type: 29.4.2 - jest-matcher-utils: 29.4.2 - jest-message-util: 29.4.2 - jest-util: 29.4.2 + '@jest/expect-utils': 29.4.3 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.4.3 + jest-message-util: 29.4.3 + jest-util: 29.4.3 dev: true /ext-list/2.2.2: @@ -6360,8 +6286,8 @@ packages: resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==} dev: false - /fastify/4.12.0: - resolution: {integrity: sha512-Hh2GCsOCqnOuewWSvqXlpq5V/9VA+/JkVoooQWUhrU6gryO9+/UGOoF/dprGcKSDxkM/9TkMXSffYp8eA/YhYQ==} + /fastify/4.13.0: + resolution: {integrity: sha512-p9ibdFWH3pZ7KPgmfHPKGUy2W4EWU2TEpwlcu58w4CwGyU3ARFfh2kwq6zpZ5W2ZGVbufi4tZbqHIHAlX/9Z/A==} dependencies: '@fastify/ajv-compiler': 3.5.0 '@fastify/error': 3.2.0 @@ -6436,8 +6362,8 @@ packages: token-types: 5.0.1 dev: true - /file-type/18.2.0: - resolution: {integrity: sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==} + /file-type/18.2.1: + resolution: {integrity: sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==} engines: {node: '>=14.16'} dependencies: readable-web-to-node-stream: 3.0.2 @@ -6670,11 +6596,6 @@ packages: map-cache: 0.2.2 dev: false - /fresh/0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - dev: false - /from/0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} dev: true @@ -6851,6 +6772,7 @@ packages: engines: {node: '>=8'} dependencies: pump: 3.0.0 + dev: true /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -6952,17 +6874,6 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /glob/8.0.3: - resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==} - engines: {node: '>=12'} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.2 - once: 1.4.0 - dev: false - /glob/8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -7057,6 +6968,7 @@ packages: lowercase-keys: 2.0.0 p-cancelable: 2.1.1 responselike: 2.0.1 + dev: true /got/12.5.3: resolution: {integrity: sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w==} @@ -7174,6 +7086,19 @@ packages: engines: {node: '>=0.8.0'} dev: false + /happy-dom/8.7.0: + resolution: {integrity: sha512-F/mH5l8aQwlfeByB0nU6Lg7a0FBax/nPCYNYg8tn/abdKCmiIJH+gU/5MVysf5XoM6KjJsvkbIaXAmS/8HxXLA==} + dependencies: + css.escape: 1.5.1 + he: 1.2.0 + node-fetch: 2.6.7 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + transitivePeerDependencies: + - encoding + dev: false + /har-schema/2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -7276,7 +7201,6 @@ packages: /he/1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - dev: true /hexoid/1.0.0: resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} @@ -7322,17 +7246,6 @@ packages: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true - /htmlparser2/3.10.1: - resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} - dependencies: - domelementtype: 1.3.1 - domhandler: 2.4.2 - domutils: 1.7.0 - entities: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.0 - dev: false - /htmlparser2/8.0.1: resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} dependencies: @@ -7341,28 +7254,9 @@ packages: domutils: 3.0.1 entities: 4.4.0 - /http-assert/1.5.0: - resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} - engines: {node: '>= 0.8'} - dependencies: - deep-equal: 1.0.1 - http-errors: 1.8.1 - dev: false - /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} - /http-errors/1.8.1: - resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} - engines: {node: '>= 0.6'} - dependencies: - depd: 1.1.2 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 1.5.0 - toidentifier: 1.0.1 - dev: false - /http-errors/2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -7409,6 +7303,7 @@ packages: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + dev: true /http2-wrapper/2.2.0: resolution: {integrity: sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==} @@ -8128,43 +8023,43 @@ packages: minimatch: 3.1.2 dev: false - /jest-changed-files/29.4.2: - resolution: {integrity: sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw==} + /jest-changed-files/29.4.3: + resolution: {integrity: sha512-Vn5cLuWuwmi2GNNbokPOEcvrXGSGrqVnPEZV7rC6P7ck07Dyw9RFnvWglnupSh+hGys0ajGtw/bc2ZgweljQoQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: execa: 5.1.1 p-limit: 3.1.0 dev: true - /jest-circus/29.4.2: - resolution: {integrity: sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg==} + /jest-circus/29.4.3: + resolution: {integrity: sha512-Vw/bVvcexmdJ7MLmgdT3ZjkJ3LKu8IlpefYokxiqoZy6OCQ2VAm6Vk3t/qHiAGUXbdbJKJWnc8gH3ypTbB/OBw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/expect': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/environment': 29.4.3 + '@jest/expect': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 is-generator-fn: 2.1.0 - jest-each: 29.4.2 - jest-matcher-utils: 29.4.2 - jest-message-util: 29.4.2 - jest-runtime: 29.4.2 - jest-snapshot: 29.4.2 - jest-util: 29.4.2 + jest-each: 29.4.3 + jest-matcher-utils: 29.4.3 + jest-message-util: 29.4.3 + jest-runtime: 29.4.3 + jest-snapshot: 29.4.3 + jest-util: 29.4.3 p-limit: 3.1.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 stack-utils: 2.0.6 transitivePeerDependencies: - supports-color dev: true - /jest-cli/29.4.2_@types+node@18.13.0: - resolution: {integrity: sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q==} + /jest-cli/29.4.3_@types+node@18.14.0: + resolution: {integrity: sha512-PiiAPuFNfWWolCE6t3ZrDXQc6OsAuM3/tVW0u27UWc1KE+n/HSn5dSE6B2juqN7WP+PP0jAcnKtGmI4u8GMYCg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -8173,16 +8068,16 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/types': 29.4.2 + '@jest/core': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/types': 29.4.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.10 import-local: 3.1.0 - jest-config: 29.4.2_@types+node@18.13.0 - jest-util: 29.4.2 - jest-validate: 29.4.2 + jest-config: 29.4.3_@types+node@18.14.0 + jest-util: 29.4.3 + jest-validate: 29.4.3 prompts: 2.4.2 yargs: 17.6.2 transitivePeerDependencies: @@ -8191,8 +8086,8 @@ packages: - ts-node dev: true - /jest-config/29.4.2_@types+node@18.13.0: - resolution: {integrity: sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA==} + /jest-config/29.4.3_@types+node@18.14.0: + resolution: {integrity: sha512-eCIpqhGnIjdUCXGtLhz4gdDoxKSWXKjzNcc5r+0S1GKOp2fwOipx5mRcwa9GB/ArsxJ1jlj2lmlD9bZAsBxaWQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' @@ -8204,26 +8099,26 @@ packages: optional: true dependencies: '@babel/core': 7.20.12 - '@jest/test-sequencer': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - babel-jest: 29.4.2_@babel+core@7.20.12 + '@jest/test-sequencer': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + babel-jest: 29.4.3_@babel+core@7.20.12 chalk: 4.1.2 ci-info: 3.7.1 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.10 - jest-circus: 29.4.2 - jest-environment-node: 29.4.2 - jest-get-type: 29.4.2 - jest-regex-util: 29.4.2 - jest-resolve: 29.4.2 - jest-runner: 29.4.2 - jest-util: 29.4.2 - jest-validate: 29.4.2 + jest-circus: 29.4.3 + jest-environment-node: 29.4.3 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.4.3 + jest-runner: 29.4.3 + jest-util: 29.4.3 + jest-validate: 29.4.3 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -8235,39 +8130,49 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - diff-sequences: 29.4.2 - jest-get-type: 29.4.2 - pretty-format: 29.4.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 dev: true - /jest-docblock/29.4.2: - resolution: {integrity: sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag==} + /jest-diff/29.4.3: + resolution: {integrity: sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 + dev: true + + /jest-docblock/29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: detect-newline: 3.1.0 dev: true - /jest-each/29.4.2: - resolution: {integrity: sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA==} + /jest-each/29.4.3: + resolution: {integrity: sha512-1ElHNAnKcbJb/b+L+7j0/w7bDvljw4gTv1wL9fYOczeJrbTbkMGQ5iQPFJ3eFQH19VPTx1IyfePdqSpePKss7Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 chalk: 4.1.2 - jest-get-type: 29.4.2 - jest-util: 29.4.2 - pretty-format: 29.4.2 + jest-get-type: 29.4.3 + jest-util: 29.4.3 + pretty-format: 29.4.3 dev: true - /jest-environment-node/29.4.2: - resolution: {integrity: sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w==} + /jest-environment-node/29.4.3: + resolution: {integrity: sha512-gAiEnSKF104fsGDXNkwk49jD/0N0Bqu2K9+aMQXA6avzsA9H3Fiv1PW2D+gzbOSR705bWd2wJZRFEFpV0tXISg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/fake-timers': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - jest-mock: 29.4.2 - jest-util: 29.4.2 + '@jest/environment': 29.4.3 + '@jest/fake-timers': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + jest-mock: 29.4.3 + jest-util: 29.4.3 dev: true /jest-get-type/29.2.0: @@ -8280,31 +8185,36 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-haste-map/29.4.2: - resolution: {integrity: sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw==} + /jest-get-type/29.4.3: + resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map/29.4.3: + resolution: {integrity: sha512-eZIgAS8tvm5IZMtKlR8Y+feEOMfo2pSQkmNbufdbMzMSn9nitgGxF1waM/+LbryO3OkMcKS98SUb+j/cQxp/vQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@types/graceful-fs': 4.1.6 - '@types/node': 18.13.0 + '@types/node': 18.14.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.10 - jest-regex-util: 29.4.2 - jest-util: 29.4.2 - jest-worker: 29.4.2 + jest-regex-util: 29.4.3 + jest-util: 29.4.3 + jest-worker: 29.4.3 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 dev: true - /jest-leak-detector/29.4.2: - resolution: {integrity: sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA==} + /jest-leak-detector/29.4.3: + resolution: {integrity: sha512-9yw4VC1v2NspMMeV3daQ1yXPNxMgCzwq9BocCwYrRgXe4uaEJPAN0ZK37nFBhcy3cUwEVstFecFLaTHpF7NiGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.4.2 - pretty-format: 29.4.2 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 dev: true /jest-matcher-utils/29.3.1: @@ -8314,17 +8224,17 @@ packages: chalk: 4.1.2 jest-diff: 29.4.2 jest-get-type: 29.4.2 - pretty-format: 29.4.2 + pretty-format: 29.4.3 dev: true - /jest-matcher-utils/29.4.2: - resolution: {integrity: sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg==} + /jest-matcher-utils/29.4.3: + resolution: {integrity: sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 - jest-diff: 29.4.2 - jest-get-type: 29.4.2 - pretty-format: 29.4.2 + jest-diff: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.4.3 dev: true /jest-message-util/29.3.1: @@ -8332,41 +8242,41 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.18.6 - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.10 micromatch: 4.0.5 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 stack-utils: 2.0.6 dev: true - /jest-message-util/29.4.2: - resolution: {integrity: sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g==} + /jest-message-util/29.4.3: + resolution: {integrity: sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.18.6 - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.10 micromatch: 4.0.5 - pretty-format: 29.4.2 + pretty-format: 29.4.3 slash: 3.0.0 stack-utils: 2.0.6 dev: true - /jest-mock/29.4.2: - resolution: {integrity: sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g==} + /jest-mock/29.4.3: + resolution: {integrity: sha512-LjFgMg+xed9BdkPMyIJh+r3KeHt1klXPJYBULXVVAkbTaaKjPX1o1uVCAZADMEp/kOxGTwy/Ot8XbvgItOrHEg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 - '@types/node': 18.13.0 - jest-util: 29.4.2 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + jest-util: 29.4.3 dev: true - /jest-pnp-resolver/1.2.3_jest-resolve@29.4.2: + /jest-pnp-resolver/1.2.3_jest-resolve@29.4.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} peerDependencies: @@ -8375,101 +8285,100 @@ packages: jest-resolve: optional: true dependencies: - jest-resolve: 29.4.2 + jest-resolve: 29.4.3 dev: true - /jest-regex-util/29.4.2: - resolution: {integrity: sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig==} + /jest-regex-util/29.4.3: + resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-resolve-dependencies/29.4.2: - resolution: {integrity: sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg==} + /jest-resolve-dependencies/29.4.3: + resolution: {integrity: sha512-uvKMZAQ3nmXLH7O8WAOhS5l0iWyT3WmnJBdmIHiV5tBbdaDZ1wqtNX04FONGoaFvSOSHBJxnwAVnSn1WHdGVaw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-regex-util: 29.4.2 - jest-snapshot: 29.4.2 + jest-regex-util: 29.4.3 + jest-snapshot: 29.4.3 transitivePeerDependencies: - supports-color dev: true - /jest-resolve/29.4.2: - resolution: {integrity: sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw==} + /jest-resolve/29.4.3: + resolution: {integrity: sha512-GPokE1tzguRyT7dkxBim4wSx6E45S3bOQ7ZdKEG+Qj0Oac9+6AwJPCk0TZh5Vu0xzeX4afpb+eDmgbmZFFwpOw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: chalk: 4.1.2 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 - jest-pnp-resolver: 1.2.3_jest-resolve@29.4.2 - jest-util: 29.4.2 - jest-validate: 29.4.2 + jest-haste-map: 29.4.3 + jest-pnp-resolver: 1.2.3_jest-resolve@29.4.3 + jest-util: 29.4.3 + jest-validate: 29.4.3 resolve: 1.22.1 resolve.exports: 2.0.0 slash: 3.0.0 dev: true - /jest-runner/29.4.2: - resolution: {integrity: sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg==} + /jest-runner/29.4.3: + resolution: {integrity: sha512-GWPTEiGmtHZv1KKeWlTX9SIFuK19uLXlRQU43ceOQ2hIfA5yPEJC7AMkvFKpdCHx6pNEdOD+2+8zbniEi3v3gA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 29.4.2 - '@jest/environment': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/console': 29.4.3 + '@jest/environment': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.10 - jest-docblock: 29.4.2 - jest-environment-node: 29.4.2 - jest-haste-map: 29.4.2 - jest-leak-detector: 29.4.2 - jest-message-util: 29.4.2 - jest-resolve: 29.4.2 - jest-runtime: 29.4.2 - jest-util: 29.4.2 - jest-watcher: 29.4.2 - jest-worker: 29.4.2 + jest-docblock: 29.4.3 + jest-environment-node: 29.4.3 + jest-haste-map: 29.4.3 + jest-leak-detector: 29.4.3 + jest-message-util: 29.4.3 + jest-resolve: 29.4.3 + jest-runtime: 29.4.3 + jest-util: 29.4.3 + jest-watcher: 29.4.3 + jest-worker: 29.4.3 p-limit: 3.1.0 source-map-support: 0.5.13 transitivePeerDependencies: - supports-color dev: true - /jest-runtime/29.4.2: - resolution: {integrity: sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw==} + /jest-runtime/29.4.3: + resolution: {integrity: sha512-F5bHvxSH+LvLV24vVB3L8K467dt3y3dio6V3W89dUz9nzvTpqd/HcT9zfYKL2aZPvD63vQFgLvaUX/UpUhrP6Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.4.2 - '@jest/fake-timers': 29.4.2 - '@jest/globals': 29.4.2 - '@jest/source-map': 29.4.2 - '@jest/test-result': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/environment': 29.4.3 + '@jest/fake-timers': 29.4.3 + '@jest/globals': 29.4.3 + '@jest/source-map': 29.4.3 + '@jest/test-result': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 glob: 7.2.3 graceful-fs: 4.2.10 - jest-haste-map: 29.4.2 - jest-message-util: 29.4.2 - jest-mock: 29.4.2 - jest-regex-util: 29.4.2 - jest-resolve: 29.4.2 - jest-snapshot: 29.4.2 - jest-util: 29.4.2 - semver: 7.3.8 + jest-haste-map: 29.4.3 + jest-message-util: 29.4.3 + jest-mock: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.4.3 + jest-snapshot: 29.4.3 + jest-util: 29.4.3 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color dev: true - /jest-snapshot/29.4.2: - resolution: {integrity: sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw==} + /jest-snapshot/29.4.3: + resolution: {integrity: sha512-NGlsqL0jLPDW91dz304QTM/SNO99lpcSYYAjNiX0Ou+sSGgkanKBcSjCfp/pqmiiO1nQaOyLp6XQddAzRcx3Xw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.20.12 @@ -8478,23 +8387,23 @@ packages: '@babel/plugin-syntax-typescript': 7.20.0_@babel+core@7.20.12 '@babel/traverse': 7.20.12 '@babel/types': 7.20.7 - '@jest/expect-utils': 29.4.2 - '@jest/transform': 29.4.2 - '@jest/types': 29.4.2 + '@jest/expect-utils': 29.4.3 + '@jest/transform': 29.4.3 + '@jest/types': 29.4.3 '@types/babel__traverse': 7.18.3 '@types/prettier': 2.7.2 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.20.12 chalk: 4.1.2 - expect: 29.4.2 + expect: 29.4.3 graceful-fs: 4.2.10 - jest-diff: 29.4.2 - jest-get-type: 29.4.2 - jest-haste-map: 29.4.2 - jest-matcher-utils: 29.4.2 - jest-message-util: 29.4.2 - jest-util: 29.4.2 + jest-diff: 29.4.3 + jest-get-type: 29.4.3 + jest-haste-map: 29.4.3 + jest-matcher-utils: 29.4.3 + jest-message-util: 29.4.3 + jest-util: 29.4.3 natural-compare: 1.4.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 semver: 7.3.8 transitivePeerDependencies: - supports-color @@ -8504,52 +8413,64 @@ packages: resolution: {integrity: sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true - /jest-validate/29.4.2: - resolution: {integrity: sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q==} + /jest-util/29.4.3: + resolution: {integrity: sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.4.2 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 + chalk: 4.1.2 + ci-info: 3.7.1 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + + /jest-validate/29.4.3: + resolution: {integrity: sha512-J3u5v7aPQoXPzaar6GndAVhdQcZr/3osWSgTeKg5v574I9ybX/dTyH0AJFb5XgXIB7faVhf+rS7t4p3lL9qFaw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.4.3 camelcase: 6.3.0 chalk: 4.1.2 - jest-get-type: 29.4.2 + jest-get-type: 29.4.3 leven: 3.1.0 - pretty-format: 29.4.2 + pretty-format: 29.4.3 dev: true - /jest-watcher/29.4.2: - resolution: {integrity: sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w==} + /jest-watcher/29.4.3: + resolution: {integrity: sha512-zwlXH3DN3iksoIZNk73etl1HzKyi5FuQdYLnkQKm5BW4n8HpoG59xSwpVdFrnh60iRRaRBGw0gcymIxjJENPcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 29.4.2 - '@jest/types': 29.4.2 - '@types/node': 18.13.0 + '@jest/test-result': 29.4.3 + '@jest/types': 29.4.3 + '@types/node': 18.14.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 - jest-util: 29.4.2 + jest-util: 29.4.3 string-length: 4.0.2 dev: true - /jest-worker/29.4.2: - resolution: {integrity: sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw==} + /jest-worker/29.4.3: + resolution: {integrity: sha512-GLHN/GTAAMEy5BFdvpUfzr9Dr80zQqBrh0fz1mtRMe05hqP45+HfQltu7oTBfduD0UeZs09d+maFtFYAXFWvAA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.13.0 - jest-util: 29.4.2 + '@types/node': 18.14.0 + jest-util: 29.4.3 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest/29.4.2_@types+node@18.13.0: - resolution: {integrity: sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg==} + /jest/29.4.3_@types+node@18.14.0: + resolution: {integrity: sha512-XvK65feuEFGZT8OO0fB/QAQS+LGHvQpaadkH5p47/j3Ocqq3xf2pK9R+G0GzgfuhXVxEv76qCOOcMb5efLk6PA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -8558,10 +8479,10 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.4.2 - '@jest/types': 29.4.2 + '@jest/core': 29.4.3 + '@jest/types': 29.4.3 import-local: 3.1.0 - jest-cli: 29.4.2_@types+node@18.13.0 + jest-cli: 29.4.3_@types+node@18.14.0 transitivePeerDependencies: - '@types/node' - supports-color @@ -8679,7 +8600,7 @@ packages: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.12.0 + ws: 8.12.1 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -8789,6 +8710,10 @@ packages: resolution: {integrity: sha512-emiQ05haY9CRj1Ho/LiuCqr/+8RgJuWdiHYNglIg2Qjfz0n+pnUq9I2QHplXuOMO2EnAW1oCGC1++aU5VoWSlw==} dev: false + /jssha/3.3.0: + resolution: {integrity: sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==} + dev: false + /jstransformer/1.0.0: resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==} dependencies: @@ -8815,13 +8740,6 @@ packages: safe-buffer: 5.2.1 dev: false - /keygrip/1.1.0: - resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} - engines: {node: '>= 0.6'} - dependencies: - tsscmp: 1.0.6 - dev: false - /keyv/4.5.2: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: @@ -8856,49 +8774,6 @@ packages: engines: {node: '>=6'} dev: true - /koa-compose/4.1.0: - resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} - dev: false - - /koa-convert/2.0.0: - resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} - engines: {node: '>= 10'} - dependencies: - co: 4.6.0 - koa-compose: 4.1.0 - dev: false - - /koa/2.13.4: - resolution: {integrity: sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==} - engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} - dependencies: - accepts: 1.3.8 - cache-content-type: 1.0.1 - content-disposition: 0.5.4 - content-type: 1.0.4 - cookies: 0.8.0 - debug: 4.3.4 - delegates: 1.0.0 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - fresh: 0.5.2 - http-assert: 1.5.0 - http-errors: 1.8.1 - is-generator-function: 1.0.10 - koa-compose: 4.1.0 - koa-convert: 2.0.0 - on-finished: 2.4.1 - only: 0.0.2 - parseurl: 1.3.3 - statuses: 1.5.0 - type-is: 1.6.18 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - dev: false - /ky-universal/0.10.1_ky@0.30.0: resolution: {integrity: sha512-r8909k+ELKZAxhVA5c440x22hqw5XcMRwLRbgpPQk4JHy3/ddJnvzcnSo5Ww3HdKdNeS3Y8dBgcIYyVahMa46g==} engines: {node: '>=14'} @@ -9054,14 +8929,6 @@ packages: p-locate: 5.0.0 dev: true - /lodash.assignin/4.2.0: - resolution: {integrity: sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==} - dev: false - - /lodash.bind/4.2.1: - resolution: {integrity: sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==} - dev: false - /lodash.defaults/4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} @@ -9069,18 +8936,10 @@ packages: resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} dev: false - /lodash.filter/4.6.0: - resolution: {integrity: sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==} - dev: false - /lodash.flatten/4.4.0: resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} dev: false - /lodash.foreach/4.5.0: - resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==} - dev: false - /lodash.isarguments/3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} @@ -9092,10 +8951,6 @@ packages: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} dev: false - /lodash.map/4.6.0: - resolution: {integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==} - dev: false - /lodash.memoize/4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: false @@ -9107,22 +8962,6 @@ packages: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} dev: true - /lodash.pick/4.4.0: - resolution: {integrity: sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==} - dev: false - - /lodash.reduce/4.6.0: - resolution: {integrity: sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==} - dev: false - - /lodash.reject/4.6.0: - resolution: {integrity: sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==} - dev: false - - /lodash.some/4.6.0: - resolution: {integrity: sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==} - dev: false - /lodash.union/4.6.0: resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} dev: false @@ -9159,6 +8998,7 @@ packages: /lowercase-keys/2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} + dev: true /lowercase-keys/3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} @@ -9278,13 +9118,8 @@ packages: resolution: {integrity: sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==} dev: false - /matter-js/0.18.0: - resolution: {integrity: sha512-/ZVem4WygUnbmo/iE4oHZpZS97btfBtYy5Iwn1396vUZU7YhgVEN8J4UWwfZwY1ZqoTYlPgjvSw9WXauuXL0mg==} - dev: false - - /media-typer/0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} + /matter-js/0.19.0: + resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==} dev: false /merge-stream/2.0.0: @@ -9356,6 +9191,7 @@ packages: /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} + dev: true /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} @@ -9381,6 +9217,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch/6.2.0: + resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist/1.2.7: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} @@ -9534,8 +9377,8 @@ packages: optionalDependencies: msgpackr-extract: 2.2.0 - /muggle-string/0.1.0: - resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==} + /muggle-string/0.2.2: + resolution: {integrity: sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==} dev: true /multi-integer-range/3.0.0: @@ -9798,6 +9641,7 @@ packages: /normalize-url/6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} + dev: true /normalize-url/8.0.0: resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} @@ -9860,17 +9704,10 @@ packages: '@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5 dev: false - /nth-check/1.0.2: - resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} - dependencies: - boolbase: 1.0.0 - dev: false - /nth-check/2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: boolbase: 1.0.0 - dev: true /num2fraction/1.2.2: resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} @@ -9993,13 +9830,6 @@ packages: resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==} dev: false - /on-finished/2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - dependencies: - ee-first: 1.1.1 - dev: false - /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -10018,10 +9848,6 @@ packages: mimic-fn: 4.0.0 dev: true - /only/0.0.2: - resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} - dev: false - /opentype.js/0.4.11: resolution: {integrity: sha512-GthxucX/6aftfLdeU5Ho7o7zmQcC8uVtqdcelVq12X++ndxwBZG8Xb5rFEKT7nEcWDD2P1x+TNuJ70jtj1Mbpw==} hasBin: true @@ -10079,9 +9905,16 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true + /otpauth/9.0.2: + resolution: {integrity: sha512-0TzpkJYg24VvIK3/K91HKpTtMlwm73UoThhcGY8fZsXcwHDrqf008rfdOjj3NnQuyuT11+vHyyO//qRzi6OZ9A==} + dependencies: + jssha: 3.3.0 + dev: false + /p-cancelable/2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} + dev: true /p-cancelable/3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} @@ -10210,6 +10043,13 @@ packages: parse5: 6.0.1 dev: false + /parse5-htmlparser2-tree-adapter/7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.2 + dev: false + /parse5/5.1.1: resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} dev: false @@ -10223,11 +10063,6 @@ packages: dependencies: entities: 4.4.0 - /parseurl/1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - dev: false - /pascalcase/0.1.1: resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} engines: {node: '>=0.10.0'} @@ -10775,11 +10610,11 @@ packages: react-is: 18.2.0 dev: true - /pretty-format/29.4.2: - resolution: {integrity: sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg==} + /pretty-format/29.4.3: + resolution: {integrity: sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.4.2 + '@jest/schemas': 29.4.3 ansi-styles: 5.2.0 react-is: 18.2.0 dev: true @@ -11415,11 +11250,6 @@ packages: uuid: 3.4.0 dev: false - /require-all/3.0.0: - resolution: {integrity: sha512-jPGN876lc5exWYrMcgZSd7U42P0PmVQzxnQB13fCSzmyGnqQWW4WUz5DosZ/qe24hz+5o9lSvW2epBNZ1xa6Fw==} - engines: {node: '>= 0.8'} - dev: false - /require-directory/2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -11497,6 +11327,7 @@ packages: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} dependencies: lowercase-keys: 2.0.0 + dev: true /responselike/3.0.0: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} @@ -11555,8 +11386,8 @@ packages: seedrandom: 2.4.2 dev: false - /rollup/3.14.0: - resolution: {integrity: sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==} + /rollup/3.17.2: + resolution: {integrity: sha512-qMNZdlQPCkWodrAZ3qnJtvCAl4vpQ8q77uEujVCCbC/6CLB7Lcmvjq7HyiOSnf4fxTT9XgsE36oLHJBH49xjqA==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -11623,8 +11454,8 @@ packages: /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sanitize-html/2.9.0: - resolution: {integrity: sha512-KY1hpSbqFNcpoLf+nP7iStbP5JfQZ2Nd19ZEE7qFsQqRdp+sO5yX/e5+HoG9puFAcSTEpzQuihfKUltDcLlQjg==} + /sanitize-html/2.10.0: + resolution: {integrity: sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==} dependencies: deepmerge: 4.2.2 escape-string-regexp: 4.0.0 @@ -11634,8 +11465,8 @@ packages: postcss: 8.4.21 dev: false - /sass/1.58.0: - resolution: {integrity: sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==} + /sass/1.58.3: + resolution: {integrity: sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==} engines: {node: '>=12.0.0'} hasBin: true dependencies: @@ -11995,13 +11826,6 @@ packages: resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} dev: false - /speakeasy/2.0.0: - resolution: {integrity: sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==} - engines: {node: '>= 0.10.0'} - dependencies: - base32.js: 0.0.1 - dev: false - /split-string/3.1.0: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} @@ -12063,8 +11887,8 @@ packages: /standard-as-callback/2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - /start-server-and-test/1.15.3: - resolution: {integrity: sha512-4GqkqghvUR9cJ8buvtgkyT0AHgVwCJ5EN8eDEhe9grTChGwWUxGm2nqfSeE9+0PZkLRdFqcwTwxVHe1y3ViutQ==} + /start-server-and-test/1.15.4: + resolution: {integrity: sha512-ucQtp5+UCr0m4aHlY+aEV2JSYNTiMZKdSKK/bsIr6AlmwAWDYDnV7uGlWWEtWa7T4XvRI5cPYcPcQgeLqpz+Tg==} engines: {node: '>=6'} hasBin: true dependencies: @@ -12088,11 +11912,6 @@ packages: object-copy: 0.1.0 dev: false - /statuses/1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - dev: false - /statuses/2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -12269,24 +12088,6 @@ packages: '@tokenizer/token': 0.3.0 peek-readable: 5.0.0 - /summaly/2.7.0: - resolution: {integrity: sha512-pEz9LL8Gp0oPIQfn6TrnBCcv/HkFE14hxhH3W6LPGdopXlPXjRcMlDMJaO+VupUNMOGaMjCsjq7+0rWnu8sp7w==} - dependencies: - cheerio: 0.22.0 - debug: 4.3.3 - escape-regexp: 0.0.1 - got: 11.8.5 - html-entities: 2.3.2 - iconv-lite: 0.6.3 - jschardet: 3.0.0 - koa: 2.13.4 - private-ip: 2.3.3 - require-all: 3.0.0 - trace-redirect: 1.0.6 - transitivePeerDependencies: - - supports-color - dev: false - /supports-color/2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} engines: {node: '>=0.8.0'} @@ -12349,8 +12150,8 @@ packages: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: false - /systeminformation/5.17.8: - resolution: {integrity: sha512-kMQsCOFQXqEjoDWKpYkC+ezJImrOnyTL3FZnYDZdWLTZcMW7Gomdp8zH9ztGlSo2L1iCAQXZ8HlV1bI8/Ts64g==} + /systeminformation/5.17.9: + resolution: {integrity: sha512-inxwRLI/4qpx4o85R54/zdhNagdBGBgs0la7Vl3qBorRVKRDk0nNsDTCGzG4lOITsw1gl7LRWeG4Zsp1pC8nfg==} engines: {node: '>=8.0.0'} os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true @@ -12675,11 +12476,6 @@ packages: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} dev: false - /tsscmp/1.0.6: - resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} - engines: {node: '>=0.6.x'} - dev: false - /tsutils/3.21.0_typescript@4.9.5: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -12730,14 +12526,6 @@ packages: engines: {node: '>=10'} dev: true - /type-is/1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - dev: false - /type/1.2.0: resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} dev: false @@ -13174,8 +12962,8 @@ packages: replace-ext: 1.0.1 dev: false - /vite/4.1.1_gyrp4zacqcjjrmgvdzgac5epyy: - resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==} + /vite/4.1.2_hlkwzk2izwsolfmdrejei4vrty: + resolution: {integrity: sha512-MWDb9Rfy3DI8omDQySbMK93nQqStwbsQWejXRY2EBzEWKmLAXWb1mkI9Yw2IJrc+oCvPCI1Os5xSSIBYY6DEAw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -13199,12 +12987,12 @@ packages: terser: optional: true dependencies: - '@types/node': 18.13.0 + '@types/node': 18.14.0 esbuild: 0.16.17 postcss: 8.4.21 resolve: 1.22.1 - rollup: 3.14.0 - sass: 1.58.0 + rollup: 3.17.2 + sass: 1.58.3 optionalDependencies: fsevents: 2.3.2 dev: false @@ -13214,14 +13002,14 @@ packages: engines: {node: '>=0.10.0'} dev: false - /vue-eslint-parser/9.1.0_eslint@8.33.0: + /vue-eslint-parser/9.1.0_eslint@8.34.0: resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 eslint-scope: 7.1.1 eslint-visitor-keys: 3.3.0 espree: 9.4.1 @@ -13255,14 +13043,14 @@ packages: he: 1.2.0 dev: true - /vue-tsc/1.0.24_typescript@4.9.5: - resolution: {integrity: sha512-mmU1s5SAqE1nByQAiQnao9oU4vX+mSdsgI8H57SfKH6UVzq/jP9+Dbi2GaV+0b4Cn361d2ln8m6xeU60ApiEXg==} + /vue-tsc/1.1.4_typescript@4.9.5: + resolution: {integrity: sha512-CMG8KZsBBPyulYie05XxJCfq/yAPiB/uMMeHmog09aLm2Kml82C6tUSRgQz8ujM4JoCrpDqVCd9G0NuM9aLt1g==} hasBin: true peerDependencies: typescript: '*' dependencies: - '@volar/vue-language-core': 1.0.24 - '@volar/vue-typescript': 1.0.24 + '@volar/vue-language-core': 1.1.4 + '@volar/vue-typescript': 1.1.4 typescript: 4.9.5 dev: true @@ -13489,8 +13277,8 @@ packages: signal-exit: 3.0.7 dev: true - /ws/8.12.0: - resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} + /ws/8.12.1: + resolution: {integrity: sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -13672,11 +13460,6 @@ packages: fd-slicer: 1.1.0 dev: true - /ylru/1.3.2: - resolution: {integrity: sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==} - engines: {node: '>= 4.0.0'} - dev: false - /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -13696,6 +13479,21 @@ packages: version: 2.2.1-misskey.3 dev: false + github.com/misskey-dev/summaly/51f3870e1ff5e0b22102e804112b10cb72f3c494: + resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/51f3870e1ff5e0b22102e804112b10cb72f3c494} + name: summaly + version: 3.0.4 + dependencies: + cheerio: 1.0.0-rc.12 + escape-regexp: 0.0.1 + got: 12.5.3 + html-entities: 2.3.2 + iconv-lite: 0.6.3 + jschardet: 3.0.0 + private-ip: 2.3.3 + trace-redirect: 1.0.6 + dev: false + github.com/sampotts/plyr/d434c9af16e641400aaee93188594208d88f2658: resolution: {tarball: https://codeload.github.com/sampotts/plyr/tar.gz/d434c9af16e641400aaee93188594208d88f2658} name: plyr