Merge branch 'develop' into 'fix/local-user-notes-count'
# Conflicts: # packages/backend/src/services/note/delete.ts
This commit is contained in:
commit
d002741ecc
194 changed files with 1947 additions and 1402 deletions
|
@ -48,7 +48,7 @@ FROM docker.io/node:20-slim
|
||||||
WORKDIR /firefish
|
WORKDIR /firefish
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install runtime dependencies
|
||||||
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get install -y --no-install-recommends zip unzip tini ffmpeg ca-certificates
|
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get install -y --no-install-recommends zip unzip tini ffmpeg ca-certificates curl
|
||||||
|
|
||||||
RUN echo 'deb https://deb.debian.org/debian experimental main' | tee /etc/apt/sources.list
|
RUN echo 'deb https://deb.debian.org/debian experimental main' | tee /etc/apt/sources.list
|
||||||
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get --target-release experimental install -y --no-install-recommends libc6
|
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get --target-release experimental install -y --no-install-recommends libc6
|
||||||
|
|
|
@ -48,7 +48,7 @@ If you have access to a server that supports one of the sources below, I recomme
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- At least [NodeJS](https://nodejs.org/en/) v18.16.0 (v20/v21 recommended)
|
- At least [NodeJS](https://nodejs.org/en/) v18.17.0 (v20/v21 recommended)
|
||||||
- At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended)
|
- At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended)
|
||||||
- At least [Redis](https://redis.io/) v7
|
- At least [Redis](https://redis.io/) v7
|
||||||
- Web Proxy (one of the following)
|
- Web Proxy (one of the following)
|
||||||
|
|
|
@ -6,8 +6,10 @@ services:
|
||||||
container_name: firefish_web
|
container_name: firefish_web
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
db:
|
||||||
- redis
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
networks:
|
networks:
|
||||||
|
@ -19,6 +21,15 @@ services:
|
||||||
- ./custom:/firefish/custom:ro
|
- ./custom:/firefish/custom:ro
|
||||||
- ./files:/firefish/files
|
- ./files:/firefish/files
|
||||||
- ./.config:/firefish/.config:ro
|
- ./.config:/firefish/.config:ro
|
||||||
|
healthcheck:
|
||||||
|
test: curl -f http://localhost:3000 || exit 1
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 4096M
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -28,6 +39,16 @@ services:
|
||||||
- calcnet
|
- calcnet
|
||||||
volumes:
|
volumes:
|
||||||
- ./redis:/data
|
- ./redis:/data
|
||||||
|
healthcheck:
|
||||||
|
test: redis-cli ping
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
# deploy:
|
||||||
|
# resources:
|
||||||
|
# limits:
|
||||||
|
# memory: 200M
|
||||||
|
|
||||||
|
|
||||||
db:
|
db:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -39,6 +60,15 @@ services:
|
||||||
- .config/docker.env
|
- .config/docker.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./db:/var/lib/postgresql/data
|
- ./db:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: pg_isready --user="$${POSTGRES_USER}" --dbname="$${POSTGRES_DB}"
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
# deploy:
|
||||||
|
# resources:
|
||||||
|
# limits:
|
||||||
|
# memory: 200M
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
calcnet:
|
calcnet:
|
||||||
|
|
|
@ -4,6 +4,7 @@ Breaking changes are indicated by the :warning: icon.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- :warning: `followingCount` and `followersCount` in `users/show` will be `null` (instead of 0) if these values are unavailable.
|
||||||
- :warning: `admin/search/index-all` is removed since posts are now indexed automatically.
|
- :warning: `admin/search/index-all` is removed since posts are now indexed automatically.
|
||||||
- New optional parameters are added to `notes/search` endpoint:
|
- New optional parameters are added to `notes/search` endpoint:
|
||||||
- `sinceDate`
|
- `sinceDate`
|
||||||
|
|
|
@ -11,9 +11,9 @@ Critical security updates are indicated by the :warning: icon.
|
||||||
- Add langage annotation to post contents (!10687)
|
- Add langage annotation to post contents (!10687)
|
||||||
- Add a toggleable setting to show a warning when you attempt to post files without alt text
|
- Add a toggleable setting to show a warning when you attempt to post files without alt text
|
||||||
- Fix bugs
|
- Fix bugs
|
||||||
- Update documents
|
- Update documents and example config files
|
||||||
|
|
||||||
## v20240301
|
## [v20240301](https://firefish.dev/firefish/firefish/-/compare/v20240229...v20240301?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Add a page (`/my/follow-requests/sent`) to check your follow requests that haven't been approved
|
- Add a page (`/my/follow-requests/sent`) to check your follow requests that haven't been approved
|
||||||
- Add ability to hide replies from certain users in timelines
|
- Add ability to hide replies from certain users in timelines
|
||||||
|
@ -32,34 +32,34 @@ Critical security updates are indicated by the :warning: icon.
|
||||||
- Disable new user registration
|
- Disable new user registration
|
||||||
- Fix bugs
|
- Fix bugs
|
||||||
|
|
||||||
## v20240229
|
## [v20240229](https://firefish.dev/firefish/firefish/-/compare/v20240228...v20240229?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Add ability to pull-down-to-refresh timelines in PWA
|
- Add ability to pull-down-to-refresh timelines in PWA
|
||||||
- Make passkey/security key independent of TOTP (!10670)
|
- Make passkey/security key independent of TOTP (!10670)
|
||||||
- Fix bugs
|
- Fix bugs
|
||||||
|
|
||||||
## v20240228
|
## [v20240228](https://firefish.dev/firefish/firefish/-/compare/v20240225...v20240228?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Update "About Firefish" page (!10673)
|
- Update "About Firefish" page (!10673)
|
||||||
- Fix bugs (!10675 !10676 !10678 !10679)
|
- Fix bugs (!10675 !10676 !10678 !10679)
|
||||||
- Remove charts generation to improve performance (#10611)
|
- Remove charts generation to improve performance (#10611)
|
||||||
|
|
||||||
## v20240225
|
## [v20240225](https://firefish.dev/firefish/firefish/-/compare/v20240222...v20240225?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Fix bugs
|
- Fix bugs
|
||||||
- Add syntax highlighting in MFM code blocks in various programming languages
|
- Add syntax highlighting in MFM code blocks in various programming languages
|
||||||
|
|
||||||
## v20240222
|
## [v20240222](https://firefish.dev/firefish/firefish/-/compare/v20240221-1...v20240222?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Enhance Mastodon post import feature (!10652)
|
- Enhance Mastodon post import feature (!10652)
|
||||||
- Minor style change in the web client
|
- Minor style change in the web client
|
||||||
- Refactoring
|
- Refactoring
|
||||||
|
|
||||||
## v20240221-1
|
## [v20240221-1](https://firefish.dev/firefish/firefish/-/compare/v20240221...v20240221-1?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Fix a bug
|
- Fix a bug
|
||||||
|
|
||||||
## v20240221
|
## [v20240221](https://firefish.dev/firefish/firefish/-/compare/v20240217-1...v20240221?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Add the ability to give regular (non-moderator) users permission to manage custom emojis
|
- Add the ability to give regular (non-moderator) users permission to manage custom emojis
|
||||||
- Fix a bug that made impossible to update user profiles under some conditions
|
- Fix a bug that made impossible to update user profiles under some conditions
|
||||||
|
@ -67,11 +67,11 @@ Critical security updates are indicated by the :warning: icon.
|
||||||
- It's just a paraphrase of DMs without recipients
|
- It's just a paraphrase of DMs without recipients
|
||||||
- You can also convert your existing public posts to private posts
|
- You can also convert your existing public posts to private posts
|
||||||
|
|
||||||
## :warning: v20240217-1
|
## :warning: [v20240217-1](https://firefish.dev/firefish/firefish/-/compare/v20240217...v20240217-1?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Fix a [security issue](https://github.com/misskey-dev/misskey/security/advisories/GHSA-qqrm-9grj-6v32)
|
- Fix a [security issue](https://github.com/misskey-dev/misskey/security/advisories/GHSA-qqrm-9grj-6v32)
|
||||||
|
|
||||||
## v20240217
|
## [v20240217](https://firefish.dev/firefish/firefish/-/compare/v20240216...v20240217?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Add ability to specify the search engine used in the search bar MFM
|
- Add ability to specify the search engine used in the search bar MFM
|
||||||
- Remove auto NSFW media detection
|
- Remove auto NSFW media detection
|
||||||
|
@ -80,49 +80,49 @@ Critical security updates are indicated by the :warning: icon.
|
||||||
- Change the second tab on the notifications page from "unread" to "reactions"
|
- Change the second tab on the notifications page from "unread" to "reactions"
|
||||||
- Add ability to show a huge post button on the posting form
|
- Add ability to show a huge post button on the posting form
|
||||||
- This is a joke feature inspired by https://mstdn.poyo.me/@prime/110668364208741253
|
- This is a joke feature inspired by https://mstdn.poyo.me/@prime/110668364208741253
|
||||||
- Bug fix
|
- Fix bugs
|
||||||
- Add `/api/emojis` endpoint (compatible with Misskey v13) for better experiences with Misskey clients
|
- Add `/api/emojis` endpoint (compatible with Misskey v13) for better experiences with Misskey clients
|
||||||
- This does not mean we will continue to maintain API compatibility with Misskey. Instead, we plan to improve the compatibility with the Mastodon API.
|
- This does not mean we will continue to maintain API compatibility with Misskey. Instead, we plan to improve the compatibility with the Mastodon API.
|
||||||
|
|
||||||
## v20240216
|
## [v20240216](https://firefish.dev/firefish/firefish/-/compare/v20240215...v20240216?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Style changes in the web client (a770ef4314e21f17fdce1f19feb3758953b04486 ab39ff5954a392cc6688a02f1723e1702df5e35c 4eefd534d8150e2cd5cf31dddd327edceb5b84dc)
|
- Style changes in the web client (a770ef4314e21f17fdce1f19feb3758953b04486 ab39ff5954a392cc6688a02f1723e1702df5e35c 4eefd534d8150e2cd5cf31dddd327edceb5b84dc)
|
||||||
- Clicking the "like" button now sends the actual emoji reaction (star, good, heart, etc.) instead of an empty "like"
|
- Clicking the "like" button now sends the actual emoji reaction (star, good, heart, etc.) instead of an empty "like"
|
||||||
|
|
||||||
## v20240215
|
## [v20240215](https://firefish.dev/firefish/firefish/-/compare/v20240214...v20240215?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Separate settings for displaying rounded avatars for cat and non-cat accounts
|
- Separate settings for displaying rounded avatars for cat and non-cat accounts
|
||||||
- Add a toggleable setting to replace the chat button with account menu on mobile
|
- Add a toggleable setting to replace the chat button with account menu on mobile
|
||||||
- Reduce the size of the container image (!10667)
|
- Reduce the size of the container image (!10667)
|
||||||
|
|
||||||
## v20240214
|
## [v20240214](https://firefish.dev/firefish/firefish/-/compare/v20240213...v20240214?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Fix container images
|
- Fix container images
|
||||||
|
|
||||||
## v20240213
|
## [v20240213](https://firefish.dev/firefish/firefish/-/compare/v20240212...v20240213?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Bug fix
|
- Fix bugs
|
||||||
- Refactoring
|
- Refactoring
|
||||||
|
|
||||||
## v20240212
|
## [v20240212](https://firefish.dev/firefish/firefish/-/compare/v20240210...v20240212?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Refactoring
|
- Refactoring
|
||||||
- Add a toggleable setting to hide follow buttons in a misclickable position
|
- Add a toggleable setting to hide follow buttons in a misclickable position
|
||||||
- Add a toggleable setting to show preview in posting form by default
|
- Add a toggleable setting to show preview in posting form by default
|
||||||
|
|
||||||
## v20240210
|
## [v20240210](https://firefish.dev/firefish/firefish/-/compare/v20240208...v20240210?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Security update (cf5b42a160ae8a4d94bf3dcea04ce12935ca4f76)
|
- Security update (cf5b42a160ae8a4d94bf3dcea04ce12935ca4f76)
|
||||||
- Refactoring
|
- Refactoring
|
||||||
|
|
||||||
## v20240208
|
## [v20240208](https://firefish.dev/firefish/firefish/-/compare/v20240206...v20240208?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Bug fix (!10654 !10665)
|
- Fix bugs (!10654 !10665)
|
||||||
- Enlarge profile picture by clicking it (!10659)
|
- Enlarge profile picture by clicking it (!10659)
|
||||||
- Support Pleroma chat (!10660)
|
- Support Pleroma chat (!10660)
|
||||||
- [Add documentation about downgrading](./docs/downgrade.md)
|
- [Add documentation about downgrading](./docs/downgrade.md)
|
||||||
|
|
||||||
## v20240206
|
## [v20240206](https://firefish.dev/firefish/firefish/-/compare/v1.0.5-rc...v20240206?from_project_id=7&straight=false)
|
||||||
|
|
||||||
- Many bug fixes
|
- Fix many bugs
|
||||||
- Per-post language selector (!10616)
|
- Per-post language selector (!10616)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
DELETE FROM "migrations" WHERE name IN (
|
DELETE FROM "migrations" WHERE name IN (
|
||||||
|
'FixMutingIndices1710690239308',
|
||||||
|
'RemoveMentionedUsersColumn1710688552234',
|
||||||
|
'NoteFile1710304584214',
|
||||||
'RenameMetaColumns1705944717480',
|
'RenameMetaColumns1705944717480',
|
||||||
'SeparateHardMuteWordsAndPatterns1706413792769',
|
'SeparateHardMuteWordsAndPatterns1706413792769',
|
||||||
'IndexAltTextAndCw1708872574733',
|
'IndexAltTextAndCw1708872574733',
|
||||||
|
@ -16,6 +19,33 @@ DELETE FROM "migrations" WHERE name IN (
|
||||||
'RemoveNativeUtilsMigration1705877093218'
|
'RemoveNativeUtilsMigration1705877093218'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- fix-muting-indices
|
||||||
|
DROP INDEX "IDX_renote_muting_createdAt";
|
||||||
|
DROP INDEX "IDX_renote_muting_muteeId";
|
||||||
|
DROP INDEX "IDX_renote_muting_muterId";
|
||||||
|
DROP INDEX "IDX_reply_muting_createdAt";
|
||||||
|
DROP INDEX "IDX_reply_muting_muteeId";
|
||||||
|
DROP INDEX "IDX_reply_muting_muterId";
|
||||||
|
CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt");
|
||||||
|
CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId");
|
||||||
|
CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId");
|
||||||
|
|
||||||
|
-- remove-mentioned-users-column
|
||||||
|
ALTER TABLE "note" ADD "mentionedRemoteUsers" text NOT NULL DEFAULT '[]'::text;
|
||||||
|
CREATE TABLE "temp_mentions_1710688552234" AS
|
||||||
|
SELECT "id", "url", "uri", "username", "host"
|
||||||
|
FROM "user"
|
||||||
|
JOIN "user_profile" ON "user"."id" = "user_profile". "userId" WHERE "user"."host" IS NOT NULL;
|
||||||
|
CREATE UNIQUE INDEX "temp_mentions_id" ON "temp_mentions_1710688552234" ("id");
|
||||||
|
UPDATE "note" SET "mentionedRemoteUsers" = (
|
||||||
|
SELECT COALESCE(json_agg(row_to_json("data")::jsonb - 'id')::text, '[]') FROM "temp_mentions_1710688552234" AS "data"
|
||||||
|
WHERE "data"."id" = ANY("note"."mentions")
|
||||||
|
);
|
||||||
|
DROP TABLE "temp_mentions_1710688552234";
|
||||||
|
|
||||||
|
-- note-file
|
||||||
|
DROP TABLE "note_file";
|
||||||
|
|
||||||
-- rename-meta-columns
|
-- rename-meta-columns
|
||||||
ALTER TABLE "meta" RENAME COLUMN "tosUrl" TO "ToSUrl";
|
ALTER TABLE "meta" RENAME COLUMN "tosUrl" TO "ToSUrl";
|
||||||
ALTER TABLE "meta" RENAME COLUMN "objectStorageUseSsl" TO "objectStorageUseSSL";
|
ALTER TABLE "meta" RENAME COLUMN "objectStorageUseSsl" TO "objectStorageUseSSL";
|
||||||
|
|
|
@ -4,6 +4,9 @@ The full-text search engine used in Firefish has been changed to [PGroonga](http
|
||||||
|
|
||||||
## For systemd/pm2 users
|
## For systemd/pm2 users
|
||||||
|
|
||||||
|
- Required Node.js version has been bumped from v18.16.0 to v18.17.0.
|
||||||
|
- You need to install PGroonga on your system. Please follow the instructions below.
|
||||||
|
|
||||||
### 1. Install PGroonga
|
### 1. Install PGroonga
|
||||||
|
|
||||||
Please execute `psql --version` to check your PostgreSQL major version. This will print a message like this:
|
Please execute `psql --version` to check your PostgreSQL major version. This will print a message like this:
|
||||||
|
|
|
@ -323,8 +323,8 @@ _2fa:
|
||||||
securityKeyInfo: A més de l'autenticació d'empremta digital o PIN, també podeu configurar
|
securityKeyInfo: A més de l'autenticació d'empremta digital o PIN, també podeu configurar
|
||||||
l'autenticació mitjançant claus de seguretat de maquinari compatibles amb FIDO2
|
l'autenticació mitjançant claus de seguretat de maquinari compatibles amb FIDO2
|
||||||
per protegir encara més el vostre compte.
|
per protegir encara més el vostre compte.
|
||||||
step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquest
|
step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquesta
|
||||||
token d'inici de sessió.
|
clau d'inici de sessió.
|
||||||
registerSecurityKey: Registrar una clau de seguretat o d'accés
|
registerSecurityKey: Registrar una clau de seguretat o d'accés
|
||||||
step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b})
|
step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b})
|
||||||
al dispositiu.
|
al dispositiu.
|
||||||
|
@ -2067,9 +2067,8 @@ _relayStatus:
|
||||||
deleted: Eliminat
|
deleted: Eliminat
|
||||||
editNote: Edita la publicació
|
editNote: Edita la publicació
|
||||||
edited: 'Editat el {date} {time}'
|
edited: 'Editat el {date} {time}'
|
||||||
signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades,
|
signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades.
|
||||||
però sempre podeu registrar-vos en un altre servidor. Si teniu un codi d'invitació
|
Si teniu un codi d'invitació per a aquest servidor, introduïu-lo a continuació.
|
||||||
per a aquest servidor, introduïu-lo a continuació.
|
|
||||||
userSaysSomethingReasonQuote: '{name} ha citat una publicació que conté {reason}'
|
userSaysSomethingReasonQuote: '{name} ha citat una publicació que conté {reason}'
|
||||||
userSaysSomethingReasonReply: '{name} ha respost a una publicació que conté {reason}'
|
userSaysSomethingReasonReply: '{name} ha respost a una publicació que conté {reason}'
|
||||||
userSaysSomethingReasonRenote: '{name} ha impulsat una publicació que conté {reason}'
|
userSaysSomethingReasonRenote: '{name} ha impulsat una publicació que conté {reason}'
|
||||||
|
@ -2253,7 +2252,34 @@ searchWordsDescription: "Per cercar publicacions, escriu el terme a buscar. Sepa
|
||||||
o la ID en aquest camp i fes clic al botó 'Trobar'. Fent clic a 'Cercar' trobarà
|
o la ID en aquest camp i fes clic al botó 'Trobar'. Fent clic a 'Cercar' trobarà
|
||||||
publicacions que, literalment , continguin la ID/adreça URL."
|
publicacions que, literalment , continguin la ID/adreça URL."
|
||||||
searchPostsWithFiles: Només publicacions amb fitxers
|
searchPostsWithFiles: Només publicacions amb fitxers
|
||||||
searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions.
|
searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions
|
||||||
searchUsers: Publicat per (opcional)
|
searchUsers: Publicat per (opcional)
|
||||||
searchRange: Publicat dintre de (opcional)
|
searchRange: Publicat dintre de (opcional)
|
||||||
publishTimelines: Publica línies de temps per visitants
|
publishTimelines: Publica línies de temps per visitants
|
||||||
|
toPost: Publicà
|
||||||
|
publishTimelinesDescription: Si està activat, les línies de temps Global i Local es
|
||||||
|
mostraran a {url} fins i tot sense estar registrat.
|
||||||
|
noAltTextWarning: Alguns fitxers adjunts no tenen una descripció. T'has s oblidat
|
||||||
|
d'escriure-les?
|
||||||
|
showNoAltTextWarning: Mostra un avís si públiques un fitxer sense descripció
|
||||||
|
toReply: Respon
|
||||||
|
toQuote: Cita
|
||||||
|
toEdit: Edita
|
||||||
|
searchUsersDescription: "Per buscar publicacions concretes d'un usuari/servidor, escriu
|
||||||
|
la ID (@usuari@exemple.com, o @usuari per un usuari local) o nom del domini (exemple.com).\n
|
||||||
|
\nSi escrius 'me' (sense cometes), totes les teves publicacions (incloent-hi publicacions
|
||||||
|
sense llistar, només per a seguidors i secretes) es buscaran.\n\nSi escrius 'local'
|
||||||
|
(sense cometes), el resultat serà filtrat per mostrar només publicacions d'aquest
|
||||||
|
servidor."
|
||||||
|
messagingUnencryptedInfo: Els xats a Firefish no són encriptats d'extrem a extrem.
|
||||||
|
No comparteixis dades sensibles fent servir Firefish.
|
||||||
|
searchRangeDescription: "Si vols filtrar per un període de temps, has de fer servir
|
||||||
|
aquest format: 20220615-20231031\n\nSi no escrius l'any (per exemple 0105-0106 o
|
||||||
|
20231105-0110), serà interpretat com l'any en curs.\n\nInclús pots morir la data
|
||||||
|
de començament o de finalització. Per exemple, -0102 filtrarà els resultats per
|
||||||
|
mostrar només publicacions fetes abans del 2 de gener d'aquest any, i 20231026-
|
||||||
|
filtrarà els resultats per mostrar publicacions fetes després del 26 d'octubre del
|
||||||
|
2023."
|
||||||
|
moderationNote: Nota de moderació
|
||||||
|
ipFirstAcknowledged: Data en què es va veure la adreça IP per primera vegada
|
||||||
|
driveCapacityOverride: Capacitat del disc esgotada
|
||||||
|
|
|
@ -2218,3 +2218,4 @@ renotes: Boosts
|
||||||
quotes: Zitate
|
quotes: Zitate
|
||||||
moreUrlsDescription: "Die Seiten, welche angepinnt werde sollen, im Hilfe-Menü in
|
moreUrlsDescription: "Die Seiten, welche angepinnt werde sollen, im Hilfe-Menü in
|
||||||
der unteren linken Ecke in folgender Notation angeben:\n\"Anzeigename\": https://example.com/"
|
der unteren linken Ecke in folgender Notation angeben:\n\"Anzeigename\": https://example.com/"
|
||||||
|
toQuote: Zitat
|
||||||
|
|
|
@ -462,7 +462,7 @@ securityKeyName: "Key name"
|
||||||
registerSecurityKey: "Register a security key"
|
registerSecurityKey: "Register a security key"
|
||||||
lastUsed: "Last used"
|
lastUsed: "Last used"
|
||||||
unregister: "Unregister"
|
unregister: "Unregister"
|
||||||
passwordLessLogin: "Password-less login"
|
passwordLessLogin: "Password-less sign in"
|
||||||
resetPassword: "Reset password"
|
resetPassword: "Reset password"
|
||||||
newPasswordIs: "The new password is \"{password}\""
|
newPasswordIs: "The new password is \"{password}\""
|
||||||
reduceUiAnimation: "Reduce UI animations"
|
reduceUiAnimation: "Reduce UI animations"
|
||||||
|
@ -527,7 +527,7 @@ disableDrawer: "Don't use drawer-style menus"
|
||||||
youHaveNoGroups: "You have no groups"
|
youHaveNoGroups: "You have no groups"
|
||||||
joinOrCreateGroup: "Get invited to a group or create your own."
|
joinOrCreateGroup: "Get invited to a group or create your own."
|
||||||
noHistory: "No history available"
|
noHistory: "No history available"
|
||||||
signinHistory: "Login history"
|
signinHistory: "Sign in history"
|
||||||
disableAnimatedMfm: "Disable MFM with animation"
|
disableAnimatedMfm: "Disable MFM with animation"
|
||||||
doing: "Processing..."
|
doing: "Processing..."
|
||||||
category: "Category"
|
category: "Category"
|
||||||
|
@ -717,9 +717,9 @@ useGlobalSetting: "Use global settings"
|
||||||
useGlobalSettingDesc: "If turned on, your account's notification settings will be
|
useGlobalSettingDesc: "If turned on, your account's notification settings will be
|
||||||
used. If turned off, individual configurations can be made."
|
used. If turned off, individual configurations can be made."
|
||||||
other: "Other"
|
other: "Other"
|
||||||
regenerateLoginToken: "Regenerate login token"
|
regenerateLoginToken: "Regenerate sign in token"
|
||||||
regenerateLoginTokenDescription: "Regenerates the token used internally during login.
|
regenerateLoginTokenDescription: "Regenerates the token used internally during sign
|
||||||
Normally this action is not necessary. If regenerated, all devices will be logged
|
in. Normally this action is not necessary. If regenerated, all devices will be logged
|
||||||
out."
|
out."
|
||||||
setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces."
|
setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces."
|
||||||
fileIdOrUrl: "File ID or URL"
|
fileIdOrUrl: "File ID or URL"
|
||||||
|
@ -1001,7 +1001,7 @@ check: "Check"
|
||||||
driveCapOverrideLabel: "Change the drive capacity for this user"
|
driveCapOverrideLabel: "Change the drive capacity for this user"
|
||||||
driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0
|
driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0
|
||||||
or lower."
|
or lower."
|
||||||
requireAdminForView: "You must log in with an administrator account to view this."
|
requireAdminForView: "You must sign in with an administrator account to view this."
|
||||||
isSystemAccount: "This account is created and automatically operated by the system.
|
isSystemAccount: "This account is created and automatically operated by the system.
|
||||||
Please do not moderate, edit, delete, or otherwise tamper with this account, or
|
Please do not moderate, edit, delete, or otherwise tamper with this account, or
|
||||||
it may break your server."
|
it may break your server."
|
||||||
|
@ -1011,7 +1011,7 @@ document: "Documentation"
|
||||||
numberOfPageCache: "Number of cached pages"
|
numberOfPageCache: "Number of cached pages"
|
||||||
numberOfPageCacheDescription: "Increasing this number will improve convenience for
|
numberOfPageCacheDescription: "Increasing this number will improve convenience for
|
||||||
users but cause more server load as well as more memory to be used."
|
users but cause more server load as well as more memory to be used."
|
||||||
logoutConfirm: "Really log out?"
|
logoutConfirm: "Really sign out?"
|
||||||
lastActiveDate: "Last used at"
|
lastActiveDate: "Last used at"
|
||||||
statusbar: "Status bar"
|
statusbar: "Status bar"
|
||||||
pleaseSelect: "Select an option"
|
pleaseSelect: "Select an option"
|
||||||
|
@ -1112,6 +1112,9 @@ signupsDisabled: "Signups on this server are currently disabled. If you have an
|
||||||
code for this server, please enter it below."
|
code for this server, please enter it below."
|
||||||
apps: "Apps"
|
apps: "Apps"
|
||||||
sendModMail: "Send Moderation Notice"
|
sendModMail: "Send Moderation Notice"
|
||||||
|
moderationNote: "Moderation Note"
|
||||||
|
ipFirstAcknowledged: "The date of the first acquisition of the IP address"
|
||||||
|
driveCapacityOverride: "Drive Capacity Override"
|
||||||
preventAiLearning: "Prevent AI bot scraping"
|
preventAiLearning: "Prevent AI bot scraping"
|
||||||
preventAiLearningDescription: "Request third-party AI language models not to study
|
preventAiLearningDescription: "Request third-party AI language models not to study
|
||||||
content you upload, such as posts and images."
|
content you upload, such as posts and images."
|
||||||
|
@ -1195,7 +1198,9 @@ searchWordsDescription: "To search for posts, enter the search term. Separate wo
|
||||||
search.\nFor example, 'morning night' will find posts that contain both 'morning'
|
search.\nFor example, 'morning night' will find posts that contain both 'morning'
|
||||||
and 'night', and 'morning OR night' will find posts that contain either 'morning'
|
and 'night', and 'morning OR night' will find posts that contain either 'morning'
|
||||||
or 'night' (or both).\nYou can also combine AND/OR conditions like '(morning OR
|
or 'night' (or both).\nYou can also combine AND/OR conditions like '(morning OR
|
||||||
night) sleepy'.\n\nIf you want to go to a specific user page or post page, enter
|
night) sleepy'.\nIf you want to search for a sequence of words (e.g., a sentence), you
|
||||||
|
must put it in double quotes, not to make it an AND search: \"Today I learned\"\n\n
|
||||||
|
If you want to go to a specific user page or post page, enter
|
||||||
the ID or URL in this field and click the 'Lookup' button. Clicking 'Search' will
|
the ID or URL in this field and click the 'Lookup' button. Clicking 'Search' will
|
||||||
search for posts that literally contain the ID/URL."
|
search for posts that literally contain the ID/URL."
|
||||||
searchUsers: "Posted by (optional)"
|
searchUsers: "Posted by (optional)"
|
||||||
|
@ -1214,7 +1219,7 @@ searchRangeDescription: "If you want to filter the time period, enter it in this
|
||||||
searchPostsWithFiles: "Only posts with files"
|
searchPostsWithFiles: "Only posts with files"
|
||||||
searchCwAndAlt: "Include content warnings and file descriptions"
|
searchCwAndAlt: "Include content warnings and file descriptions"
|
||||||
publishTimelines: "Publish timelines for visitors"
|
publishTimelines: "Publish timelines for visitors"
|
||||||
publishTimelinesDescription: "If enabled, the Local and Global timeline will be shown
|
publishTimelinesDescription: "If enabled, the Local and Global timelines will be shown
|
||||||
on {url} even when signed out."
|
on {url} even when signed out."
|
||||||
noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?"
|
noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?"
|
||||||
showNoAltTextWarning: "Show a warning if you attempt to post files without a description"
|
showNoAltTextWarning: "Show a warning if you attempt to post files without a description"
|
||||||
|
@ -1627,7 +1632,7 @@ _2fa:
|
||||||
step2Url: "You can also enter this URL if you're using a desktop program:"
|
step2Url: "You can also enter this URL if you're using a desktop program:"
|
||||||
step3Title: "Enter an authentication code"
|
step3Title: "Enter an authentication code"
|
||||||
step3: "Enter the token provided by your app to finish setup."
|
step3: "Enter the token provided by your app to finish setup."
|
||||||
step4: "From now on, any future login attempts will ask for such a login token."
|
step4: "From now on, any future sign in attempts will ask for such a token."
|
||||||
securityKeyNotSupported: "Your browser does not support security keys."
|
securityKeyNotSupported: "Your browser does not support security keys."
|
||||||
securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup
|
securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup
|
||||||
authentication via hardware security keys that support FIDO2 to further secure
|
authentication via hardware security keys that support FIDO2 to further secure
|
||||||
|
@ -2220,3 +2225,5 @@ _iconSets:
|
||||||
moreUrls: "Pinned pages"
|
moreUrls: "Pinned pages"
|
||||||
moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower
|
moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower
|
||||||
left corner using this notation:\n\"Display name\": https://example.com/"
|
left corner using this notation:\n\"Display name\": https://example.com/"
|
||||||
|
messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't share
|
||||||
|
any sensitive infomation over Firefish."
|
||||||
|
|
|
@ -666,7 +666,7 @@ useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votr
|
||||||
other: "Autre"
|
other: "Autre"
|
||||||
regenerateLoginToken: "Régénérer le jeton de connexion"
|
regenerateLoginToken: "Régénérer le jeton de connexion"
|
||||||
regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette
|
regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette
|
||||||
opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton,
|
opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton,
|
||||||
tous les appareils seront déconnectés."
|
tous les appareils seront déconnectés."
|
||||||
setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant
|
setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant
|
||||||
par des espaces."
|
par des espaces."
|
||||||
|
@ -2038,7 +2038,7 @@ noInstances: Il n'y a aucun serveur
|
||||||
showLocalPosts: 'Montrer les notes locales dans :'
|
showLocalPosts: 'Montrer les notes locales dans :'
|
||||||
homeTimeline: Timeline d'Accueil
|
homeTimeline: Timeline d'Accueil
|
||||||
socialTimeline: Timeline Sociale
|
socialTimeline: Timeline Sociale
|
||||||
requireAdminForView: Vous avez besoin d'un compte d'administration pour voir cela.
|
requireAdminForView: Vous avez besoin d'un compte d'administration pour voir ceci.
|
||||||
isSystemAccount: Ce compte est créé et géré automatiquement par le système. Veuillez
|
isSystemAccount: Ce compte est créé et géré automatiquement par le système. Veuillez
|
||||||
ne pas modérer, éditer, supprimer ou altérer d'une autre manière ce compte, ou cela
|
ne pas modérer, éditer, supprimer ou altérer d'une autre manière ce compte, ou cela
|
||||||
risque de perturber votre serveur.
|
risque de perturber votre serveur.
|
||||||
|
@ -2091,9 +2091,8 @@ _experiments:
|
||||||
peut entraîner des ralentissements lors du chargement si votre file d'attente
|
peut entraîner des ralentissements lors du chargement si votre file d'attente
|
||||||
est congestionnée.
|
est congestionnée.
|
||||||
userSaysSomethingReasonQuote: '{name} a cité une publication contenant {reason}'
|
userSaysSomethingReasonQuote: '{name} a cité une publication contenant {reason}'
|
||||||
signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés, mais
|
signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés. Si
|
||||||
vous pouvez toujours vous inscrire sur un autre serveur ! Si vous avez un code d'invitation
|
vous avez un code d'invitation pour ce serveur, saisissez-le ci-dessous.
|
||||||
pour ce serveur, entrez-le ci-dessous s'il vous plait.
|
|
||||||
apps: Applications
|
apps: Applications
|
||||||
userSaysSomethingReasonReply: '{noms} a répondu à une publication contenant {raison}'
|
userSaysSomethingReasonReply: '{noms} a répondu à une publication contenant {raison}'
|
||||||
defaultValueIs: 'défaut : {valeur}'
|
defaultValueIs: 'défaut : {valeur}'
|
||||||
|
@ -2300,3 +2299,16 @@ searchRangeDescription: "Si vous voulez filtrer par période de temps, saisissez
|
||||||
résultats de recherche pour afficher seulement les messages effectués avant le 2
|
résultats de recherche pour afficher seulement les messages effectués avant le 2
|
||||||
janvier de cette année, et 20231026- filtrera les résultats pour afficher seulement
|
janvier de cette année, et 20231026- filtrera les résultats pour afficher seulement
|
||||||
les messages effectués après le 26 octobre 2023."
|
les messages effectués après le 26 octobre 2023."
|
||||||
|
toReply: Répondre
|
||||||
|
toPost: Publier
|
||||||
|
toQuote: Citer
|
||||||
|
toEdit: Modifier
|
||||||
|
messagingUnencryptedInfo: Les conversations sur Firefish ne sont pas cryptées. Ne
|
||||||
|
partagez aucune information sensible sur Firefish.
|
||||||
|
moderationNote: Note de modération
|
||||||
|
driveCapacityOverride: Limite de stockage personalisée
|
||||||
|
ipFirstAcknowledged: La date de la première acquisition de l'adresse IP
|
||||||
|
noAltTextWarning: Certains fichiers joints n'ont aucune description. Avez-vous oublié
|
||||||
|
de l'écrire ?
|
||||||
|
showNoAltTextWarning: Afficher un avertissement si vous essayez de publier des fichiers
|
||||||
|
sans description
|
||||||
|
|
|
@ -415,7 +415,7 @@ securityKeyName: "Nama kunci"
|
||||||
registerSecurityKey: "Daftarkan kunci keamanan"
|
registerSecurityKey: "Daftarkan kunci keamanan"
|
||||||
lastUsed: "Terakhir digunakan"
|
lastUsed: "Terakhir digunakan"
|
||||||
unregister: "Batalkan pendaftaran"
|
unregister: "Batalkan pendaftaran"
|
||||||
passwordLessLogin: "Setel login tanpa kata sandi"
|
passwordLessLogin: "Masuk tanpa kata sandi"
|
||||||
resetPassword: "Atur ulang kata sandi"
|
resetPassword: "Atur ulang kata sandi"
|
||||||
newPasswordIs: "Kata sandi baru adalah \"{password}\""
|
newPasswordIs: "Kata sandi baru adalah \"{password}\""
|
||||||
reduceUiAnimation: "Kurangi animasi antarmuka"
|
reduceUiAnimation: "Kurangi animasi antarmuka"
|
||||||
|
@ -655,10 +655,10 @@ useGlobalSetting: "Gunakan setelan global"
|
||||||
useGlobalSettingDesc: "Jika dinyalakan, setelan pemberitahuan akun kamu akan digunakan.
|
useGlobalSettingDesc: "Jika dinyalakan, setelan pemberitahuan akun kamu akan digunakan.
|
||||||
Jika dimatikan, konfigurasi secara individu dapat dibuat."
|
Jika dimatikan, konfigurasi secara individu dapat dibuat."
|
||||||
other: "Lainnya"
|
other: "Lainnya"
|
||||||
regenerateLoginToken: "Perbarui token login"
|
regenerateLoginToken: "Perbarui token masuk"
|
||||||
regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat
|
regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat
|
||||||
login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan
|
masuk ke akun. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat
|
||||||
dilogout."
|
akan dikeluarkan dari akun."
|
||||||
setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya
|
setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya
|
||||||
menggunakan spasi."
|
menggunakan spasi."
|
||||||
fileIdOrUrl: "File-ID atau URL"
|
fileIdOrUrl: "File-ID atau URL"
|
||||||
|
@ -1296,8 +1296,8 @@ _2fa:
|
||||||
step2Url: "Di aplikasi desktop, masukkan URL berikut:"
|
step2Url: "Di aplikasi desktop, masukkan URL berikut:"
|
||||||
step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan
|
step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan
|
||||||
pemasangan."
|
pemasangan."
|
||||||
step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi
|
step4: "Mulai sekarang, upaya pemasukan akun apa pun akan meminta token masuk dari
|
||||||
otentikasi kamu."
|
aplikasi autentikasi kamu."
|
||||||
securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses
|
securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses
|
||||||
login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung
|
login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung
|
||||||
FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu."
|
FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu."
|
||||||
|
@ -1988,9 +1988,8 @@ moveAccountDescription: Proses ini permanen. Pastikan kamu sudah mengatur alias
|
||||||
akun ini ke akun barumu sebelum pindah. Silakan masukkan tag akun dengan format
|
akun ini ke akun barumu sebelum pindah. Silakan masukkan tag akun dengan format
|
||||||
seperti @orang@server.com
|
seperti @orang@server.com
|
||||||
sendModMail: Kirim Pemberitahuan Moderasi
|
sendModMail: Kirim Pemberitahuan Moderasi
|
||||||
signupsDisabled: Pendaftaran ke server ini nonaktif, tapi kamu dapat selalu mendaftar
|
signupsDisabled: Pendaftaran ke server ini nonaktifkam. Jika kamu memiliki kode undangan
|
||||||
ke server lain! Jika kamu memiliki kode undangan server ini, harap masukkan di bawah
|
server ini, harap masukkan di bawah ini.
|
||||||
ini.
|
|
||||||
enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus
|
enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus
|
||||||
isBot: Akun ini akun otomatis
|
isBot: Akun ini akun otomatis
|
||||||
customMOTD: MOTD khusus (pesan layar percik)
|
customMOTD: MOTD khusus (pesan layar percik)
|
||||||
|
@ -2185,7 +2184,7 @@ emojiModPerm: Perizinan pengelolaan emoji kustom
|
||||||
emojiModPermDescription: "Tambah: Perbolehkan pengguna ini untuk menambahkan emoji
|
emojiModPermDescription: "Tambah: Perbolehkan pengguna ini untuk menambahkan emoji
|
||||||
kustom baru dan menetapkan tag/kategori/lisensi untuk semua emoji kustom yang telah
|
kustom baru dan menetapkan tag/kategori/lisensi untuk semua emoji kustom yang telah
|
||||||
ditambahkan.\nTambah dan Sunting: Perizinan \"Tambah\" + Perbolehkan pengguna ini
|
ditambahkan.\nTambah dan Sunting: Perizinan \"Tambah\" + Perbolehkan pengguna ini
|
||||||
untuk menyunting nama/kategori/tag/lisensi emoji kustom yang sudah ada.\nPerbolehkan
|
untuk menyunting nama/kategori/tag/lisensi emoji kustom yang sudah ada.\n Perbolehkan
|
||||||
Semua: Perizinan \"Tambah dan Sunting\" + Perbolehkan pengguna ini untuk menghapus
|
Semua: Perizinan \"Tambah dan Sunting\" + Perbolehkan pengguna ini untuk menghapus
|
||||||
semua emoji kustom yang sudah ada."
|
semua emoji kustom yang sudah ada."
|
||||||
private: Privat
|
private: Privat
|
||||||
|
@ -2241,7 +2240,7 @@ reloading: Memuat ulang
|
||||||
replyMute: Bisukan balasan dalam lini masa
|
replyMute: Bisukan balasan dalam lini masa
|
||||||
searchRange: Dikirim dalam (opsional)
|
searchRange: Dikirim dalam (opsional)
|
||||||
searchUsersDescription: "Untuk mencari kiriman oleh pengguna/server tertentu, masukkan
|
searchUsersDescription: "Untuk mencari kiriman oleh pengguna/server tertentu, masukkan
|
||||||
ID (@pengguna@contoh.id, atau @pengguna untuk pengguna lokal) atau nama domain (contoh.id).\n
|
ID (@pengguna@contoh.id, atau @pengguna untuk pengguna lokal) atau nama domain (contoh.id)\n
|
||||||
\nJika kamu memasukkan 'me' ('aku', tanpa tanda kutip), semua kirimanmu (termasuk
|
\nJika kamu memasukkan 'me' ('aku', tanpa tanda kutip), semua kirimanmu (termasuk
|
||||||
kiriman yang tidak terdaftar, khusus pengikut, langsung, dan rahasia) akan dicari.\n
|
kiriman yang tidak terdaftar, khusus pengikut, langsung, dan rahasia) akan dicari.\n
|
||||||
\nJika Anda memasukkan 'local' (tanpa tanda kutip), hasilnya akan disaring untuk
|
\nJika Anda memasukkan 'local' (tanpa tanda kutip), hasilnya akan disaring untuk
|
||||||
|
@ -2253,3 +2252,16 @@ searchRangeDescription: "Jika kamu ingin memfilter periode waktu, masukkan dalam
|
||||||
pencarian untuk menampilkan hanya kiriman yang dibuat sebelum tanggal 2 Januari
|
pencarian untuk menampilkan hanya kiriman yang dibuat sebelum tanggal 2 Januari
|
||||||
tahun ini, dan 20231026- akan memfilter hasil pencarian untuk menampilkan hanya
|
tahun ini, dan 20231026- akan memfilter hasil pencarian untuk menampilkan hanya
|
||||||
kiriman yang dibuat setelah tanggal 26 Oktober 2023."
|
kiriman yang dibuat setelah tanggal 26 Oktober 2023."
|
||||||
|
toPost: Kirim
|
||||||
|
toQuote: Kutip
|
||||||
|
noAltTextWarning: Beberapa berkas yang dilampirkan tidak memiliki deskripsi. Lupa
|
||||||
|
menulis deskripsinya?
|
||||||
|
toEdit: Sunting
|
||||||
|
showNoAltTextWarning: Tampilkan peringatan jika kamu mencoba mengirim berkas tanpa
|
||||||
|
deskripsi
|
||||||
|
toReply: Balas
|
||||||
|
messagingUnencryptedInfo: Percakapan di Firefish tidak terenkripsi secara ujung ke
|
||||||
|
ujung. Jangan bagikan informasi sensitif apa pun melalui Firefish.
|
||||||
|
moderationNote: Catatan Moderasi
|
||||||
|
driveCapacityOverride: Penimpaan Kapasitas Drive
|
||||||
|
ipFirstAcknowledged: Tanggal akuisisi pertama dari alamat IP
|
||||||
|
|
|
@ -107,7 +107,7 @@ cantRenote: "この投稿はブーストできません。"
|
||||||
cantReRenote: "ブーストをブーストすることはできません。"
|
cantReRenote: "ブーストをブーストすることはできません。"
|
||||||
quote: "引用"
|
quote: "引用"
|
||||||
quotes: "引用"
|
quotes: "引用"
|
||||||
toQuote: "引用"
|
toQuote: "引用する"
|
||||||
pinnedNote: "ピン留めされた投稿"
|
pinnedNote: "ピン留めされた投稿"
|
||||||
pinned: "ピン留め"
|
pinned: "ピン留め"
|
||||||
you: "あなた"
|
you: "あなた"
|
||||||
|
@ -416,7 +416,7 @@ securityKeyName: "キーの名前"
|
||||||
registerSecurityKey: "セキュリティキーを登録する"
|
registerSecurityKey: "セキュリティキーを登録する"
|
||||||
lastUsed: "最後の使用"
|
lastUsed: "最後の使用"
|
||||||
unregister: "登録を解除"
|
unregister: "登録を解除"
|
||||||
passwordLessLogin: "パスワード無しでログイン"
|
passwordLessLogin: "パスワード無しでサインイン"
|
||||||
resetPassword: "パスワードをリセット"
|
resetPassword: "パスワードをリセット"
|
||||||
newPasswordIs: "新しいパスワードは「{password}」です"
|
newPasswordIs: "新しいパスワードは「{password}」です"
|
||||||
reduceUiAnimation: "UIのアニメーションを減らす"
|
reduceUiAnimation: "UIのアニメーションを減らす"
|
||||||
|
@ -481,7 +481,7 @@ disableDrawer: "メニューをドロワーで表示しない"
|
||||||
youHaveNoGroups: "グループがありません"
|
youHaveNoGroups: "グループがありません"
|
||||||
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
|
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
|
||||||
noHistory: "履歴はありません"
|
noHistory: "履歴はありません"
|
||||||
signinHistory: "ログイン履歴"
|
signinHistory: "サインイン履歴"
|
||||||
disableAnimatedMfm: "動きのあるMFMを無効にする"
|
disableAnimatedMfm: "動きのあるMFMを無効にする"
|
||||||
doing: "やっています"
|
doing: "やっています"
|
||||||
category: "カテゴリ"
|
category: "カテゴリ"
|
||||||
|
@ -648,8 +648,8 @@ notificationSettingDesc: "表示する通知の種別を選択してください
|
||||||
useGlobalSetting: "グローバル設定を使う"
|
useGlobalSetting: "グローバル設定を使う"
|
||||||
useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使用されます。オフにすると、個別に設定できるようになります。"
|
useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使用されます。オフにすると、個別に設定できるようになります。"
|
||||||
other: "その他"
|
other: "その他"
|
||||||
regenerateLoginToken: "ログイントークンを再生成"
|
regenerateLoginToken: "サインイントークンを再生成"
|
||||||
regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。"
|
regenerateLoginTokenDescription: "サインインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスからサインアウトされます。"
|
||||||
setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
|
setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
|
||||||
fileIdOrUrl: "ファイルIDまたはURL"
|
fileIdOrUrl: "ファイルIDまたはURL"
|
||||||
behavior: "動作"
|
behavior: "動作"
|
||||||
|
@ -908,14 +908,14 @@ thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
|
||||||
check: "チェック"
|
check: "チェック"
|
||||||
driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更"
|
driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更"
|
||||||
driveCapOverrideCaption: "0以下を指定すると解除されます。"
|
driveCapOverrideCaption: "0以下を指定すると解除されます。"
|
||||||
requireAdminForView: "閲覧するには管理者アカウントでログインしている必要があります。"
|
requireAdminForView: "閲覧するには管理者アカウントでサインインしている必要があります。"
|
||||||
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。モデレーション・編集・削除を行うとサーバーの動作が不正になる可能性があるため、操作しないでください。"
|
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。モデレーション・編集・削除を行うとサーバーの動作が不正になる可能性があるため、操作しないでください。"
|
||||||
typeToConfirm: "この操作を行うには {x} と入力してください"
|
typeToConfirm: "この操作を行うには {x} と入力してください"
|
||||||
deleteAccount: "アカウント削除"
|
deleteAccount: "アカウント削除"
|
||||||
document: "ドキュメント"
|
document: "ドキュメント"
|
||||||
numberOfPageCache: "ページキャッシュ数"
|
numberOfPageCache: "ページキャッシュ数"
|
||||||
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
|
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
|
||||||
logoutConfirm: "ログアウトしますか?"
|
logoutConfirm: "サインアウトしますか?"
|
||||||
lastActiveDate: "最終利用日時"
|
lastActiveDate: "最終利用日時"
|
||||||
statusbar: "ステータスバー"
|
statusbar: "ステータスバー"
|
||||||
pleaseSelect: "選択してください"
|
pleaseSelect: "選択してください"
|
||||||
|
@ -1008,7 +1008,8 @@ enableTimelineStreaming: "タイムラインを自動で更新する"
|
||||||
searchWords: "検索語句・照会するIDやURL"
|
searchWords: "検索語句・照会するIDやURL"
|
||||||
searchWordsDescription: "投稿を検索するには、ここに検索語句を入力してください。空白区切りでAND検索になり、ORを挟むとOR検索になります。\n
|
searchWordsDescription: "投稿を検索するには、ここに検索語句を入力してください。空白区切りでAND検索になり、ORを挟むとOR検索になります。\n
|
||||||
例えば「朝 夜」と入力すると「朝」と「夜」が両方含まれた投稿を検索し、「朝 OR 夜」と入力すると「朝」または「夜」(または両方)が含まれた投稿を検索します。\n
|
例えば「朝 夜」と入力すると「朝」と「夜」が両方含まれた投稿を検索し、「朝 OR 夜」と入力すると「朝」または「夜」(または両方)が含まれた投稿を検索します。\n
|
||||||
「(朝 OR 夜) 眠い」のように、AND検索とOR検索を同時に行うこともできます。\n\n特定のユーザーや投稿のページに飛びたい場合には、この欄にID (@user@example.com)
|
「(朝 OR 夜) 眠い」のように、AND検索とOR検索を同時に行うこともできます。\n空白を含む文字列をAND検索ではなくそのまま検索したい場合、\"明日 買うもの\"\
|
||||||
|
\ のように二重引用符 (\") で囲む必要があります。\n\n特定のユーザーや投稿のページに飛びたい場合には、この欄にID (@user@example.com)
|
||||||
や投稿のURLを入力し「照会」を押してください。「検索」を押すとそのIDやURLが文字通り含まれる投稿を検索します。"
|
や投稿のURLを入力し「照会」を押してください。「検索」を押すとそのIDやURLが文字通り含まれる投稿を検索します。"
|
||||||
searchUsers: "投稿元(オプション)"
|
searchUsers: "投稿元(オプション)"
|
||||||
searchUsersDescription: "投稿検索で投稿者を絞りたい場合、@user@example.com(ローカルユーザーなら @user)の形式で投稿者のIDを入力してください。ユーザーIDではなくドメイン名
|
searchUsersDescription: "投稿検索で投稿者を絞りたい場合、@user@example.com(ローカルユーザーなら @user)の形式で投稿者のIDを入力してください。ユーザーIDではなくドメイン名
|
||||||
|
@ -2051,3 +2052,7 @@ makePrivate: "秘密にする"
|
||||||
makePrivateConfirm: "リモートサーバーに削除リクエストを送信し、投稿の公開範囲を「秘密」にして他の人から見られないようにします。実行しますか?"
|
makePrivateConfirm: "リモートサーバーに削除リクエストを送信し、投稿の公開範囲を「秘密」にして他の人から見られないようにします。実行しますか?"
|
||||||
sentFollowRequests: 未承認のフォローリクエスト
|
sentFollowRequests: 未承認のフォローリクエスト
|
||||||
noSentFollowRequests: 未承認のフォローリクエストはありません
|
noSentFollowRequests: 未承認のフォローリクエストはありません
|
||||||
|
messagingUnencryptedInfo: FirefishのチャットはE2E暗号化されていません。漏洩してはいけない情報はFirefishで送らないでください。
|
||||||
|
moderationNote: モデレーション用のメモ
|
||||||
|
ipFirstAcknowledged: IPアドレスが最初に取得された日
|
||||||
|
driveCapacityOverride: ドライブ容量の変更
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
---
|
|
||||||
_lang_: "Română"
|
_lang_: "Română"
|
||||||
headlineFirefish: "O rețea conectată prin note"
|
headlineFirefish: "O rețea conectată prin note"
|
||||||
introFirefish: "Bine ai venit! Firefish este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀"
|
introFirefish: "Bine ai venit! Firefish este un serviciu de microblogging open source
|
||||||
|
și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine
|
||||||
|
din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui.
|
||||||
|
👍\nHai să explorăm o lume nouă! 🚀"
|
||||||
monthAndDay: "{day}/{month}"
|
monthAndDay: "{day}/{month}"
|
||||||
search: "Caută"
|
search: "Caută"
|
||||||
notifications: "Notificări"
|
notifications: "Notificări"
|
||||||
|
@ -44,7 +46,8 @@ copyContent: "Copiază conținutul"
|
||||||
copyLink: "Copiază link-ul"
|
copyLink: "Copiază link-ul"
|
||||||
delete: "Şterge"
|
delete: "Şterge"
|
||||||
deleteAndEdit: "Șterge și editează"
|
deleteAndEdit: "Șterge și editează"
|
||||||
deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi? Vei pierde reacțiile, re-notele și răspunsurile acesteia."
|
deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi?
|
||||||
|
Vei pierde reacțiile, re-notele și răspunsurile acesteia."
|
||||||
addToList: "Adaugă în listă"
|
addToList: "Adaugă în listă"
|
||||||
sendMessage: "Trimite un mesaj"
|
sendMessage: "Trimite un mesaj"
|
||||||
copyUsername: "Copiază numele de utilizator"
|
copyUsername: "Copiază numele de utilizator"
|
||||||
|
@ -64,9 +67,11 @@ import: "Importă"
|
||||||
export: "Exportă"
|
export: "Exportă"
|
||||||
files: "Fișiere"
|
files: "Fișiere"
|
||||||
download: "Descarcă"
|
download: "Descarcă"
|
||||||
driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele atașate fișierului vor fi șterse și ele."
|
driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele
|
||||||
|
atașate fișierului vor fi șterse și ele."
|
||||||
unfollowConfirm: "Ești sigur ca vrei să nu mai urmărești pe {name}?"
|
unfollowConfirm: "Ești sigur ca vrei să nu mai urmărești pe {name}?"
|
||||||
exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul tău odată completat."
|
exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul
|
||||||
|
tău odată completat."
|
||||||
importRequested: "Ai cerut un import. S-ar putea să ia un pic."
|
importRequested: "Ai cerut un import. S-ar putea să ia un pic."
|
||||||
lists: "Liste"
|
lists: "Liste"
|
||||||
noLists: "Nu ai nici o listă"
|
noLists: "Nu ai nici o listă"
|
||||||
|
@ -81,9 +86,12 @@ error: "Eroare"
|
||||||
somethingHappened: "A survenit o eroare"
|
somethingHappened: "A survenit o eroare"
|
||||||
retry: "Reîncearcă"
|
retry: "Reîncearcă"
|
||||||
pageLoadError: "A apărut o eroare la încărcarea paginii."
|
pageLoadError: "A apărut o eroare la încărcarea paginii."
|
||||||
pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
|
pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul
|
||||||
serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci din nou."
|
browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
|
||||||
youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi clientul."
|
serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci
|
||||||
|
din nou."
|
||||||
|
youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi
|
||||||
|
clientul."
|
||||||
enterListName: "Introdu un nume pentru listă"
|
enterListName: "Introdu un nume pentru listă"
|
||||||
privacy: "Confidenţialitate"
|
privacy: "Confidenţialitate"
|
||||||
makeFollowManuallyApprove: "Fă cererile de urmărire să necesite aprobare"
|
makeFollowManuallyApprove: "Fă cererile de urmărire să necesite aprobare"
|
||||||
|
@ -137,14 +145,21 @@ emojiUrl: "URL-ul emoji-ului"
|
||||||
addEmoji: "Adaugă un emoji"
|
addEmoji: "Adaugă un emoji"
|
||||||
settingGuide: "Setări recomandate"
|
settingGuide: "Setări recomandate"
|
||||||
cacheRemoteFiles: "Ține fișierele externe in cache"
|
cacheRemoteFiles: "Ține fișierele externe in cache"
|
||||||
cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
|
cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe
|
||||||
|
sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului
|
||||||
|
de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
|
||||||
flagAsBot: "Marchează acest cont ca bot"
|
flagAsBot: "Marchează acest cont ca bot"
|
||||||
flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Firefish pentru a trata acest cont drept un bot."
|
flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de
|
||||||
|
un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori
|
||||||
|
pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează
|
||||||
|
sistemele interne al Firefish pentru a trata acest cont drept un bot."
|
||||||
flagAsCat: "Marchează acest cont ca pisică"
|
flagAsCat: "Marchează acest cont ca pisică"
|
||||||
flagAsCatDescription: "Activează această opțiune dacă acest cont este o pisică."
|
flagAsCatDescription: "Activează această opțiune dacă acest cont este o pisică."
|
||||||
flagShowTimelineReplies: "Arată răspunsurile în cronologie"
|
flagShowTimelineReplies: "Arată răspunsurile în cronologie"
|
||||||
flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie răspunsurile utilizatorilor către alte notele altor utilizatori."
|
flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie
|
||||||
autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care îi urmărești"
|
răspunsurile utilizatorilor către alte notele altor utilizatori."
|
||||||
|
autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care
|
||||||
|
îi urmărești"
|
||||||
addAccount: "Adaugă un cont"
|
addAccount: "Adaugă un cont"
|
||||||
loginFailed: "Autentificare eșuată"
|
loginFailed: "Autentificare eșuată"
|
||||||
showOnRemote: "Vezi mai multe pe instanța externă"
|
showOnRemote: "Vezi mai multe pe instanța externă"
|
||||||
|
@ -156,7 +171,11 @@ searchWith: "Caută: {q}"
|
||||||
youHaveNoLists: "Nu ai nici o listă"
|
youHaveNoLists: "Nu ai nici o listă"
|
||||||
followConfirm: "Ești sigur ca vrei să urmărești pe {name}?"
|
followConfirm: "Ești sigur ca vrei să urmărești pe {name}?"
|
||||||
proxyAccount: "Cont proxy"
|
proxyAccount: "Cont proxy"
|
||||||
proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator, așa că în schimb contul proxy îl va urmări."
|
proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor
|
||||||
|
extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva
|
||||||
|
adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va
|
||||||
|
fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator,
|
||||||
|
așa că în schimb contul proxy îl va urmări."
|
||||||
host: "Gazdă"
|
host: "Gazdă"
|
||||||
selectUser: "Selectează un utilizator"
|
selectUser: "Selectează un utilizator"
|
||||||
recipient: "Destinatar"
|
recipient: "Destinatar"
|
||||||
|
@ -186,11 +205,14 @@ instanceInfo: "Informații despre instanță"
|
||||||
statistics: "Statistici"
|
statistics: "Statistici"
|
||||||
clearQueue: "Șterge coada"
|
clearQueue: "Șterge coada"
|
||||||
clearQueueConfirmTitle: "Ești sigur că vrei să cureți coada?"
|
clearQueueConfirmTitle: "Ești sigur că vrei să cureți coada?"
|
||||||
clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această operație nu este necesară."
|
clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această
|
||||||
|
operație nu este necesară."
|
||||||
clearCachedFiles: "Golește cache-ul"
|
clearCachedFiles: "Golește cache-ul"
|
||||||
clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din cache?"
|
clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din
|
||||||
|
cache?"
|
||||||
blockedInstances: "Instanțe blocate"
|
blockedInstances: "Instanțe blocate"
|
||||||
blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
|
blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să
|
||||||
|
le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
|
||||||
muteAndBlock: "Amuțiri și Blocări"
|
muteAndBlock: "Amuțiri și Blocări"
|
||||||
mutedUsers: "Utilizatori amuțiți"
|
mutedUsers: "Utilizatori amuțiți"
|
||||||
blockedUsers: "Utilizatori blocați"
|
blockedUsers: "Utilizatori blocați"
|
||||||
|
@ -238,7 +260,8 @@ saved: "Salvat"
|
||||||
messaging: "Chat"
|
messaging: "Chat"
|
||||||
upload: "Încarcă"
|
upload: "Încarcă"
|
||||||
keepOriginalUploading: "Păstrează imaginea originală"
|
keepOriginalUploading: "Păstrează imaginea originală"
|
||||||
keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări. Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
|
keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări.
|
||||||
|
Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
|
||||||
fromDrive: "Din Drive"
|
fromDrive: "Din Drive"
|
||||||
fromUrl: "Din URL"
|
fromUrl: "Din URL"
|
||||||
uploadFromUrl: "Încarcă dintr-un URL"
|
uploadFromUrl: "Încarcă dintr-un URL"
|
||||||
|
@ -254,7 +277,8 @@ agreeTo: "Sunt de acord cu {0}"
|
||||||
tos: "Termenii de utilizare"
|
tos: "Termenii de utilizare"
|
||||||
start: "Să începem"
|
start: "Să începem"
|
||||||
home: "Acasă"
|
home: "Acasă"
|
||||||
remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația afișată poate fi incompletă."
|
remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația
|
||||||
|
afișată poate fi incompletă."
|
||||||
activity: "Activitate"
|
activity: "Activitate"
|
||||||
images: "Imagini"
|
images: "Imagini"
|
||||||
birthday: "Zi de naștere"
|
birthday: "Zi de naștere"
|
||||||
|
@ -287,7 +311,8 @@ unableToDelete: "Nu se poate șterge"
|
||||||
inputNewFileName: "Introdu un nou nume de fișier"
|
inputNewFileName: "Introdu un nou nume de fișier"
|
||||||
inputNewDescription: "Introdu o descriere nouă"
|
inputNewDescription: "Introdu o descriere nouă"
|
||||||
inputNewFolderName: "Introdu un nume de folder nou"
|
inputNewFolderName: "Introdu un nume de folder nou"
|
||||||
circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe care dorești să îl muți."
|
circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe
|
||||||
|
care dorești să îl muți."
|
||||||
hasChildFilesOrFolders: "Acest folder nu este gol, așa că nu poate fi șters."
|
hasChildFilesOrFolders: "Acest folder nu este gol, așa că nu poate fi șters."
|
||||||
copyUrl: "Copiază URL"
|
copyUrl: "Copiază URL"
|
||||||
rename: "Redenumește"
|
rename: "Redenumește"
|
||||||
|
@ -318,7 +343,8 @@ yearX: "{year}"
|
||||||
pages: "Pagini"
|
pages: "Pagini"
|
||||||
enableLocalTimeline: "Activează cronologia locală"
|
enableLocalTimeline: "Activează cronologia locală"
|
||||||
enableGlobalTimeline: "Activeaza cronologia globală"
|
enableGlobalTimeline: "Activeaza cronologia globală"
|
||||||
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate cronologiile, chiar dacă nu sunt activate."
|
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate
|
||||||
|
cronologiile, chiar dacă nu sunt activate."
|
||||||
registration: "Inregistrare"
|
registration: "Inregistrare"
|
||||||
enableRegistration: "Activează înregistrările pentru utilizatori noi"
|
enableRegistration: "Activează înregistrările pentru utilizatori noi"
|
||||||
invite: "Invită"
|
invite: "Invită"
|
||||||
|
@ -330,9 +356,11 @@ bannerUrl: "URL-ul imaginii de banner"
|
||||||
backgroundImageUrl: "URL-ul imaginii de fundal"
|
backgroundImageUrl: "URL-ul imaginii de fundal"
|
||||||
basicInfo: "Informații de bază"
|
basicInfo: "Informații de bază"
|
||||||
pinnedUsers: "Utilizatori fixați"
|
pinnedUsers: "Utilizatori fixați"
|
||||||
pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor fi fixați pe pagina \"Explorează\"."
|
pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor
|
||||||
|
fi fixați pe pagina \"Explorează\"."
|
||||||
pinnedPages: "Pagini fixate"
|
pinnedPages: "Pagini fixate"
|
||||||
pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful paginii acestei instanțe, separate de pauze de rând."
|
pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful
|
||||||
|
paginii acestei instanțe, separate de pauze de rând."
|
||||||
pinnedClipId: "ID-ul clip-ului pe care să îl fixezi"
|
pinnedClipId: "ID-ul clip-ului pe care să îl fixezi"
|
||||||
pinnedNotes: "Notă fixată"
|
pinnedNotes: "Notă fixată"
|
||||||
hcaptcha: "hCaptcha"
|
hcaptcha: "hCaptcha"
|
||||||
|
@ -343,14 +371,17 @@ recaptcha: "reCAPTCHA"
|
||||||
enableRecaptcha: "Activează reCAPTCHA"
|
enableRecaptcha: "Activează reCAPTCHA"
|
||||||
recaptchaSiteKey: "Site key"
|
recaptchaSiteKey: "Site key"
|
||||||
recaptchaSecretKey: "Secret key"
|
recaptchaSecretKey: "Secret key"
|
||||||
avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi să rămână activate, apasă Anulare."
|
avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență
|
||||||
|
între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi
|
||||||
|
să rămână activate, apasă Anulare."
|
||||||
antennas: "Antene"
|
antennas: "Antene"
|
||||||
manageAntennas: "Gestionează Antenele"
|
manageAntennas: "Gestionează Antenele"
|
||||||
name: "Nume"
|
name: "Nume"
|
||||||
antennaSource: "Sursa antenei"
|
antennaSource: "Sursa antenei"
|
||||||
antennaKeywords: "Cuvinte cheie ascultate"
|
antennaKeywords: "Cuvinte cheie ascultate"
|
||||||
antennaExcludeKeywords: "Cuvinte cheie excluse"
|
antennaExcludeKeywords: "Cuvinte cheie excluse"
|
||||||
antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere de rând pentru o condiție SAU."
|
antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere
|
||||||
|
de rând pentru o condiție SAU."
|
||||||
notifyAntenna: "Notifică-mă pentru note noi"
|
notifyAntenna: "Notifică-mă pentru note noi"
|
||||||
withFileAntenna: "Doar note cu fișiere"
|
withFileAntenna: "Doar note cu fișiere"
|
||||||
enableServiceworker: "Activează ServiceWorker"
|
enableServiceworker: "Activează ServiceWorker"
|
||||||
|
@ -437,7 +468,8 @@ strongPassword: "Parolă puternică"
|
||||||
passwordMatched: "Se potrivește!"
|
passwordMatched: "Se potrivește!"
|
||||||
passwordNotMatched: "Nu se potrivește"
|
passwordNotMatched: "Nu se potrivește"
|
||||||
signinWith: "Autentifică-te cu {x}"
|
signinWith: "Autentifică-te cu {x}"
|
||||||
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse sunt incorecte."
|
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse
|
||||||
|
sunt incorecte."
|
||||||
tapSecurityKey: "Apasă pe cheia ta de securitate."
|
tapSecurityKey: "Apasă pe cheia ta de securitate."
|
||||||
or: "Sau"
|
or: "Sau"
|
||||||
language: "Limbă"
|
language: "Limbă"
|
||||||
|
@ -478,19 +510,26 @@ showFeaturedNotesInTimeline: "Arată notele recomandate în cronologii"
|
||||||
objectStorage: "Object Storage"
|
objectStorage: "Object Storage"
|
||||||
useObjectStorage: "Folosește Object Storage"
|
useObjectStorage: "Folosește Object Storage"
|
||||||
objectStorageBaseUrl: "URL de bază"
|
objectStorageBaseUrl: "URL de bază"
|
||||||
objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com' și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>', etc."
|
objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul
|
||||||
|
CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com'
|
||||||
|
și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>',
|
||||||
|
etc."
|
||||||
objectStorageBucket: "Bucket"
|
objectStorageBucket: "Bucket"
|
||||||
objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
|
objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
|
||||||
objectStoragePrefix: "Prefix"
|
objectStoragePrefix: "Prefix"
|
||||||
objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
|
objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
|
||||||
objectStorageEndpoint: "Endpoint"
|
objectStorageEndpoint: "Endpoint"
|
||||||
objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
|
objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică
|
||||||
|
endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
|
||||||
objectStorageRegion: "Regiune"
|
objectStorageRegion: "Regiune"
|
||||||
objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
|
objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău
|
||||||
|
nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
|
||||||
objectStorageUseSSL: "Folosește SSl"
|
objectStorageUseSSL: "Folosește SSl"
|
||||||
objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru conexiunile API-ului"
|
objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru
|
||||||
|
conexiunile API-ului"
|
||||||
objectStorageUseProxy: "Conectează-te prin Proxy"
|
objectStorageUseProxy: "Conectează-te prin Proxy"
|
||||||
objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru conexiunile API-ului"
|
objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru
|
||||||
|
conexiunile API-ului"
|
||||||
objectStorageSetPublicRead: "Setează \"public-read\" pentru încărcare"
|
objectStorageSetPublicRead: "Setează \"public-read\" pentru încărcare"
|
||||||
serverLogs: "Loguri server"
|
serverLogs: "Loguri server"
|
||||||
deleteAll: "Șterge tot"
|
deleteAll: "Șterge tot"
|
||||||
|
@ -518,7 +557,9 @@ sort: "Sortează"
|
||||||
ascendingOrder: "Crescător"
|
ascendingOrder: "Crescător"
|
||||||
descendingOrder: "Descrescător"
|
descendingOrder: "Descrescător"
|
||||||
scratchpad: "Scratchpad"
|
scratchpad: "Scratchpad"
|
||||||
scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript. Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish în el."
|
scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript.
|
||||||
|
Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish
|
||||||
|
în el."
|
||||||
output: "Ieșire"
|
output: "Ieșire"
|
||||||
script: "Script"
|
script: "Script"
|
||||||
disablePagesScript: "Dezactivează AiScript în Pagini"
|
disablePagesScript: "Dezactivează AiScript în Pagini"
|
||||||
|
@ -526,11 +567,14 @@ updateRemoteUser: "Actualizează informațiile utilizatorului extern"
|
||||||
deleteAllFiles: "Șterge toate fișierele"
|
deleteAllFiles: "Șterge toate fișierele"
|
||||||
deleteAllFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele?"
|
deleteAllFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele?"
|
||||||
removeAllFollowing: "Dezurmărește toți utilizatorii urmăriți"
|
removeAllFollowing: "Dezurmărește toți utilizatorii urmăriți"
|
||||||
removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te rog execută asta numai dacă instanța, de ex., nu mai există."
|
removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te
|
||||||
|
rog execută asta numai dacă instanța, de ex., nu mai există."
|
||||||
userSuspended: "Acest utilizator a fost suspendat."
|
userSuspended: "Acest utilizator a fost suspendat."
|
||||||
userSilenced: "Acest utilizator a fost setat silențios."
|
userSilenced: "Acest utilizator a fost setat silențios."
|
||||||
yourAccountSuspendedTitle: "Acest cont a fost suspendat"
|
yourAccountSuspendedTitle: "Acest cont a fost suspendat"
|
||||||
yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării termenilor de serviciu al serverului sau ceva similar. Contactează administratorul dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
|
yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării
|
||||||
|
termenilor de serviciu al serverului sau ceva similar. Contactează administratorul
|
||||||
|
dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
|
||||||
menu: "Meniu"
|
menu: "Meniu"
|
||||||
divider: "Separator"
|
divider: "Separator"
|
||||||
addItem: "Adaugă element"
|
addItem: "Adaugă element"
|
||||||
|
@ -569,12 +613,14 @@ permission: "Permisiuni"
|
||||||
enableAll: "Actevează tot"
|
enableAll: "Actevează tot"
|
||||||
disableAll: "Dezactivează tot"
|
disableAll: "Dezactivează tot"
|
||||||
tokenRequested: "Acordă acces la cont"
|
tokenRequested: "Acordă acces la cont"
|
||||||
pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile setate aici."
|
pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile
|
||||||
|
setate aici."
|
||||||
notificationType: "Tipul notificării"
|
notificationType: "Tipul notificării"
|
||||||
edit: "Editează"
|
edit: "Editează"
|
||||||
emailServer: "Server email"
|
emailServer: "Server email"
|
||||||
enableEmail: "Activează distribuția de emailuri"
|
enableEmail: "Activează distribuția de emailuri"
|
||||||
emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți uiți parola"
|
emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți
|
||||||
|
uiți parola"
|
||||||
email: "Email"
|
email: "Email"
|
||||||
emailAddress: "Adresă de email"
|
emailAddress: "Adresă de email"
|
||||||
smtpConfig: "Configurare Server SMTP"
|
smtpConfig: "Configurare Server SMTP"
|
||||||
|
@ -582,13 +628,15 @@ smtpHost: "Gazdă"
|
||||||
smtpPort: "Port"
|
smtpPort: "Port"
|
||||||
smtpUser: "Nume de utilizator"
|
smtpUser: "Nume de utilizator"
|
||||||
smtpPass: "Parolă"
|
smtpPass: "Parolă"
|
||||||
emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva verificarea SMTP"
|
emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva
|
||||||
|
verificarea SMTP"
|
||||||
smtpSecure: "Folosește SSL/TLS implicit pentru conecțiunile SMTP"
|
smtpSecure: "Folosește SSL/TLS implicit pentru conecțiunile SMTP"
|
||||||
smtpSecureInfo: "Oprește opțiunea asta dacă STARTTLS este folosit"
|
smtpSecureInfo: "Oprește opțiunea asta dacă STARTTLS este folosit"
|
||||||
testEmail: "Testează livrarea emailurilor"
|
testEmail: "Testează livrarea emailurilor"
|
||||||
wordMute: "Cuvinte pe mut"
|
wordMute: "Cuvinte pe mut"
|
||||||
regexpError: "Eroare de Expresie Regulată"
|
regexpError: "Eroare de Expresie Regulată"
|
||||||
regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al cuvintelor {tab} setate pe mut:"
|
regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al
|
||||||
|
cuvintelor {tab} setate pe mut:"
|
||||||
instanceMute: "Instanțe pe mut"
|
instanceMute: "Instanțe pe mut"
|
||||||
userSaysSomething: "{name} a spus ceva"
|
userSaysSomething: "{name} a spus ceva"
|
||||||
makeActive: "Activează"
|
makeActive: "Activează"
|
||||||
|
@ -604,10 +652,13 @@ create: "Crează"
|
||||||
notificationSetting: "Setări notificări"
|
notificationSetting: "Setări notificări"
|
||||||
notificationSettingDesc: "Selectează tipurile de notificări care să fie arătate"
|
notificationSettingDesc: "Selectează tipurile de notificări care să fie arătate"
|
||||||
useGlobalSetting: "Folosește setările globale"
|
useGlobalSetting: "Folosește setările globale"
|
||||||
useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite. Dacă e oprită, configurația va fi individuală."
|
useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite.
|
||||||
|
Dacă e oprită, configurația va fi individuală."
|
||||||
other: "Altele"
|
other: "Altele"
|
||||||
regenerateLoginToken: "Regenerează token de login"
|
regenerateLoginToken: "Regenerează token de login"
|
||||||
regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări. În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi delogate."
|
regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări.
|
||||||
|
În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi
|
||||||
|
delogate."
|
||||||
setMultipleBySeparatingWithSpace: "Separă mai multe intrări cu spații."
|
setMultipleBySeparatingWithSpace: "Separă mai multe intrări cu spații."
|
||||||
fileIdOrUrl: "Introdu ID sau URL"
|
fileIdOrUrl: "Introdu ID sau URL"
|
||||||
behavior: "Comportament"
|
behavior: "Comportament"
|
||||||
|
@ -615,13 +666,15 @@ sample: "exemplu"
|
||||||
abuseReports: "Rapoarte"
|
abuseReports: "Rapoarte"
|
||||||
reportAbuse: "Raportează"
|
reportAbuse: "Raportează"
|
||||||
reportAbuseOf: "Raportează {name}"
|
reportAbuseOf: "Raportează {name}"
|
||||||
fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este despre o notă specifică, te rog introdu URL-ul ei."
|
fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este
|
||||||
|
despre o notă specifică, te rog introdu URL-ul ei."
|
||||||
abuseReported: "Raportul tău a fost trimis. Mulțumim."
|
abuseReported: "Raportul tău a fost trimis. Mulțumim."
|
||||||
reporter: "Raportorul"
|
reporter: "Raportorul"
|
||||||
reporteeOrigin: "Originea raportatului"
|
reporteeOrigin: "Originea raportatului"
|
||||||
reporterOrigin: "Originea raportorului"
|
reporterOrigin: "Originea raportorului"
|
||||||
forwardReport: "Redirecționează raportul către instanța externă"
|
forwardReport: "Redirecționează raportul către instanța externă"
|
||||||
forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de sistem, ca raportor către instanța externă."
|
forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de
|
||||||
|
sistem, ca raportor către instanța externă."
|
||||||
send: "Trimite"
|
send: "Trimite"
|
||||||
abuseMarkAsResolved: "Marchează raportul ca rezolvat"
|
abuseMarkAsResolved: "Marchează raportul ca rezolvat"
|
||||||
openInNewTab: "Deschide în tab nou"
|
openInNewTab: "Deschide în tab nou"
|
||||||
|
|
|
@ -873,7 +873,7 @@ recommended: "推荐"
|
||||||
check: "检查"
|
check: "检查"
|
||||||
driveCapOverrideLabel: "修改此用户的网盘容量"
|
driveCapOverrideLabel: "修改此用户的网盘容量"
|
||||||
driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。"
|
driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。"
|
||||||
requireAdminForView: "需要使用管理员账号登录才能查看。"
|
requireAdminForView: "您需要使用管理员账号登录才能查看。"
|
||||||
isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。"
|
isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。"
|
||||||
typeToConfirm: "输入 {x} 以确认操作"
|
typeToConfirm: "输入 {x} 以确认操作"
|
||||||
deleteAccount: "删除账号"
|
deleteAccount: "删除账号"
|
||||||
|
@ -1263,7 +1263,7 @@ _2fa:
|
||||||
step2: "然后,扫描屏幕上显示的二维码。"
|
step2: "然后,扫描屏幕上显示的二维码。"
|
||||||
step2Url: "如果您使用的是桌面程序,您也可以输入这个URL:"
|
step2Url: "如果您使用的是桌面程序,您也可以输入这个URL:"
|
||||||
step3: "输入您的应用提供的令牌以完成设置。"
|
step3: "输入您的应用提供的令牌以完成设置。"
|
||||||
step4: "从现在开始,任何登录操作都将要求您提供这样一个登录令牌。"
|
step4: "从现在开始,任何登录操作都将要求您提供这样一个令牌。"
|
||||||
securityKeyInfo: "除了指纹或 PIN 身份验证外,您还可以通过支持 FIDO2 的硬件安全密钥设置身份验证,以进一步保护您的账号。"
|
securityKeyInfo: "除了指纹或 PIN 身份验证外,您还可以通过支持 FIDO2 的硬件安全密钥设置身份验证,以进一步保护您的账号。"
|
||||||
token: 2FA 令牌
|
token: 2FA 令牌
|
||||||
step3Title: 输入验证码
|
step3Title: 输入验证码
|
||||||
|
@ -1934,7 +1934,10 @@ migrationConfirm: "您确实确定要将账号迁移到 {account} 吗?此操
|
||||||
noteId: 帖子 ID
|
noteId: 帖子 ID
|
||||||
moveFrom: 从旧账号迁移至此账号
|
moveFrom: 从旧账号迁移至此账号
|
||||||
defaultReaction: 发出和收到帖子的默认表情符号反应
|
defaultReaction: 发出和收到帖子的默认表情符号反应
|
||||||
sendModMail: 发送审核通知
|
sendModMail: 发送管理通知
|
||||||
|
moderationNote: "管理笔记"
|
||||||
|
ipFirstAcknowledged: "首次获取此 IP 地址的日期"
|
||||||
|
driveCapacityOverride: "网盘容量变更"
|
||||||
isLocked: 该账号设置了关注请求
|
isLocked: 该账号设置了关注请求
|
||||||
_filters:
|
_filters:
|
||||||
notesBefore: 帖子早于
|
notesBefore: 帖子早于
|
||||||
|
@ -2044,8 +2047,12 @@ publishTimelines: 为访客发布时间线
|
||||||
publishTimelinesDescription: 如果启用,在用户登出时本地和全局时间线也会显示在 {url} 上。
|
publishTimelinesDescription: 如果启用,在用户登出时本地和全局时间线也会显示在 {url} 上。
|
||||||
searchWordsDescription: "要搜索帖子,请输入关键词。交集搜索关键词之间使用空格进行区分,并集搜索关键词之间使用 OR 进行区分。\n例如 '早上
|
searchWordsDescription: "要搜索帖子,请输入关键词。交集搜索关键词之间使用空格进行区分,并集搜索关键词之间使用 OR 进行区分。\n例如 '早上
|
||||||
晚上' 将查找包含 '早上' 和 '晚上' 的帖子,而 '早上 OR 晚上' 将查找包含 '早上' 或 '晚上' (以及同时包含两者)的帖子。\n您还可以组合交集/并集条件,例如
|
晚上' 将查找包含 '早上' 和 '晚上' 的帖子,而 '早上 OR 晚上' 将查找包含 '早上' 或 '晚上' (以及同时包含两者)的帖子。\n您还可以组合交集/并集条件,例如
|
||||||
'(早上 OR 晚上) 困了' 。\n\n如果您想转到特定的用户页面或帖子页面,请在此字段中输入用户 ID 或 URL,然后单击 “查询” 按钮。 单击 “搜索”
|
'(早上 OR 晚上) 困了' 。\n如果您想搜索单词序列(例如一个英语句子),您必须将其放在双引号中,例如 \"Today I learned\" 以区分于交集搜索。\n
|
||||||
将搜索字面包含用户 ID/URL 的帖子。"
|
\n如果您想转到特定的用户页面或帖子页面,请在此字段中输入用户 ID 或 URL,然后单击 “查询” 按钮。 单击 “搜索” 将搜索字面包含用户 ID/URL
|
||||||
|
的帖子。"
|
||||||
searchRangeDescription: "如果您要过滤时间段,请按以下格式输入:20220615-20231031\n\n如果您省略年份(例如 0105-0106
|
searchRangeDescription: "如果您要过滤时间段,请按以下格式输入:20220615-20231031\n\n如果您省略年份(例如 0105-0106
|
||||||
或 20231105-0110),它将被解释为当前年份。\n\n您还可以省略开始日期或结束日期。 例如 -0102 将过滤搜索结果以仅显示今年 1 月 2 日之前发布的帖子,而
|
或 20231105-0110),它将被解释为当前年份。\n\n您还可以省略开始日期或结束日期。 例如 -0102 将过滤搜索结果以仅显示今年 1 月 2 日之前发布的帖子,而
|
||||||
20231026- 将过滤结果以仅显示 2023 年 10 月 26 日之后发布的帖子。"
|
20231026- 将过滤结果以仅显示 2023 年 10 月 26 日之后发布的帖子。"
|
||||||
|
messagingUnencryptedInfo: "Firefish 上的聊天没有经过端到端加密,请不要在聊天中分享您的敏感信息。"
|
||||||
|
noAltTextWarning: 有些附件没有描述。您是否忘记写描述了?
|
||||||
|
showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告
|
||||||
|
|
|
@ -2048,3 +2048,7 @@ searchUsersDescription: "如欲搜尋特定使用者的貼文,請以「@user@e
|
||||||
\n輸入「me」以搜尋自己的所有貼文(包含不在主頁顯示、追隨者、指定使用者、祕密貼文)。\n\n輸入「local」以搜尋本地伺服器的貼文。"
|
\n輸入「me」以搜尋自己的所有貼文(包含不在主頁顯示、追隨者、指定使用者、祕密貼文)。\n\n輸入「local」以搜尋本地伺服器的貼文。"
|
||||||
searchRangeDescription: "如欲搜尋特定期間的貼文,請以「20220615-20231031」的格式輸入日期範圍。\n\n今年的日期可省略年份(例如0105-0106、20231105-0110)。\n\
|
searchRangeDescription: "如欲搜尋特定期間的貼文,請以「20220615-20231031」的格式輸入日期範圍。\n\n今年的日期可省略年份(例如0105-0106、20231105-0110)。\n\
|
||||||
\n開始日期和結果日期可擇一省略。舉例來說,「-0102」表示僅搜尋今年1月2日為止的貼文,「20231026-」表示僅搜尋2023年10月26日以後的貼文。"
|
\n開始日期和結果日期可擇一省略。舉例來說,「-0102」表示僅搜尋今年1月2日為止的貼文,「20231026-」表示僅搜尋2023年10月26日以後的貼文。"
|
||||||
|
noAltTextWarning: 有些附件沒有說明,您是否忘記寫了?
|
||||||
|
moderationNote: 管理員備註
|
||||||
|
ipFirstAcknowledged: 首次取得此 IP 位址的日期
|
||||||
|
driveCapacityOverride: 雲端硬碟容量變更
|
||||||
|
|
14
package.json
14
package.json
|
@ -43,14 +43,14 @@
|
||||||
"gulp-terser": "2.1.0"
|
"gulp-terser": "2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.5.3",
|
"@biomejs/biome": "1.6.1",
|
||||||
"@biomejs/cli-darwin-arm64": "^1.5.3",
|
"@biomejs/cli-darwin-arm64": "^1.6.1",
|
||||||
"@biomejs/cli-darwin-x64": "^1.5.3",
|
"@biomejs/cli-darwin-x64": "^1.6.1",
|
||||||
"@biomejs/cli-linux-arm64": "^1.5.3",
|
"@biomejs/cli-linux-arm64": "^1.6.1",
|
||||||
"@biomejs/cli-linux-x64": "^1.5.3",
|
"@biomejs/cli-linux-x64": "^1.6.1",
|
||||||
"@types/node": "20.11.24",
|
"@types/node": "20.11.28",
|
||||||
"execa": "8.0.1",
|
"execa": "8.0.1",
|
||||||
"pnpm": "8.15.4",
|
"pnpm": "8.15.4",
|
||||||
"typescript": "5.3.3"
|
"typescript": "5.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
125
packages/backend-rs/Cargo.lock
generated
125
packages/backend-rs/Cargo.lock
generated
|
@ -128,9 +128,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.80"
|
version = "1.0.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
|
@ -180,16 +180,6 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomic-write-file"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8204db279bf648d64fe845bd8840f78b39c8132ed4d6a4194c3b10d4b4cfb0b"
|
|
||||||
dependencies = [
|
|
||||||
"nix",
|
|
||||||
"rand",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -342,9 +332,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.15.3"
|
version = "3.15.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
|
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecheck"
|
name = "bytecheck"
|
||||||
|
@ -388,9 +378,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.89"
|
version = "1.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -406,9 +396,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.34"
|
version = "0.4.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
|
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
@ -421,9 +411,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.1"
|
version = "4.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -431,9 +421,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.1"
|
version = "4.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -443,11 +433,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.0"
|
version = "4.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.52",
|
"syn 2.0.52",
|
||||||
|
@ -868,9 +858,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.24"
|
version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
|
checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
|
@ -922,6 +912,12 @@ dependencies = [
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -1321,18 +1317,6 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nix"
|
|
||||||
version = "0.28.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.4.2",
|
|
||||||
"cfg-if",
|
|
||||||
"cfg_aliases",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
@ -1499,7 +1483,7 @@ version = "0.17.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
|
checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1672,9 +1656,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.78"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -1799,9 +1783,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.24"
|
version = "0.11.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
|
checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -2015,7 +1999,7 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114"
|
checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2056,7 +2040,7 @@ version = "0.12.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec13bfb4c4aef208f68dbea970dd40d13830c868aa8dcb4e106b956e6bb4f2fa"
|
checksum = "ec13bfb4c4aef208f68dbea970dd40d13830c868aa8dcb4e106b956e6bb4f2fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"sea-bae",
|
"sea-bae",
|
||||||
|
@ -2283,9 +2267,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx"
|
name = "sqlx"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf"
|
checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros",
|
"sqlx-macros",
|
||||||
|
@ -2296,9 +2280,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-core"
|
name = "sqlx-core"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
|
checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"atoi",
|
"atoi",
|
||||||
|
@ -2308,7 +2292,6 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"dotenvy",
|
|
||||||
"either",
|
"either",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
@ -2344,9 +2327,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros"
|
name = "sqlx-macros"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5"
|
checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2357,14 +2340,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros-core"
|
name = "sqlx-macros-core"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841"
|
checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-write-file",
|
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"either",
|
"either",
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"hex",
|
"hex",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -2384,9 +2366,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-mysql"
|
name = "sqlx-mysql"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4"
|
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64",
|
||||||
|
@ -2431,9 +2413,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-postgres"
|
name = "sqlx-postgres"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24"
|
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64",
|
||||||
|
@ -2462,7 +2444,6 @@ dependencies = [
|
||||||
"rust_decimal",
|
"rust_decimal",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha1",
|
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
|
@ -2476,9 +2457,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-sqlite"
|
name = "sqlx-sqlite"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490"
|
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -2639,18 +2620,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.57"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.57"
|
version = "1.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2735,9 +2716,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.14"
|
version = "0.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
|
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
@ -3013,9 +2994,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
|
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"wasite",
|
"wasite",
|
||||||
|
|
|
@ -12,31 +12,31 @@ napi = ["dep:napi", "dep:napi-derive"]
|
||||||
crate-type = ["cdylib", "lib"]
|
crate-type = ["cdylib", "lib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.75"
|
async-trait = "0.1.77"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.35"
|
||||||
cuid2 = "0.1.2"
|
cuid2 = "0.1.2"
|
||||||
jsonschema = "0.17.1"
|
jsonschema = "0.17.1"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
parse-display = "0.8.2"
|
parse-display = "0.8.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
schemars = { version = "0.8.16", features = ["chrono"] }
|
schemars = { version = "0.8.16", features = ["chrono"] }
|
||||||
sea-orm = { version = "0.12.10", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
sea-orm = { version = "0.12.14", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
|
||||||
serde = { version = "1.0.193", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.114"
|
||||||
thiserror = "1.0.52"
|
thiserror = "1.0.58"
|
||||||
tokio = { version = "1.35.1", features = ["full"] }
|
tokio = { version = "1.36.0", features = ["full"] }
|
||||||
|
|
||||||
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||||
napi = { version = "2.14.1", default-features = false, features = ["napi6", "tokio_rt"], optional = true }
|
napi = { version = "2.16.0", default-features = false, features = ["napi9", "tokio_rt"], optional = true }
|
||||||
napi-derive = { version = "2.14.5", optional = true }
|
napi-derive = { version = "2.16.0", optional = true }
|
||||||
basen = "0.1.0"
|
basen = "0.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
napi-build = "2.1.0"
|
napi-build = "2.1.2"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
|
@ -64,6 +64,8 @@ pub enum Relation {
|
||||||
DriveFolder,
|
DriveFolder,
|
||||||
#[sea_orm(has_many = "super::messaging_message::Entity")]
|
#[sea_orm(has_many = "super::messaging_message::Entity")]
|
||||||
MessagingMessage,
|
MessagingMessage,
|
||||||
|
#[sea_orm(has_many = "super::note_file::Entity")]
|
||||||
|
NoteFile,
|
||||||
#[sea_orm(has_many = "super::page::Entity")]
|
#[sea_orm(has_many = "super::page::Entity")]
|
||||||
Page,
|
Page,
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
|
@ -94,6 +96,12 @@ impl Related<super::messaging_message::Entity> for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::note_file::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::NoteFile.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::page::Entity> for Entity {
|
impl Related<super::page::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Page.def()
|
Relation::Page.def()
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub mod muting;
|
||||||
pub mod note;
|
pub mod note;
|
||||||
pub mod note_edit;
|
pub mod note_edit;
|
||||||
pub mod note_favorite;
|
pub mod note_favorite;
|
||||||
|
pub mod note_file;
|
||||||
pub mod note_reaction;
|
pub mod note_reaction;
|
||||||
pub mod note_thread_muting;
|
pub mod note_thread_muting;
|
||||||
pub mod note_unread;
|
pub mod note_unread;
|
||||||
|
|
|
@ -38,8 +38,6 @@ pub struct Model {
|
||||||
#[sea_orm(column_name = "visibleUserIds")]
|
#[sea_orm(column_name = "visibleUserIds")]
|
||||||
pub visible_user_ids: Vec<String>,
|
pub visible_user_ids: Vec<String>,
|
||||||
pub mentions: Vec<String>,
|
pub mentions: Vec<String>,
|
||||||
#[sea_orm(column_name = "mentionedRemoteUsers", column_type = "Text")]
|
|
||||||
pub mentioned_remote_users: String,
|
|
||||||
pub emojis: Vec<String>,
|
pub emojis: Vec<String>,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
#[sea_orm(column_name = "hasPoll")]
|
#[sea_orm(column_name = "hasPoll")]
|
||||||
|
@ -100,6 +98,8 @@ pub enum Relation {
|
||||||
NoteEdit,
|
NoteEdit,
|
||||||
#[sea_orm(has_many = "super::note_favorite::Entity")]
|
#[sea_orm(has_many = "super::note_favorite::Entity")]
|
||||||
NoteFavorite,
|
NoteFavorite,
|
||||||
|
#[sea_orm(has_many = "super::note_file::Entity")]
|
||||||
|
NoteFile,
|
||||||
#[sea_orm(has_many = "super::note_reaction::Entity")]
|
#[sea_orm(has_many = "super::note_reaction::Entity")]
|
||||||
NoteReaction,
|
NoteReaction,
|
||||||
#[sea_orm(has_many = "super::note_unread::Entity")]
|
#[sea_orm(has_many = "super::note_unread::Entity")]
|
||||||
|
@ -164,6 +164,12 @@ impl Related<super::note_favorite::Entity> for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::note_file::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::NoteFile.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Related<super::note_reaction::Entity> for Entity {
|
impl Related<super::note_reaction::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::NoteReaction.def()
|
Relation::NoteReaction.def()
|
||||||
|
|
48
packages/backend-rs/src/model/entity/note_file.rs
Normal file
48
packages/backend-rs/src/model/entity/note_file.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.12
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||||
|
#[sea_orm(table_name = "note_file")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(column_name = "serialNo", primary_key)]
|
||||||
|
pub serial_no: i64,
|
||||||
|
#[sea_orm(column_name = "noteId")]
|
||||||
|
pub note_id: String,
|
||||||
|
#[sea_orm(column_name = "fileId")]
|
||||||
|
pub file_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::drive_file::Entity",
|
||||||
|
from = "Column::FileId",
|
||||||
|
to = "super::drive_file::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
DriveFile,
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::note::Entity",
|
||||||
|
from = "Column::NoteId",
|
||||||
|
to = "super::note::Column::Id",
|
||||||
|
on_update = "NoAction",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::drive_file::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::DriveFile.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::note::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Note.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -33,6 +33,7 @@ pub use super::muting::Entity as Muting;
|
||||||
pub use super::note::Entity as Note;
|
pub use super::note::Entity as Note;
|
||||||
pub use super::note_edit::Entity as NoteEdit;
|
pub use super::note_edit::Entity as NoteEdit;
|
||||||
pub use super::note_favorite::Entity as NoteFavorite;
|
pub use super::note_favorite::Entity as NoteFavorite;
|
||||||
|
pub use super::note_file::Entity as NoteFile;
|
||||||
pub use super::note_reaction::Entity as NoteReaction;
|
pub use super::note_reaction::Entity as NoteReaction;
|
||||||
pub use super::note_thread_muting::Entity as NoteThreadMuting;
|
pub use super::note_thread_muting::Entity as NoteThreadMuting;
|
||||||
pub use super::note_unread::Entity as NoteUnread;
|
pub use super::note_unread::Entity as NoteUnread;
|
||||||
|
|
|
@ -22,23 +22,23 @@
|
||||||
"@swc/core-android-arm64": "1.3.11"
|
"@swc/core-android-arm64": "1.3.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bull-board/api": "5.14.2",
|
"@bull-board/api": "5.15.1",
|
||||||
"@bull-board/koa": "5.14.2",
|
"@bull-board/koa": "5.15.1",
|
||||||
"@bull-board/ui": "5.14.2",
|
"@bull-board/ui": "5.15.1",
|
||||||
"@discordapp/twemoji": "^15.0.2",
|
"@discordapp/twemoji": "^15.0.2",
|
||||||
"@koa/cors": "5.0.0",
|
"@koa/cors": "5.0.0",
|
||||||
"@koa/multer": "3.0.2",
|
"@koa/multer": "3.0.2",
|
||||||
"@koa/router": "12.0.1",
|
"@koa/router": "12.0.1",
|
||||||
"@ladjs/koa-views": "9.0.0",
|
"@ladjs/koa-views": "9.0.0",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@redocly/openapi-core": "1.10.3",
|
"@redocly/openapi-core": "1.10.4",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@twemoji/parser": "^15.0.0",
|
"@twemoji/parser": "^15.0.0",
|
||||||
"adm-zip": "^0.5.10",
|
"adm-zip": "^0.5.12",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"archiver": "7.0.0",
|
"archiver": "7.0.1",
|
||||||
"argon2": "^0.40.1",
|
"argon2": "^0.40.1",
|
||||||
"aws-sdk": "2.1571.0",
|
"aws-sdk": "2.1578.0",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"backend-rs": "workspace:*",
|
"backend-rs": "workspace:*",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"color-convert": "2.0.1",
|
"color-convert": "2.0.1",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"date-fns": "3.3.1",
|
"date-fns": "3.5.0",
|
||||||
"decompress": "^4.2.1",
|
"decompress": "^4.2.1",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"deepl-node": "1.12.0",
|
"deepl-node": "1.12.0",
|
||||||
|
@ -60,9 +60,9 @@
|
||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"got": "14.2.0",
|
"got": "14.2.1",
|
||||||
"gunzip-maybe": "^1.4.2",
|
"gunzip-maybe": "^1.4.2",
|
||||||
"happy-dom": "^13.6.2",
|
"happy-dom": "^13.8.6",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"ioredis": "5.3.2",
|
"ioredis": "5.3.2",
|
||||||
"ip-cidr": "4.0.0",
|
"ip-cidr": "4.0.0",
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.2",
|
"jsonld": "8.3.2",
|
||||||
"jsrsasign": "11.1.0",
|
"jsrsasign": "11.1.0",
|
||||||
"koa": "2.15.0",
|
"koa": "2.15.1",
|
||||||
"koa-body": "^6.0.1",
|
"koa-body": "^6.0.1",
|
||||||
"koa-bodyparser": "4.4.1",
|
"koa-bodyparser": "4.4.1",
|
||||||
"koa-favicon": "2.1.0",
|
"koa-favicon": "2.1.0",
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.9.11",
|
"nodemailer": "6.9.12",
|
||||||
"opencc-js": "^1.0.5",
|
"opencc-js": "^1.0.5",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "^9.2.2",
|
"otpauth": "^9.2.2",
|
||||||
|
@ -100,7 +100,7 @@
|
||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"pureimage": "0.4.13",
|
"pureimage": "0.4.13",
|
||||||
"qrcode": "1.5.3",
|
"qrcode": "1.5.3",
|
||||||
"qs": "6.11.2",
|
"qs": "6.12.0",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"re2": "1.20.10",
|
"re2": "1.20.10",
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"summaly": "2.7.0",
|
"summaly": "2.7.0",
|
||||||
"syslog-pro": "1.0.0",
|
"syslog-pro": "1.0.0",
|
||||||
"systeminformation": "5.22.0",
|
"systeminformation": "5.22.2",
|
||||||
"tar-stream": "^3.1.7",
|
"tar-stream": "^3.1.7",
|
||||||
"tesseract.js": "^5.0.5",
|
"tesseract.js": "^5.0.5",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
|
@ -129,7 +129,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swc/cli": "0.3.10",
|
"@swc/cli": "0.3.10",
|
||||||
"@swc/core": "1.4.4",
|
"@swc/core": "1.4.8",
|
||||||
"@types/adm-zip": "^0.5.5",
|
"@types/adm-zip": "^0.5.5",
|
||||||
"@types/bcryptjs": "2.4.6",
|
"@types/bcryptjs": "2.4.6",
|
||||||
"@types/color-convert": "^2.0.3",
|
"@types/color-convert": "^2.0.3",
|
||||||
|
@ -138,7 +138,7 @@
|
||||||
"@types/fluent-ffmpeg": "2.1.24",
|
"@types/fluent-ffmpeg": "2.1.24",
|
||||||
"@types/js-yaml": "4.0.9",
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/jsonld": "1.5.13",
|
"@types/jsonld": "1.5.13",
|
||||||
"@types/jsrsasign": "10.5.12",
|
"@types/jsrsasign": "10.5.13",
|
||||||
"@types/koa": "2.15.0",
|
"@types/koa": "2.15.0",
|
||||||
"@types/koa-bodyparser": "4.3.12",
|
"@types/koa-bodyparser": "4.3.12",
|
||||||
"@types/koa-cors": "0.0.6",
|
"@types/koa-cors": "0.0.6",
|
||||||
|
@ -150,7 +150,7 @@
|
||||||
"@types/koa__multer": "2.0.7",
|
"@types/koa__multer": "2.0.7",
|
||||||
"@types/koa__router": "12.0.4",
|
"@types/koa__router": "12.0.4",
|
||||||
"@types/mocha": "10.0.6",
|
"@types/mocha": "10.0.6",
|
||||||
"@types/node": "20.11.24",
|
"@types/node": "20.11.28",
|
||||||
"@types/node-fetch": "2.6.11",
|
"@types/node-fetch": "2.6.11",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.4",
|
||||||
|
@ -183,7 +183,7 @@
|
||||||
"ts-loader": "9.5.1",
|
"ts-loader": "9.5.1",
|
||||||
"ts-node": "10.9.2",
|
"ts-node": "10.9.2",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.4.2",
|
||||||
"webpack": "^5.90.3",
|
"webpack": "^5.90.3",
|
||||||
"ws": "8.16.0"
|
"ws": "8.16.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ function greet() {
|
||||||
136,
|
136,
|
||||||
0,
|
0,
|
||||||
)(
|
)(
|
||||||
" If you like Firefish, please consider starring or contributing to the repo. https://firefish.dev/firefish/firefish",
|
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ function showNodejsVersion(): void {
|
||||||
|
|
||||||
nodejsLogger.info(`Version ${process.version} detected.`);
|
nodejsLogger.info(`Version ${process.version} detected.`);
|
||||||
|
|
||||||
const minVersion = "v18.16.0";
|
const minVersion = "v18.17.0";
|
||||||
if (semver.lt(process.version, minVersion)) {
|
if (semver.lt(process.version, minVersion)) {
|
||||||
nodejsLogger.error(`At least Node.js ${minVersion} required!`);
|
nodejsLogger.error(`At least Node.js ${minVersion} required!`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
|
@ -73,6 +73,7 @@ import { UserPending } from "@/models/entities/user-pending.js";
|
||||||
import { Webhook } from "@/models/entities/webhook.js";
|
import { Webhook } from "@/models/entities/webhook.js";
|
||||||
import { UserIp } from "@/models/entities/user-ip.js";
|
import { UserIp } from "@/models/entities/user-ip.js";
|
||||||
import { NoteEdit } from "@/models/entities/note-edit.js";
|
import { NoteEdit } from "@/models/entities/note-edit.js";
|
||||||
|
import { NoteFile } from "@/models/entities/note-file.js";
|
||||||
|
|
||||||
import { entities as charts } from "@/services/chart/entities.js";
|
import { entities as charts } from "@/services/chart/entities.js";
|
||||||
import { dbLogger } from "./logger.js";
|
import { dbLogger } from "./logger.js";
|
||||||
|
@ -143,6 +144,7 @@ export const entities = [
|
||||||
Note,
|
Note,
|
||||||
NoteEdit,
|
NoteEdit,
|
||||||
NoteFavorite,
|
NoteFavorite,
|
||||||
|
NoteFile,
|
||||||
NoteReaction,
|
NoteReaction,
|
||||||
NoteWatching,
|
NoteWatching,
|
||||||
NoteThreadMuting,
|
NoteThreadMuting,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { EventEmitter } from "node:events";
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import boot from "./boot/index.js";
|
import boot from "./boot/index.js";
|
||||||
|
|
||||||
Error.stackTraceLimit = Infinity;
|
Error.stackTraceLimit = Number.POSITIVE_INFINITY;
|
||||||
EventEmitter.defaultMaxListeners = 128;
|
EventEmitter.defaultMaxListeners = 128;
|
||||||
|
|
||||||
boot().catch((err) => {
|
boot().catch((err) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ export class RemoveNativeUtilsMigration1705877093218
|
||||||
await queryRunner.query(`DROP TABLE IF EXISTS "reversi_game"`);
|
await queryRunner.query(`DROP TABLE IF EXISTS "reversi_game"`);
|
||||||
await queryRunner.query(`DROP TABLE IF EXISTS "reversi_matching"`);
|
await queryRunner.query(`DROP TABLE IF EXISTS "reversi_matching"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE INDEX IF NOT EXISTS "IDX_note_url" ON "note" ("text")`,
|
`CREATE INDEX IF NOT EXISTS "IDX_note_url" ON "note" ("url")`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(`DROP TABLE IF EXISTS "antenna_note"`);
|
await queryRunner.query(`DROP TABLE IF EXISTS "antenna_note"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
|
@ -105,10 +105,10 @@ export class RemoveNativeUtilsMigration1705877093218
|
||||||
`CREATE INDEX "IDX_9937ea48d7ae97ffb4f3f063a4" ON "antenna_note" ("read")`,
|
`CREATE INDEX "IDX_9937ea48d7ae97ffb4f3f063a4" ON "antenna_note" ("read")`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "antenna_note" ADD CONSTRAINT IF NOT EXISTS "FK_0d775946662d2575dfd2068a5f5" FOREIGN KEY ("antennaId") REFERENCES "antenna"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
`ALTER TABLE "antenna_note" ADD CONSTRAINT "FK_0d775946662d2575dfd2068a5f5" FOREIGN KEY ("antennaId") REFERENCES "antenna"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "antenna_note" ADD CONSTRAINT IF NOT EXISTS "FK_bd0397be22147e17210940e125b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
`ALTER TABLE "antenna_note" ADD CONSTRAINT "FK_bd0397be22147e17210940e125b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(`DROP INDEX IF EXISTS "IDX_note_url"`);
|
await queryRunner.query(`DROP INDEX IF EXISTS "IDX_note_url"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
|
@ -124,10 +124,10 @@ export class RemoveNativeUtilsMigration1705877093218
|
||||||
`CREATE INDEX IF NOT EXISTS "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId")`,
|
`CREATE INDEX IF NOT EXISTS "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId")`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "reversi_matching" ADD CONSTRAINT IF NOT EXISTS "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "reversi_matching" ADD CONSTRAINT IF NOT EXISTS "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`COMMENT ON COLUMN "reversi_matching"."createdAt" IS 'The created date of the ReversiMatching.'`,
|
`COMMENT ON COLUMN "reversi_matching"."createdAt" IS 'The created date of the ReversiMatching.'`,
|
||||||
|
@ -139,10 +139,10 @@ export class RemoveNativeUtilsMigration1705877093218
|
||||||
`CREATE INDEX IF NOT EXISTS "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt")`,
|
`CREATE INDEX IF NOT EXISTS "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt")`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "reversi_game" ADD CONSTRAINT IF NOT EXISTS "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "reversi_game" ADD CONSTRAINT IF NOT EXISTS "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`COMMENT ON COLUMN "reversi_game"."createdAt" IS 'The created date of the ReversiGame.'`,
|
`COMMENT ON COLUMN "reversi_game"."createdAt" IS 'The created date of the ReversiGame.'`,
|
||||||
|
|
41
packages/backend/src/migration/1710304584214-note-file.ts
Normal file
41
packages/backend/src/migration/1710304584214-note-file.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class NoteFile1710304584214 implements MigrationInterface {
|
||||||
|
name = "NoteFile1710304584214";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE TABLE "note_file" (
|
||||||
|
"serialNo" bigserial PRIMARY KEY,
|
||||||
|
"noteId" varchar(32) NOT NULL,
|
||||||
|
"fileId" varchar(32) NOT NULL
|
||||||
|
)`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(`
|
||||||
|
INSERT INTO "note_file" ("noteId", "fileId")
|
||||||
|
SELECT "t"."id", "t"."fid" FROM (
|
||||||
|
SELECT ROW_NUMBER() OVER () AS "rn", * FROM (
|
||||||
|
SELECT "id", UNNEST("fileIds") AS "fid" FROM "note"
|
||||||
|
) AS "s"
|
||||||
|
) AS "t"
|
||||||
|
INNER JOIN "drive_file" ON "drive_file"."id" = "t"."fid"
|
||||||
|
ORDER BY "rn"
|
||||||
|
`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "note_file" ADD FOREIGN KEY ("noteId") REFERENCES "note" ("id") ON DELETE CASCADE`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "note_file" ADD FOREIGN KEY ("fileId") REFERENCES "drive_file" ("id") ON DELETE CASCADE`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_note_file_noteId" ON "note_file" ("noteId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_note_file_fileId" ON "note_file" ("fileId")`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE "note_file"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class RemoveMentionedUsersColumn1710688552234
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "note" DROP COLUMN "mentionedRemoteUsers"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "note" ADD "mentionedRemoteUsers" TEXT NOT NULL DEFAULT '[]'::text`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(`CREATE TEMP TABLE IF NOT EXISTS "temp_mentions" AS
|
||||||
|
SELECT "id", "url", "uri", "username", "host"
|
||||||
|
FROM "user"
|
||||||
|
JOIN "user_profile" ON "user"."id" = "user_profile". "userId" WHERE "user"."host" IS NOT NULL`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE UNIQUE INDEX "temp_mentions_id" ON "temp_mentions"("id")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(`UPDATE "note" SET "mentionedRemoteUsers" = (
|
||||||
|
SELECT COALESCE(json_agg(row_to_json("data")::jsonb - 'id')::text, '[]') FROM "temp_mentions" AS "data"
|
||||||
|
WHERE "data"."id" = ANY("note"."mentions")
|
||||||
|
)`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class FixMutingIndices1710690239308 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muterId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_reply_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_reply_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_reply_muting_muterId"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_renote_muting_createdAt" ON "renote_muting" ("createdAt")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_renote_muting_muteeId" ON "renote_muting" ("muteeId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_renote_muting_muterId" ON "renote_muting" ("muterId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_reply_muting_createdAt" ON "reply_muting" ("createdAt")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_reply_muting_muteeId" ON "reply_muting" ("muteeId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_reply_muting_muterId" ON "reply_muting" ("muterId")`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muterId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_reply_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_reply_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_reply_muting_muterId"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_reply_muting_createdAt" ON "muting" ("createdAt")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_reply_muting_muteeId" ON "muting" ("muteeId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_reply_muting_muterId" ON "muting" ("muterId")`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
import { URL } from "node:url";
|
import { URL } from "node:url";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { toASCII } from "punycode";
|
import { toASCII } from "punycode";
|
||||||
|
import Logger from "@/services/logger.js";
|
||||||
|
import { inspect } from "node:util";
|
||||||
|
|
||||||
|
const logger = new Logger("convert-host");
|
||||||
|
|
||||||
export function getFullApAccount(username: string, host: string | null) {
|
export function getFullApAccount(username: string, host: string | null) {
|
||||||
return host
|
return host
|
||||||
|
@ -13,6 +17,20 @@ export function isSelfHost(host: string) {
|
||||||
return toPuny(config.host) === toPuny(host);
|
return toPuny(config.host) === toPuny(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isSameOrigin(src: unknown): boolean | null {
|
||||||
|
if (typeof src !== "string") {
|
||||||
|
logger.debug(`unknown origin: ${inspect(src)}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const u = new URL(src);
|
||||||
|
return u.origin === config.url;
|
||||||
|
} catch (e) {
|
||||||
|
logger.debug(inspect(e));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function extractDbHost(uri: string) {
|
export function extractDbHost(uri: string) {
|
||||||
const url = new URL(uri);
|
const url = new URL(uri);
|
||||||
return toPuny(url.hostname);
|
return toPuny(url.hostname);
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { packedQueueCountSchema } from "@/models/schema/queue.js";
|
||||||
import { packedGalleryPostSchema } from "@/models/schema/gallery-post.js";
|
import { packedGalleryPostSchema } from "@/models/schema/gallery-post.js";
|
||||||
import { packedEmojiSchema } from "@/models/schema/emoji.js";
|
import { packedEmojiSchema } from "@/models/schema/emoji.js";
|
||||||
import { packedNoteEdit } from "@/models/schema/note-edit.js";
|
import { packedNoteEdit } from "@/models/schema/note-edit.js";
|
||||||
|
import { packedNoteFileSchema } from "@/models/schema/note-file.js";
|
||||||
|
|
||||||
export const refs = {
|
export const refs = {
|
||||||
UserLite: packedUserLiteSchema,
|
UserLite: packedUserLiteSchema,
|
||||||
|
@ -47,6 +48,7 @@ export const refs = {
|
||||||
App: packedAppSchema,
|
App: packedAppSchema,
|
||||||
MessagingMessage: packedMessagingMessageSchema,
|
MessagingMessage: packedMessagingMessageSchema,
|
||||||
Note: packedNoteSchema,
|
Note: packedNoteSchema,
|
||||||
|
NoteFile: packedNoteFileSchema,
|
||||||
NoteEdit: packedNoteEdit,
|
NoteEdit: packedNoteEdit,
|
||||||
NoteReaction: packedNoteReactionSchema,
|
NoteReaction: packedNoteReactionSchema,
|
||||||
NoteFavorite: packedNoteFavoriteSchema,
|
NoteFavorite: packedNoteFavoriteSchema,
|
||||||
|
|
|
@ -4,12 +4,17 @@ import {
|
||||||
Index,
|
Index,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
Column,
|
Column,
|
||||||
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OneToMany,
|
||||||
|
type Relation,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { id } from "../id.js";
|
import { id } from "../id.js";
|
||||||
|
import { Note } from "./note.js";
|
||||||
import { User } from "./user.js";
|
import { User } from "./user.js";
|
||||||
import { DriveFolder } from "./drive-folder.js";
|
import { DriveFolder } from "./drive-folder.js";
|
||||||
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
|
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
|
||||||
|
import { NoteFile } from "./note-file.js";
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index(["userId", "folderId", "id"])
|
@Index(["userId", "folderId", "id"])
|
||||||
|
@ -31,12 +36,6 @@ export class DriveFile {
|
||||||
})
|
})
|
||||||
public userId: User["id"] | null;
|
public userId: User["id"] | null;
|
||||||
|
|
||||||
@ManyToOne((type) => User, {
|
|
||||||
onDelete: "SET NULL",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: 512,
|
length: 512,
|
||||||
|
@ -171,12 +170,6 @@ export class DriveFile {
|
||||||
})
|
})
|
||||||
public folderId: DriveFolder["id"] | null;
|
public folderId: DriveFolder["id"] | null;
|
||||||
|
|
||||||
@ManyToOne((type) => DriveFolder, {
|
|
||||||
onDelete: "SET NULL",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public folder: DriveFolder | null;
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column("boolean", {
|
@Column("boolean", {
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -205,4 +198,30 @@ export class DriveFile {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public requestIp: string | null;
|
public requestIp: string | null;
|
||||||
|
|
||||||
|
//#region Relations
|
||||||
|
@OneToMany(
|
||||||
|
() => NoteFile,
|
||||||
|
(noteFile: NoteFile) => noteFile.file,
|
||||||
|
)
|
||||||
|
public noteFiles: Relation<NoteFile[]>;
|
||||||
|
|
||||||
|
@ManyToMany(
|
||||||
|
() => Note,
|
||||||
|
(note: Note) => note.files,
|
||||||
|
)
|
||||||
|
public notes: Relation<Note[]>;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, {
|
||||||
|
onDelete: "SET NULL",
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: User | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => DriveFolder, {
|
||||||
|
onDelete: "SET NULL",
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public folder: DriveFolder | null;
|
||||||
|
//#endregion Relations
|
||||||
}
|
}
|
||||||
|
|
45
packages/backend/src/models/entities/note-file.ts
Normal file
45
packages/backend/src/models/entities/note-file.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
Column,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
type Relation,
|
||||||
|
} from "typeorm";
|
||||||
|
import { Note } from "./note.js";
|
||||||
|
import { DriveFile } from "./drive-file.js";
|
||||||
|
import { id } from "../id.js";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class NoteFile {
|
||||||
|
@PrimaryGeneratedColumn("increment")
|
||||||
|
public serialNo: number;
|
||||||
|
|
||||||
|
@Index("IDX_note_file_noteId", { unique: false })
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
public noteId: Note["id"];
|
||||||
|
|
||||||
|
@Index("IDX_note_file_fileId", { unique: false })
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
public fileId: DriveFile["id"];
|
||||||
|
|
||||||
|
//#region Relations
|
||||||
|
@ManyToOne(
|
||||||
|
() => Note,
|
||||||
|
(note: Note) => note.noteFiles,
|
||||||
|
)
|
||||||
|
public note: Relation<Note>;
|
||||||
|
|
||||||
|
@ManyToOne(
|
||||||
|
() => DriveFile,
|
||||||
|
(file: DriveFile) => file.noteFiles,
|
||||||
|
)
|
||||||
|
public file: Relation<DriveFile>;
|
||||||
|
//#endregion Relations
|
||||||
|
}
|
|
@ -2,15 +2,20 @@ import {
|
||||||
Entity,
|
Entity,
|
||||||
Index,
|
Index,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
|
JoinTable,
|
||||||
Column,
|
Column,
|
||||||
PrimaryColumn,
|
PrimaryColumn,
|
||||||
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OneToMany,
|
||||||
|
type Relation,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { User } from "./user.js";
|
import { User } from "./user.js";
|
||||||
import type { DriveFile } from "./drive-file.js";
|
import { DriveFile } from "./drive-file.js";
|
||||||
import { id } from "../id.js";
|
import { id } from "../id.js";
|
||||||
import { noteVisibilities } from "../../types.js";
|
import { noteVisibilities } from "../../types.js";
|
||||||
import { Channel } from "./channel.js";
|
import { Channel } from "./channel.js";
|
||||||
|
import { NoteFile } from "./note-file.js";
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index("IDX_NOTE_TAGS", { synchronize: false })
|
@Index("IDX_NOTE_TAGS", { synchronize: false })
|
||||||
|
@ -34,12 +39,6 @@ export class Note {
|
||||||
})
|
})
|
||||||
public replyId: Note["id"] | null;
|
public replyId: Note["id"] | null;
|
||||||
|
|
||||||
@ManyToOne((type) => Note, {
|
|
||||||
onDelete: "CASCADE",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public reply: Note | null;
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
|
@ -48,12 +47,6 @@ export class Note {
|
||||||
})
|
})
|
||||||
public renoteId: Note["id"] | null;
|
public renoteId: Note["id"] | null;
|
||||||
|
|
||||||
@ManyToOne((type) => Note, {
|
|
||||||
onDelete: "CASCADE",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public renote: Note | null;
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: 256,
|
length: 256,
|
||||||
|
@ -93,12 +86,6 @@ export class Note {
|
||||||
})
|
})
|
||||||
public userId: User["id"];
|
public userId: User["id"];
|
||||||
|
|
||||||
@ManyToOne((type) => User, {
|
|
||||||
onDelete: "CASCADE",
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Column("boolean", {
|
@Column("boolean", {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
@ -151,6 +138,8 @@ export class Note {
|
||||||
})
|
})
|
||||||
public score: number;
|
public score: number;
|
||||||
|
|
||||||
|
// FIXME: file id is not removed from this array even if the file is deleted
|
||||||
|
// TODO: drop this column and use note_files
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
|
@ -183,11 +172,6 @@ export class Note {
|
||||||
})
|
})
|
||||||
public mentions: User["id"][];
|
public mentions: User["id"][];
|
||||||
|
|
||||||
@Column("text", {
|
|
||||||
default: "[]",
|
|
||||||
})
|
|
||||||
public mentionedRemoteUsers: string;
|
|
||||||
|
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: 128,
|
length: 128,
|
||||||
array: true,
|
array: true,
|
||||||
|
@ -216,12 +200,55 @@ export class Note {
|
||||||
})
|
})
|
||||||
public channelId: Channel["id"] | null;
|
public channelId: Channel["id"] | null;
|
||||||
|
|
||||||
@ManyToOne((type) => Channel, {
|
//#region Relations
|
||||||
|
@OneToMany(
|
||||||
|
() => NoteFile,
|
||||||
|
(noteFile: NoteFile) => noteFile.note,
|
||||||
|
)
|
||||||
|
public noteFiles: Relation<NoteFile[]>;
|
||||||
|
|
||||||
|
@ManyToMany(
|
||||||
|
() => DriveFile,
|
||||||
|
(file: DriveFile) => file.notes,
|
||||||
|
)
|
||||||
|
@JoinTable({
|
||||||
|
name: "note_file",
|
||||||
|
joinColumn: {
|
||||||
|
name: "noteId",
|
||||||
|
referencedColumnName: "id",
|
||||||
|
},
|
||||||
|
inverseJoinColumn: {
|
||||||
|
name: "fileId",
|
||||||
|
referencedColumnName: "id",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
public files: Relation<DriveFile[]>;
|
||||||
|
|
||||||
|
@ManyToOne(() => Note, {
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public reply: Note | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => Note, {
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public renote: Note | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => Channel, {
|
||||||
onDelete: "CASCADE",
|
onDelete: "CASCADE",
|
||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public channel: Channel | null;
|
public channel: Channel | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, {
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: User | null;
|
||||||
|
//#endregion Relations
|
||||||
|
|
||||||
//#region Denormalized fields
|
//#region Denormalized fields
|
||||||
@Index()
|
@Index()
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
|
@ -274,10 +301,3 @@ export class Note {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IMentionedRemoteUsers = {
|
|
||||||
uri: string;
|
|
||||||
url?: string;
|
|
||||||
username: string;
|
|
||||||
host: string;
|
|
||||||
}[];
|
|
||||||
|
|
|
@ -66,12 +66,14 @@ import { InstanceRepository } from "./repositories/instance.js";
|
||||||
import { Webhook } from "./entities/webhook.js";
|
import { Webhook } from "./entities/webhook.js";
|
||||||
import { UserIp } from "./entities/user-ip.js";
|
import { UserIp } from "./entities/user-ip.js";
|
||||||
import { NoteEdit } from "./entities/note-edit.js";
|
import { NoteEdit } from "./entities/note-edit.js";
|
||||||
|
import { NoteFileRepository } from "./repositories/note-file.js";
|
||||||
|
|
||||||
export const Announcements = db.getRepository(Announcement);
|
export const Announcements = db.getRepository(Announcement);
|
||||||
export const AnnouncementReads = db.getRepository(AnnouncementRead);
|
export const AnnouncementReads = db.getRepository(AnnouncementRead);
|
||||||
export const Apps = AppRepository;
|
export const Apps = AppRepository;
|
||||||
export const Notes = NoteRepository;
|
export const Notes = NoteRepository;
|
||||||
export const NoteEdits = db.getRepository(NoteEdit);
|
export const NoteEdits = db.getRepository(NoteEdit);
|
||||||
|
export const NoteFiles = NoteFileRepository;
|
||||||
export const NoteFavorites = NoteFavoriteRepository;
|
export const NoteFavorites = NoteFavoriteRepository;
|
||||||
export const NoteWatchings = db.getRepository(NoteWatching);
|
export const NoteWatchings = db.getRepository(NoteWatching);
|
||||||
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
|
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
|
||||||
|
|
4
packages/backend/src/models/repositories/note-file.ts
Normal file
4
packages/backend/src/models/repositories/note-file.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { db } from "@/db/postgre.js";
|
||||||
|
import { NoteFile } from "@/models/entities/note-file.js";
|
||||||
|
|
||||||
|
export const NoteFileRepository = db.getRepository(NoteFile).extend({});
|
|
@ -513,8 +513,8 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
location: profile!.location,
|
location: profile!.location,
|
||||||
birthday: profile!.birthday,
|
birthday: profile!.birthday,
|
||||||
fields: profile!.fields,
|
fields: profile!.fields,
|
||||||
followersCount: followersCount || 0,
|
followersCount: followersCount ?? null,
|
||||||
followingCount: followingCount || 0,
|
followingCount: followingCount ?? null,
|
||||||
notesCount: user.notesCount,
|
notesCount: user.notesCount,
|
||||||
pinnedNoteIds: pins.map((pin) => pin.noteId),
|
pinnedNoteIds: pins.map((pin) => pin.noteId),
|
||||||
pinnedNotes: Notes.packMany(
|
pinnedNotes: Notes.packMany(
|
||||||
|
@ -528,8 +528,11 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
pinnedPage: profile!.pinnedPageId
|
pinnedPage: profile!.pinnedPageId
|
||||||
? Pages.pack(profile!.pinnedPageId, me)
|
? Pages.pack(profile!.pinnedPageId, me)
|
||||||
: null,
|
: null,
|
||||||
publicReactions: profile!.publicReactions,
|
// TODO: federate publicReactions
|
||||||
ffVisibility: profile!.ffVisibility,
|
publicReactions:
|
||||||
|
user.host == null ? profile!.publicReactions : false,
|
||||||
|
// TODO: federate ffVisibility
|
||||||
|
ffVisibility: user.host == null ? profile!.ffVisibility : "private",
|
||||||
twoFactorEnabled: profile!.twoFactorEnabled,
|
twoFactorEnabled: profile!.twoFactorEnabled,
|
||||||
usePasswordLessLogin: profile!.usePasswordLessLogin,
|
usePasswordLessLogin: profile!.usePasswordLessLogin,
|
||||||
securityKeys: UserSecurityKeys.countBy({
|
securityKeys: UserSecurityKeys.countBy({
|
||||||
|
|
24
packages/backend/src/models/schema/note-file.ts
Normal file
24
packages/backend/src/models/schema/note-file.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
export const packedNoteFileSchema = {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
serialNo: {
|
||||||
|
type: "number",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
noteId: {
|
||||||
|
type: "string",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
format: "id",
|
||||||
|
example: "xxxxxxxxxx",
|
||||||
|
},
|
||||||
|
fileId: {
|
||||||
|
type: "string",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
format: "id",
|
||||||
|
example: "xxxxxxxxxx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
|
@ -1,6 +1,5 @@
|
||||||
import promiseLimit from "promise-limit";
|
import promiseLimit from "promise-limit";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import config from "@/config/index.js";
|
|
||||||
import Resolver from "../resolver.js";
|
import Resolver from "../resolver.js";
|
||||||
import post from "@/services/note/create.js";
|
import post from "@/services/note/create.js";
|
||||||
import { extractMentionedUsers } from "@/services/note/create.js";
|
import { extractMentionedUsers } from "@/services/note/create.js";
|
||||||
|
@ -14,7 +13,7 @@ import { extractPollFromQuestion } from "./question.js";
|
||||||
import vote from "@/services/note/polls/vote.js";
|
import vote from "@/services/note/polls/vote.js";
|
||||||
import { apLogger } from "../logger.js";
|
import { apLogger } from "../logger.js";
|
||||||
import { DriveFile } from "@/models/entities/drive-file.js";
|
import { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import { extractDbHost, toPuny } from "@/misc/convert-host.js";
|
import { extractDbHost, isSameOrigin, toPuny } from "@/misc/convert-host.js";
|
||||||
import {
|
import {
|
||||||
Emojis,
|
Emojis,
|
||||||
Polls,
|
Polls,
|
||||||
|
@ -234,7 +233,7 @@ export async function createNote(
|
||||||
.catch(async (e) => {
|
.catch(async (e) => {
|
||||||
// トークだったらinReplyToのエラーは無視
|
// トークだったらinReplyToのエラーは無視
|
||||||
const uri = getApId(note.inReplyTo);
|
const uri = getApId(note.inReplyTo);
|
||||||
if (uri.startsWith(`${config.url}/`)) {
|
if (isSameOrigin(uri)) {
|
||||||
const id = uri.split("/").pop();
|
const id = uri.split("/").pop();
|
||||||
const talk = await MessagingMessages.findOneBy({ id });
|
const talk = await MessagingMessages.findOneBy({ id });
|
||||||
if (talk) {
|
if (talk) {
|
||||||
|
@ -439,7 +438,7 @@ export async function resolveNote(
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (uri.startsWith(config.url)) {
|
if (isSameOrigin(uri)) {
|
||||||
throw new StatusError(
|
throw new StatusError(
|
||||||
"cannot resolve local note",
|
"cannot resolve local note",
|
||||||
400,
|
400,
|
||||||
|
@ -556,7 +555,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) {
|
||||||
if (!uri) throw new Error("Missing note uri");
|
if (!uri) throw new Error("Missing note uri");
|
||||||
|
|
||||||
// Skip if URI points to this server
|
// Skip if URI points to this server
|
||||||
if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local");
|
if (isSameOrigin(uri)) throw new Error("uri points local");
|
||||||
|
|
||||||
// A new resolver is created if not specified
|
// A new resolver is created if not specified
|
||||||
if (resolver == null) resolver = new Resolver();
|
if (resolver == null) resolver = new Resolver();
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { UserNotePining } from "@/models/entities/user-note-pining.js";
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from "@/misc/gen-id.js";
|
||||||
import { UserPublickey } from "@/models/entities/user-publickey.js";
|
import { UserPublickey } from "@/models/entities/user-publickey.js";
|
||||||
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
|
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
|
||||||
import { toPuny } from "@/misc/convert-host.js";
|
import { isSameOrigin, toPuny } from "@/misc/convert-host.js";
|
||||||
import { UserProfile } from "@/models/entities/user-profile.js";
|
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
import { toArray } from "@/prelude/array.js";
|
import { toArray } from "@/prelude/array.js";
|
||||||
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
|
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
|
||||||
|
@ -138,7 +138,7 @@ export async function fetchPerson(
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
|
|
||||||
// Fetch from the database if the URI points to this server
|
// Fetch from the database if the URI points to this server
|
||||||
if (uri.startsWith(`${config.url}/`)) {
|
if (isSameOrigin(uri)) {
|
||||||
const id = uri.split("/").pop();
|
const id = uri.split("/").pop();
|
||||||
const u = await Users.findOneBy({ id });
|
const u = await Users.findOneBy({ id });
|
||||||
if (u) await uriPersonCache.set(uri, u);
|
if (u) await uriPersonCache.set(uri, u);
|
||||||
|
@ -166,7 +166,7 @@ export async function createPerson(
|
||||||
): Promise<User> {
|
): Promise<User> {
|
||||||
if (typeof uri !== "string") throw new Error("uri is not string");
|
if (typeof uri !== "string") throw new Error("uri is not string");
|
||||||
|
|
||||||
if (uri.startsWith(config.url)) {
|
if (isSameOrigin(uri)) {
|
||||||
throw new StatusError(
|
throw new StatusError(
|
||||||
"cannot resolve local user",
|
"cannot resolve local user",
|
||||||
400,
|
400,
|
||||||
|
@ -419,7 +419,7 @@ export async function updatePerson(
|
||||||
if (typeof uri !== "string") throw new Error("uri is not string");
|
if (typeof uri !== "string") throw new Error("uri is not string");
|
||||||
|
|
||||||
// Skip if the URI points to this server
|
// Skip if the URI points to this server
|
||||||
if (uri.startsWith(`${config.url}/`)) {
|
if (isSameOrigin(uri)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import config from "@/config/index.js";
|
|
||||||
import Resolver from "../resolver.js";
|
import Resolver from "../resolver.js";
|
||||||
import type { IObject, IQuestion } from "../type.js";
|
import type { IObject, IQuestion } from "../type.js";
|
||||||
import { getApId, isQuestion } from "../type.js";
|
import { getApId, isQuestion } from "../type.js";
|
||||||
import { apLogger } from "../logger.js";
|
import { apLogger } from "../logger.js";
|
||||||
import { Notes, Polls } from "@/models/index.js";
|
import { Notes, Polls } from "@/models/index.js";
|
||||||
import type { IPoll } from "@/models/entities/poll.js";
|
import type { IPoll } from "@/models/entities/poll.js";
|
||||||
|
import { isSameOrigin } from "@/misc/convert-host.js";
|
||||||
|
|
||||||
export async function extractPollFromQuestion(
|
export async function extractPollFromQuestion(
|
||||||
source: string | IObject,
|
source: string | IObject,
|
||||||
|
@ -57,7 +57,7 @@ export async function updateQuestion(
|
||||||
const uri = typeof value === "string" ? value : getApId(value);
|
const uri = typeof value === "string" ? value : getApId(value);
|
||||||
|
|
||||||
// Skip if URI points to this server
|
// Skip if URI points to this server
|
||||||
if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local");
|
if (isSameOrigin(uri)) throw new Error("uri points local");
|
||||||
|
|
||||||
//#region Already registered with this server?
|
//#region Already registered with this server?
|
||||||
const note = await Notes.findOneBy({ uri });
|
const note = await Notes.findOneBy({ uri });
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { In, IsNull } from "typeorm";
|
import { In, IsNull } from "typeorm";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import { DriveFiles, Notes, Users, Emojis, Polls } from "@/models/index.js";
|
import { DriveFiles, Notes, Users, Emojis, Polls } from "@/models/index.js";
|
||||||
import type { Emoji } from "@/models/entities/emoji.js";
|
import type { Emoji } from "@/models/entities/emoji.js";
|
||||||
|
@ -61,26 +61,6 @@ export default async function renderNote(
|
||||||
|
|
||||||
const attributedTo = `${config.url}/users/${note.userId}`;
|
const attributedTo = `${config.url}/users/${note.userId}`;
|
||||||
|
|
||||||
const mentions = (
|
|
||||||
JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers
|
|
||||||
).map((x) => x.uri);
|
|
||||||
|
|
||||||
let to: string[] = [];
|
|
||||||
let cc: string[] = [];
|
|
||||||
|
|
||||||
if (note.visibility === "public") {
|
|
||||||
to = ["https://www.w3.org/ns/activitystreams#Public"];
|
|
||||||
cc = [`${attributedTo}/followers`].concat(mentions);
|
|
||||||
} else if (note.visibility === "home") {
|
|
||||||
to = [`${attributedTo}/followers`];
|
|
||||||
cc = ["https://www.w3.org/ns/activitystreams#Public"].concat(mentions);
|
|
||||||
} else if (note.visibility === "followers") {
|
|
||||||
to = [`${attributedTo}/followers`];
|
|
||||||
cc = mentions;
|
|
||||||
} else {
|
|
||||||
to = mentions;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mentionedUsers =
|
const mentionedUsers =
|
||||||
note.mentions.length > 0
|
note.mentions.length > 0
|
||||||
? await Users.findBy({
|
? await Users.findBy({
|
||||||
|
@ -88,6 +68,27 @@ export default async function renderNote(
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const mentionUris = mentionedUsers
|
||||||
|
// only remote users
|
||||||
|
.filter((user) => Users.isRemoteUser(user))
|
||||||
|
.map((user) => user.uri);
|
||||||
|
|
||||||
|
let to: string[] = [];
|
||||||
|
let cc: string[] = [];
|
||||||
|
|
||||||
|
if (note.visibility === "public") {
|
||||||
|
to = ["https://www.w3.org/ns/activitystreams#Public"];
|
||||||
|
cc = [`${attributedTo}/followers`].concat(mentionUris);
|
||||||
|
} else if (note.visibility === "home") {
|
||||||
|
to = [`${attributedTo}/followers`];
|
||||||
|
cc = ["https://www.w3.org/ns/activitystreams#Public"].concat(mentionUris);
|
||||||
|
} else if (note.visibility === "followers") {
|
||||||
|
to = [`${attributedTo}/followers`];
|
||||||
|
cc = mentionUris;
|
||||||
|
} else {
|
||||||
|
to = mentionUris;
|
||||||
|
}
|
||||||
|
|
||||||
const hashtagTags = (note.tags || []).map((tag) => renderHashtag(tag));
|
const hashtagTags = (note.tags || []).map((tag) => renderHashtag(tag));
|
||||||
const mentionTags = mentionedUsers.map((u) => renderMention(u));
|
const mentionTags = mentionedUsers.map((u) => renderMention(u));
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Brackets } from "typeorm";
|
|
||||||
import { Notes } from "@/models/index.js";
|
import { Notes } from "@/models/index.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
@ -7,6 +6,7 @@ import { generateVisibilityQuery } from "@/server/api/common/generate-visibility
|
||||||
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
|
||||||
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
||||||
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
|
||||||
|
import type { SelectQueryBuilder } from "typeorm";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -69,6 +69,9 @@ export const paramDef = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default define(meta, paramDef, async (ps, me) => {
|
export default define(meta, paramDef, async (ps, me) => {
|
||||||
|
async function search(
|
||||||
|
modifier?: (query: SelectQueryBuilder<Note>) => void,
|
||||||
|
): Promise<Note[]> {
|
||||||
const query = makePaginationQuery(
|
const query = makePaginationQuery(
|
||||||
Notes.createQueryBuilder("note"),
|
Notes.createQueryBuilder("note"),
|
||||||
ps.sinceId,
|
ps.sinceId,
|
||||||
|
@ -76,6 +79,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
ps.sinceDate ?? undefined,
|
ps.sinceDate ?? undefined,
|
||||||
ps.untilDate ?? undefined,
|
ps.untilDate ?? undefined,
|
||||||
);
|
);
|
||||||
|
modifier?.(query);
|
||||||
|
|
||||||
if (ps.userId != null) {
|
if (ps.userId != null) {
|
||||||
query.andWhere("note.userId = :userId", { userId: ps.userId });
|
query.andWhere("note.userId = :userId", { userId: ps.userId });
|
||||||
|
@ -87,31 +91,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.query != null) {
|
|
||||||
const q = sqlLikeEscape(ps.query);
|
|
||||||
|
|
||||||
if (ps.searchCwAndAlt) {
|
|
||||||
query.andWhere(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where("note.text &@~ :q", { q })
|
|
||||||
.orWhere("note.cw &@~ :q", { q })
|
|
||||||
.orWhere(
|
|
||||||
`EXISTS (
|
|
||||||
SELECT FROM "drive_file"
|
|
||||||
WHERE
|
|
||||||
comment &@~ :q
|
|
||||||
AND
|
|
||||||
drive_file."id" = ANY(note."fileIds")
|
|
||||||
)`,
|
|
||||||
{ q },
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
query.andWhere("note.text &@~ :q", { q });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query.innerJoinAndSelect("note.user", "user");
|
query.innerJoinAndSelect("note.user", "user");
|
||||||
|
|
||||||
// "from: me": search all (public, home, followers, specified) my posts
|
// "from: me": search all (public, home, followers, specified) my posts
|
||||||
|
@ -153,7 +132,60 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
if (me) generateMutedUserQuery(query, me);
|
if (me) generateMutedUserQuery(query, me);
|
||||||
if (me) generateBlockedUserQuery(query, me);
|
if (me) generateBlockedUserQuery(query, me);
|
||||||
|
|
||||||
const notes: Note[] = await query.take(ps.limit).getMany();
|
return await query.take(ps.limit).getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
let notes: Note[];
|
||||||
|
|
||||||
|
if (ps.query != null) {
|
||||||
|
const q = sqlLikeEscape(ps.query);
|
||||||
|
|
||||||
|
if (ps.searchCwAndAlt) {
|
||||||
|
// Whether we should return latest notes first
|
||||||
|
const isDescendingOrder =
|
||||||
|
(ps.sinceId == null || ps.untilId != null) &&
|
||||||
|
(ps.sinceId != null ||
|
||||||
|
ps.untilId != null ||
|
||||||
|
ps.sinceDate == null ||
|
||||||
|
ps.untilDate != null);
|
||||||
|
|
||||||
|
const compare = isDescendingOrder
|
||||||
|
? (lhs: Note, rhs: Note) =>
|
||||||
|
Math.sign(rhs.createdAt.getTime() - lhs.createdAt.getTime())
|
||||||
|
: (lhs: Note, rhs: Note) =>
|
||||||
|
Math.sign(lhs.createdAt.getTime() - rhs.createdAt.getTime());
|
||||||
|
|
||||||
|
notes = [
|
||||||
|
...new Map(
|
||||||
|
(
|
||||||
|
await Promise.all([
|
||||||
|
search((query) => {
|
||||||
|
query.andWhere("note.text &@~ :q", { q });
|
||||||
|
}),
|
||||||
|
search((query) => {
|
||||||
|
query.andWhere("note.cw &@~ :q", { q });
|
||||||
|
}),
|
||||||
|
search((query) => {
|
||||||
|
query
|
||||||
|
.andWhere("drive_file.comment &@~ :q", { q })
|
||||||
|
.innerJoin("note.files", "drive_file");
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
.flatMap((e) => e)
|
||||||
|
.map((note) => [note.id, note]),
|
||||||
|
).values(),
|
||||||
|
]
|
||||||
|
.sort(compare)
|
||||||
|
.slice(0, ps.limit);
|
||||||
|
} else {
|
||||||
|
notes = await search((query) => {
|
||||||
|
query.andWhere("note.text &@~ :q", { q });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notes = await search();
|
||||||
|
}
|
||||||
|
|
||||||
return await Notes.packMany(notes, me);
|
return await Notes.packMany(notes, me);
|
||||||
});
|
});
|
||||||
|
|
|
@ -90,6 +90,11 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
|
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||||
|
|
||||||
|
// TODO: federate ffVisibility
|
||||||
|
if (profile.userHost != null) {
|
||||||
|
throw new ApiError(meta.errors.forbidden);
|
||||||
|
}
|
||||||
|
|
||||||
if (profile.ffVisibility === "private") {
|
if (profile.ffVisibility === "private") {
|
||||||
if (me == null || me.id !== user.id) {
|
if (me == null || me.id !== user.id) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.forbidden);
|
||||||
|
|
|
@ -89,6 +89,11 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
|
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
|
||||||
|
|
||||||
|
// TODO: federate ffVisibility
|
||||||
|
if (profile.userHost != null) {
|
||||||
|
throw new ApiError(meta.errors.forbidden);
|
||||||
|
}
|
||||||
|
|
||||||
if (profile.ffVisibility === "private") {
|
if (profile.ffVisibility === "private") {
|
||||||
if (me == null || me.id !== user.id) {
|
if (me == null || me.id !== user.id) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.forbidden);
|
||||||
|
|
|
@ -49,6 +49,11 @@ export const paramDef = {
|
||||||
export default define(meta, paramDef, async (ps, me) => {
|
export default define(meta, paramDef, async (ps, me) => {
|
||||||
const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
|
const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
|
||||||
|
|
||||||
|
// TODO: federate publicReactions
|
||||||
|
if (profile.userHost != null) {
|
||||||
|
throw new ApiError(meta.errors.reactionsNotPublic);
|
||||||
|
}
|
||||||
|
|
||||||
if (!profile.publicReactions && (me == null || me.id !== ps.userId)) {
|
if (!profile.publicReactions && (me == null || me.id !== ps.userId)) {
|
||||||
throw new ApiError(meta.errors.reactionsNotPublic);
|
throw new ApiError(meta.errors.reactionsNotPublic);
|
||||||
}
|
}
|
||||||
|
|
|
@ -463,7 +463,7 @@ export default abstract class Chart<T extends Schema> {
|
||||||
protected commit(diff: Commit<T>, group: string | null = null): void {
|
protected commit(diff: Commit<T>, group: string | null = null): void {
|
||||||
for (const [k, v] of Object.entries(diff)) {
|
for (const [k, v] of Object.entries(diff)) {
|
||||||
if (v == null || v === 0 || (Array.isArray(v) && v.length === 0))
|
if (v == null || v === 0 || (Array.isArray(v) && v.length === 0))
|
||||||
// rome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
|
// biome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
|
||||||
delete diff[k];
|
delete diff[k];
|
||||||
}
|
}
|
||||||
this.buffer.push({
|
this.buffer.push({
|
||||||
|
|
|
@ -134,13 +134,6 @@ export async function createMessage(
|
||||||
userId: message.userId,
|
userId: message.userId,
|
||||||
visibility: "specified",
|
visibility: "specified",
|
||||||
mentions: [recipientUser].map((u) => u.id),
|
mentions: [recipientUser].map((u) => u.id),
|
||||||
mentionedRemoteUsers: JSON.stringify(
|
|
||||||
[recipientUser].map((u) => ({
|
|
||||||
uri: u.uri,
|
|
||||||
username: u.username,
|
|
||||||
host: u.host,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
} as Note;
|
} as Note;
|
||||||
|
|
||||||
let renderedNote: Record<string, unknown> = await renderNote(
|
let renderedNote: Record<string, unknown> = await renderNote(
|
||||||
|
|
|
@ -18,7 +18,6 @@ import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instanc
|
||||||
import { extractMentions } from "@/misc/extract-mentions.js";
|
import { extractMentions } from "@/misc/extract-mentions.js";
|
||||||
import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js";
|
import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js";
|
||||||
import { extractHashtags } from "@/misc/extract-hashtags.js";
|
import { extractHashtags } from "@/misc/extract-hashtags.js";
|
||||||
import type { IMentionedRemoteUsers } from "@/models/entities/note.js";
|
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
import {
|
import {
|
||||||
Mutings,
|
Mutings,
|
||||||
|
@ -31,6 +30,7 @@ import {
|
||||||
Channels,
|
Channels,
|
||||||
ChannelFollowings,
|
ChannelFollowings,
|
||||||
NoteThreadMutings,
|
NoteThreadMutings,
|
||||||
|
NoteFiles,
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import type { App } from "@/models/entities/app.js";
|
import type { App } from "@/models/entities/app.js";
|
||||||
|
@ -343,6 +343,12 @@ export default async (
|
||||||
|
|
||||||
const note = await insertNote(user, data, tags, emojis, mentionedUsers);
|
const note = await insertNote(user, data, tags, emojis, mentionedUsers);
|
||||||
|
|
||||||
|
await NoteFiles.insert(
|
||||||
|
note.fileIds.map((fileId) => ({ noteId: note.id, fileId })),
|
||||||
|
).catch((e) => {
|
||||||
|
logger.error(inspect(e));
|
||||||
|
});
|
||||||
|
|
||||||
res(note);
|
res(note);
|
||||||
|
|
||||||
// Register host
|
// Register host
|
||||||
|
@ -744,21 +750,6 @@ async function insertNote(
|
||||||
// Append mentions data
|
// Append mentions data
|
||||||
if (mentionedUsers.length > 0) {
|
if (mentionedUsers.length > 0) {
|
||||||
insert.mentions = mentionedUsers.map((u) => u.id);
|
insert.mentions = mentionedUsers.map((u) => u.id);
|
||||||
const profiles = await UserProfiles.findBy({ userId: In(insert.mentions) });
|
|
||||||
insert.mentionedRemoteUsers = JSON.stringify(
|
|
||||||
mentionedUsers
|
|
||||||
.filter((u) => Users.isRemoteUser(u))
|
|
||||||
.map((u) => {
|
|
||||||
const profile = profiles.find((p) => p.userId === u.id);
|
|
||||||
const url = profile != null ? profile.url : null;
|
|
||||||
return {
|
|
||||||
uri: u.uri,
|
|
||||||
url: url == null ? undefined : url,
|
|
||||||
username: u.username,
|
|
||||||
host: u.host,
|
|
||||||
} as IMentionedRemoteUsers[0];
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿を作成
|
// 投稿を作成
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Brackets, In } from "typeorm";
|
import { Brackets, In, IsNull, Not } from "typeorm";
|
||||||
import { publishNoteStream } from "@/services/stream.js";
|
import { publishNoteStream } from "@/services/stream.js";
|
||||||
import renderDelete from "@/remote/activitypub/renderer/delete.js";
|
import renderDelete from "@/remote/activitypub/renderer/delete.js";
|
||||||
import renderAnnounce from "@/remote/activitypub/renderer/announce.js";
|
import renderAnnounce from "@/remote/activitypub/renderer/announce.js";
|
||||||
|
@ -6,8 +6,8 @@ import renderUndo from "@/remote/activitypub/renderer/undo.js";
|
||||||
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
|
||||||
import renderTombstone from "@/remote/activitypub/renderer/tombstone.js";
|
import renderTombstone from "@/remote/activitypub/renderer/tombstone.js";
|
||||||
import config from "@/config/index.js";
|
import config from "@/config/index.js";
|
||||||
import { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
||||||
import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js";
|
import type { Note } from "@/models/entities/note.js";
|
||||||
import { Notes, Users, Instances } from "@/models/index.js";
|
import { Notes, Users, Instances } from "@/models/index.js";
|
||||||
import {
|
import {
|
||||||
deliverToFollowers,
|
deliverToFollowers,
|
||||||
|
@ -199,11 +199,12 @@ async function getMentionedRemoteUsers(note: Note) {
|
||||||
const where = [] as any[];
|
const where = [] as any[];
|
||||||
|
|
||||||
// mention / reply / dm
|
// mention / reply / dm
|
||||||
const uris = (
|
if (note.mentions.length > 0) {
|
||||||
JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers
|
where.push({
|
||||||
).map((x) => x.uri);
|
id: In(note.mentions),
|
||||||
if (uris.length > 0) {
|
// only remote users, local users are on the server and do not need to be notified
|
||||||
where.push({ uri: In(uris) });
|
host: Not(IsNull()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// renote / quote
|
// renote / quote
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import rndstr from "rndstr";
|
import rndstr from "rndstr";
|
||||||
import { initDb } from "../src/db/postgre.js";
|
import { initDb } from "../src/db/postgre.js";
|
||||||
import { initTestDb } from "./utils.js";
|
import { initTestDb } from "./utils.js";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import httpSignature from "@peertube/http-signature";
|
import httpSignature from "@peertube/http-signature";
|
||||||
import { genRsaKeyPair } from "../src/misc/gen-key-pair.js";
|
import { genRsaKeyPair } from "../src/misc/gen-key-pair.js";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
post,
|
post,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
post,
|
post,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
post,
|
post,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import {
|
import {
|
||||||
signup,
|
signup,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
|
|
||||||
import { parse } from "mfm-js";
|
import { parse } from "mfm-js";
|
||||||
import { extractMentions } from "../src/misc/extract-mentions.js";
|
import { extractMentions } from "../src/misc/extract-mentions.js";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import * as openapi from "@redocly/openapi-core";
|
import * as openapi from "@redocly/openapi-core";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
connectStream,
|
connectStream,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import { dirname } from "node:path";
|
import { dirname } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { getFileInfo } from "../src/misc/get-file-info.js";
|
import { getFileInfo } from "../src/misc/get-file-info.js";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
|
|
||||||
import { fromHtml } from "../src/mfm/from-html.js";
|
import { fromHtml } from "../src/mfm/from-html.js";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
post,
|
post,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import { Note } from "../src/models/entities/note.js";
|
import { Note } from "../src/models/entities/note.js";
|
||||||
import {
|
import {
|
||||||
api,
|
api,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import { just, nothing } from "../../src/prelude/maybe.js";
|
import { just, nothing } from "../../src/prelude/maybe.js";
|
||||||
|
|
||||||
describe("just", () => {
|
describe("just", () => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import { query } from "../../src/prelude/url.js";
|
import { query } from "../../src/prelude/url.js";
|
||||||
|
|
||||||
describe("url", () => {
|
describe("url", () => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import { Following } from "../src/models/entities/following.js";
|
import { Following } from "../src/models/entities/following.js";
|
||||||
import {
|
import {
|
||||||
api,
|
api,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
connectStream,
|
connectStream,
|
||||||
|
|
|
@ -26,16 +26,9 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["../src/*"]
|
"@/*": ["../src/*"]
|
||||||
},
|
},
|
||||||
"typeRoots": [
|
"typeRoots": ["../node_modules/@types", "../src/@types"],
|
||||||
"../node_modules/@types",
|
"lib": ["esnext"]
|
||||||
"../src/@types"
|
|
||||||
],
|
|
||||||
"lib": [
|
|
||||||
"esnext"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"include": [
|
"include": ["./**/*.ts"]
|
||||||
"./**/*.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
import * as assert from "assert";
|
import * as assert from "node:assert";
|
||||||
import * as childProcess from "child_process";
|
import type * as childProcess from "node:child_process";
|
||||||
import {
|
import {
|
||||||
async,
|
async,
|
||||||
post,
|
post,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import * as childProcess from "child_process";
|
|
||||||
import { SIGKILL } from "constants";
|
import { SIGKILL } from "constants";
|
||||||
|
import * as childProcess from "node:child_process";
|
||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import * as http from "node:http";
|
import * as http from "node:http";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { dirname } from "node:path";
|
import { dirname } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import type { endpoints, Entities } from "firefish-js";
|
import type { Entities, endpoints } from "firefish-js";
|
||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
import got from "got";
|
import got from "got";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
|
|
@ -25,24 +25,13 @@
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": ["./src/*"]
|
||||||
"./src/*"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"outDir": "./built",
|
"outDir": "./built",
|
||||||
"types": [
|
"types": ["node"],
|
||||||
"node"
|
"typeRoots": ["./node_modules/@types", "./src/@types"],
|
||||||
],
|
"lib": ["esnext"]
|
||||||
"typeRoots": [
|
|
||||||
"./node_modules/@types",
|
|
||||||
"./src/@types"
|
|
||||||
],
|
|
||||||
"lib": [
|
|
||||||
"esnext"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"include": [
|
"include": ["./src/**/*.ts"]
|
||||||
"./src/**/*.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
2
packages/client/@types/theme.d.ts
vendored
2
packages/client/@types/theme.d.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
declare module "@/themes/*.json5" {
|
declare module "@/themes/*.json5" {
|
||||||
import { Theme } from "@/scripts/theme";
|
import type { Theme } from "@/scripts/theme";
|
||||||
|
|
||||||
const theme: Theme;
|
const theme: Theme;
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
"city-timezones": "^1.2.1",
|
"city-timezones": "^1.2.1",
|
||||||
"compare-versions": "6.1.0",
|
"compare-versions": "6.1.0",
|
||||||
"cropperjs": "2.0.0-beta.4",
|
"cropperjs": "2.0.0-beta.4",
|
||||||
"date-fns": "3.3.1",
|
"date-fns": "3.5.0",
|
||||||
"emojilib": "^3.0.11",
|
"emojilib": "^3.0.11",
|
||||||
"eslint-config-prettier": "9.1.0",
|
"eslint-config-prettier": "9.1.0",
|
||||||
"eslint-plugin-file-progress": "^1.3.0",
|
"eslint-plugin-file-progress": "^1.3.0",
|
||||||
|
@ -69,9 +69,9 @@
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"prismjs": "1.29.0",
|
"prismjs": "1.29.0",
|
||||||
"punycode": "2.3.1",
|
"punycode": "2.3.1",
|
||||||
"rollup": "4.12.0",
|
"rollup": "4.13.0",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"sass": "1.71.1",
|
"sass": "1.72.0",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"swiper": "11.0.7",
|
"swiper": "11.0.7",
|
||||||
|
@ -81,10 +81,10 @@
|
||||||
"throttle-debounce": "5.0.0",
|
"throttle-debounce": "5.0.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tinyld": "^1.3.4",
|
"tinyld": "^1.3.4",
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.4.2",
|
||||||
"unicode-emoji-json": "^0.4.0",
|
"unicode-emoji-json": "^0.4.0",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"vite": "5.1.5",
|
"vite": "5.1.6",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vue": "3.4.21",
|
"vue": "3.4.21",
|
||||||
"vue-draggable-plus": "^0.3.5",
|
"vue-draggable-plus": "^0.3.5",
|
||||||
|
|
|
@ -2,9 +2,8 @@ import type { entities } from "firefish-js";
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
import { i18n } from "./i18n";
|
import { i18n } from "./i18n";
|
||||||
import { apiUrl } from "@/config";
|
import { apiUrl } from "@/config";
|
||||||
|
import { me } from "@/me";
|
||||||
import { alert, api, popup, popupMenu, waiting } from "@/os";
|
import { alert, api, popup, popupMenu, waiting } from "@/os";
|
||||||
import { $i } from "@/reactiveAccount";
|
|
||||||
import icon from "@/scripts/icon";
|
|
||||||
import { del, get, set } from "@/scripts/idb-proxy";
|
import { del, get, set } from "@/scripts/idb-proxy";
|
||||||
import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
|
import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
|
||||||
|
|
||||||
|
@ -12,11 +11,11 @@ import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
|
||||||
|
|
||||||
export type Account = entities.MeDetailed;
|
export type Account = entities.MeDetailed;
|
||||||
|
|
||||||
export async function signout() {
|
export async function signOut() {
|
||||||
waiting();
|
waiting();
|
||||||
localStorage.removeItem("account");
|
localStorage.removeItem("account");
|
||||||
|
|
||||||
await removeAccount($i.id);
|
await removeAccount(me.id);
|
||||||
|
|
||||||
const accounts = await getAccounts();
|
const accounts = await getAccounts();
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ export async function signout() {
|
||||||
await fetch(`${apiUrl}/sw/unregister`, {
|
await fetch(`${apiUrl}/sw/unregister`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
i: $i.token,
|
i: me.token,
|
||||||
endpoint: push.endpoint,
|
endpoint: push.endpoint,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -48,7 +47,7 @@ export async function signout() {
|
||||||
|
|
||||||
document.cookie = "igi=; path=/";
|
document.cookie = "igi=; path=/";
|
||||||
|
|
||||||
if (accounts.length > 0) login(accounts[0].token);
|
if (accounts.length > 0) signIn(accounts[0].token);
|
||||||
else unisonReload("/");
|
else unisonReload("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +89,7 @@ function fetchAccount(token: string): Promise<Account> {
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
if (res.error.id === "a8c724b3-6e9c-4b46-b1a8-bc3ed6258370") {
|
if (res.error.id === "a8c724b3-6e9c-4b46-b1a8-bc3ed6258370") {
|
||||||
showSuspendedDialog();
|
showSuspendedDialog();
|
||||||
signout();
|
signOut();
|
||||||
} else {
|
} else {
|
||||||
alert({
|
alert({
|
||||||
type: "error",
|
type: "error",
|
||||||
|
@ -117,22 +116,23 @@ function showSuspendedDialog() {
|
||||||
|
|
||||||
export function updateAccount(accountData) {
|
export function updateAccount(accountData) {
|
||||||
for (const [key, value] of Object.entries(accountData)) {
|
for (const [key, value] of Object.entries(accountData)) {
|
||||||
$i[key] = value;
|
me[key] = value;
|
||||||
}
|
}
|
||||||
localStorage.setItem("account", JSON.stringify($i));
|
localStorage.setItem("account", JSON.stringify(me));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshAccount() {
|
export async function refreshAccount() {
|
||||||
return fetchAccount($i.token).then(updateAccount);
|
const accountData = await fetchAccount(me.token);
|
||||||
|
return updateAccount(accountData);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login(token: Account["token"], redirect?: string) {
|
export async function signIn(token: Account["token"], redirect?: string) {
|
||||||
waiting();
|
waiting();
|
||||||
if (_DEV_) console.log("logging as token ", token);
|
if (_DEV_) console.log("logging as token ", token);
|
||||||
const me = await fetchAccount(token);
|
const newAccount = await fetchAccount(token);
|
||||||
localStorage.setItem("account", JSON.stringify(me));
|
localStorage.setItem("account", JSON.stringify(newAccount));
|
||||||
document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う
|
document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う
|
||||||
await addAccount(me.id, token);
|
await addAccount(newAccount.id, token);
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
// 他のタブは再読み込みするだけ
|
// 他のタブは再読み込みするだけ
|
||||||
|
@ -190,11 +190,11 @@ export async function openAccountMenu(
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchAccountWithToken(token: string) {
|
function switchAccountWithToken(token: string) {
|
||||||
login(token);
|
signIn(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
const storedAccounts = await getAccounts().then((accounts) =>
|
const storedAccounts = await getAccounts().then((accounts) =>
|
||||||
accounts.filter((x) => x.id !== $i.id),
|
accounts.filter((x) => x.id !== me.id),
|
||||||
);
|
);
|
||||||
const accountsPromise = api("users/show", {
|
const accountsPromise = api("users/show", {
|
||||||
userIds: storedAccounts.map((x) => x.id),
|
userIds: storedAccounts.map((x) => x.id),
|
||||||
|
@ -256,12 +256,12 @@ export async function openAccountMenu(
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
text: i18n.ts.profile,
|
text: i18n.ts.profile,
|
||||||
to: `/@${$i.username}`,
|
to: `/@${me.username}`,
|
||||||
avatar: $i,
|
avatar: me,
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
]),
|
]),
|
||||||
...(opts.includeCurrentAccount ? [createItem($i)] : []),
|
...(opts.includeCurrentAccount ? [createItem(me)] : []),
|
||||||
...accountItemPromises,
|
...accountItemPromises,
|
||||||
...(isMobile ?? false
|
...(isMobile ?? false
|
||||||
? [
|
? [
|
||||||
|
@ -269,8 +269,8 @@ export async function openAccountMenu(
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
text: i18n.ts.profile,
|
text: i18n.ts.profile,
|
||||||
to: `/@${$i.username}`,
|
to: `/@${me.username}`,
|
||||||
avatar: $i,
|
avatar: me,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
|
@ -304,7 +304,7 @@ export async function openAccountMenu(
|
||||||
} else {
|
} else {
|
||||||
popupMenu(
|
popupMenu(
|
||||||
[
|
[
|
||||||
...(opts.includeCurrentAccount ? [createItem($i)] : []),
|
...(opts.includeCurrentAccount ? [createItem(me)] : []),
|
||||||
...accountItemPromises,
|
...accountItemPromises,
|
||||||
],
|
],
|
||||||
ev.currentTarget ?? ev.target,
|
ev.currentTarget ?? ev.target,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
:class="{
|
:class="{
|
||||||
isMe: isMe(message),
|
isMe: isMe(message),
|
||||||
isRead: message.groupId
|
isRead: message.groupId
|
||||||
? message.reads.includes($i?.id)
|
? message.reads.includes(me?.id)
|
||||||
: message.isRead,
|
: message.isRead,
|
||||||
}"
|
}"
|
||||||
:to="
|
:to="
|
||||||
|
@ -67,14 +67,14 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { acct } from "firefish-js";
|
import { acct } from "firefish-js";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
message: Record<string, any>;
|
message: Record<string, any>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function isMe(message): boolean {
|
function isMe(message): boolean {
|
||||||
return message.userId === $i?.id;
|
return message.userId === me?.id;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ import Cropper from "cropperjs";
|
||||||
import tinycolor from "tinycolor2";
|
import tinycolor from "tinycolor2";
|
||||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import { apiUrl, url } from "@/config";
|
import { apiUrl, url } from "@/config";
|
||||||
import { query } from "@/scripts/url";
|
import { query } from "@/scripts/url";
|
||||||
|
@ -81,7 +81,7 @@ const ok = async () => {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${$i.token}`,
|
authorization: `Bearer ${me.token}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.text">
|
<div :class="$style.text">
|
||||||
{{ i18n.ts._aboutFirefish.pleaseDonateToFirefish }}
|
{{ i18n.ts._aboutFirefish.pleaseDonateToFirefish }}
|
||||||
<p v-if="$instance.donationLink">
|
<p v-if="instance.donationLink">
|
||||||
{{
|
{{
|
||||||
i18n.t("_aboutFirefish.pleaseDonateToHost", {
|
i18n.t("_aboutFirefish.pleaseDonateToHost", {
|
||||||
host: hostname,
|
host: hostname,
|
||||||
|
@ -27,9 +27,9 @@
|
||||||
>{{ i18n.ts._aboutFirefish.donate }}</MkButton
|
>{{ i18n.ts._aboutFirefish.donate }}</MkButton
|
||||||
>
|
>
|
||||||
<MkButton
|
<MkButton
|
||||||
v-if="$instance.donationLink"
|
v-if="instance.donationLink"
|
||||||
gradate
|
gradate
|
||||||
@click="openExternal($instance.donationLink)"
|
@click="openExternal(instance.donationLink)"
|
||||||
>{{
|
>{{
|
||||||
i18n.t("_aboutFirefish.donateHost", {
|
i18n.t("_aboutFirefish.donateHost", {
|
||||||
host: hostname,
|
host: hostname,
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
@dragstart="onDragstart"
|
@dragstart="onDragstart"
|
||||||
@dragend="onDragend"
|
@dragend="onDragend"
|
||||||
>
|
>
|
||||||
<div v-if="$i?.avatarId == file.id" class="label">
|
<div v-if="me?.avatarId == file.id" class="label">
|
||||||
<img src="/client-assets/label.svg" />
|
<img src="/client-assets/label.svg" />
|
||||||
<p>{{ i18n.ts.avatar }}</p>
|
<p>{{ i18n.ts.avatar }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="$i?.bannerId == file.id" class="label">
|
<div v-if="me?.bannerId == file.id" class="label">
|
||||||
<img src="/client-assets/label.svg" />
|
<img src="/client-assets/label.svg" />
|
||||||
<p>{{ i18n.ts.banner }}</p>
|
<p>{{ i18n.ts.banner }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,7 +45,7 @@ import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
|
||||||
import bytes from "@/filters/bytes";
|
import bytes from "@/filters/bytes";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<i :class="icon('ph-dots-three-outline')"></i>
|
<i :class="icon('ph-dots-three-outline')"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="!hideFollowButton && isSignedIn && $i.id != user.id"
|
v-if="!hideFollowButton && isSignedIn && me.id != user.id"
|
||||||
v-tooltip="full ? null : `${state} ${user.name || user.username}`"
|
v-tooltip="full ? null : `${state} ${user.name || user.username}`"
|
||||||
class="kpoogebi _button follow-button"
|
class="kpoogebi _button follow-button"
|
||||||
:class="{
|
:class="{
|
||||||
|
@ -66,7 +66,7 @@ import type { entities } from "firefish-js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { useStream } from "@/stream";
|
import { useStream } from "@/stream";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import { getUserMenu } from "@/scripts/get-user-menu";
|
import { getUserMenu } from "@/scripts/get-user-menu";
|
||||||
import { useRouter } from "@/router";
|
import { useRouter } from "@/router";
|
||||||
import { vibrate } from "@/scripts/vibrate";
|
import { vibrate } from "@/scripts/vibrate";
|
||||||
|
|
|
@ -55,6 +55,7 @@ const commonNames = new Map<string, string>([
|
||||||
["gnusocial", "GNU social"],
|
["gnusocial", "GNU social"],
|
||||||
["gotosocial", "GoToSocial"],
|
["gotosocial", "GoToSocial"],
|
||||||
["kbin", "/kbin"],
|
["kbin", "/kbin"],
|
||||||
|
["kmyblue", "kmyblue"],
|
||||||
["microblogpub", "microblog.pub"],
|
["microblogpub", "microblog.pub"],
|
||||||
["nextcloud social", "Nextcloud Social"],
|
["nextcloud social", "Nextcloud Social"],
|
||||||
["peertube", "PeerTube"],
|
["peertube", "PeerTube"],
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toUnicode } from "punycode";
|
import { toUnicode } from "punycode";
|
||||||
import { host as localHost } from "@/config";
|
import { host as localHost } from "@/config";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -53,8 +53,8 @@ const url = `/${canonical}`;
|
||||||
|
|
||||||
const isMe =
|
const isMe =
|
||||||
isSignedIn &&
|
isSignedIn &&
|
||||||
`@${props.username}@${toUnicode(props.host)}` ===
|
`@${props.username}@${toUnicode(props.host)}`.toLowerCase() ===
|
||||||
`@${$i.username}@${toUnicode(localHost)}`.toLowerCase();
|
`@${me.username}@${toUnicode(localHost)}`.toLowerCase();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -127,7 +127,7 @@
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="translation.text"
|
:text="translation.text"
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:i="$i"
|
:i="me"
|
||||||
:lang="targetLang"
|
:lang="targetLang"
|
||||||
:custom-emojis="appearNote.emojis"
|
:custom-emojis="appearNote.emojis"
|
||||||
/>
|
/>
|
||||||
|
@ -296,7 +296,7 @@ import { userPage } from "@/filters/user";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { defaultStore, noteViewInterruptors } from "@/store";
|
import { defaultStore, noteViewInterruptors } from "@/store";
|
||||||
import { reactionPicker } from "@/scripts/reaction-picker";
|
import { reactionPicker } from "@/scripts/reaction-picker";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { getNoteMenu } from "@/scripts/get-note-menu";
|
import { getNoteMenu } from "@/scripts/get-note-menu";
|
||||||
import { useNoteCapture } from "@/scripts/use-note-capture";
|
import { useNoteCapture } from "@/scripts/use-note-capture";
|
||||||
|
@ -355,13 +355,13 @@ const reactButton = ref<HTMLElement>();
|
||||||
const appearNote = computed(() =>
|
const appearNote = computed(() =>
|
||||||
isRenote ? (note.value.renote as entities.Note) : note.value,
|
isRenote ? (note.value.renote as entities.Note) : note.value,
|
||||||
);
|
);
|
||||||
const isMyRenote = isSignedIn && $i.id === note.value.userId;
|
const isMyRenote = isSignedIn && me.id === note.value.userId;
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(
|
const muted = ref(
|
||||||
getWordSoftMute(
|
getWordSoftMute(
|
||||||
note.value,
|
note.value,
|
||||||
$i?.id,
|
me?.id,
|
||||||
defaultStore.state.mutedWords,
|
defaultStore.state.mutedWords,
|
||||||
defaultStore.state.mutedLangs,
|
defaultStore.state.mutedLangs,
|
||||||
),
|
),
|
||||||
|
@ -632,9 +632,7 @@ function setPostExpanded(val: boolean) {
|
||||||
const accessibleLabel = computed(() => {
|
const accessibleLabel = computed(() => {
|
||||||
let label = `${appearNote.value.user.username}; `;
|
let label = `${appearNote.value.user.username}; `;
|
||||||
if (appearNote.value.renote) {
|
if (appearNote.value.renote) {
|
||||||
label += `${i18n.t("renoted")} ${
|
label += `${i18n.t("renoted")} ${appearNote.value.renote.user.username}; `;
|
||||||
appearNote.value.renote.user.username
|
|
||||||
}; `;
|
|
||||||
if (appearNote.value.renote.cw) {
|
if (appearNote.value.renote.cw) {
|
||||||
label += `${i18n.t("cw")}: ${appearNote.value.renote.cw}; `;
|
label += `${i18n.t("cw")}: ${appearNote.value.renote.cw}; `;
|
||||||
if (postIsExpanded.value) {
|
if (postIsExpanded.value) {
|
||||||
|
|
|
@ -180,7 +180,7 @@ import { userPage } from "@/filters/user";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { defaultStore, noteViewInterruptors } from "@/store";
|
import { defaultStore, noteViewInterruptors } from "@/store";
|
||||||
import { reactionPicker } from "@/scripts/reaction-picker";
|
import { reactionPicker } from "@/scripts/reaction-picker";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { getNoteMenu } from "@/scripts/get-note-menu";
|
import { getNoteMenu } from "@/scripts/get-note-menu";
|
||||||
import { useNoteCapture } from "@/scripts/use-note-capture";
|
import { useNoteCapture } from "@/scripts/use-note-capture";
|
||||||
|
@ -235,7 +235,7 @@ const isDeleted = ref(false);
|
||||||
const muted = ref(
|
const muted = ref(
|
||||||
getWordSoftMute(
|
getWordSoftMute(
|
||||||
note.value,
|
note.value,
|
||||||
$i?.id,
|
me?.id,
|
||||||
defaultStore.state.mutedWords,
|
defaultStore.state.mutedWords,
|
||||||
defaultStore.state.mutedLangs,
|
defaultStore.state.mutedLangs,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-size="{ min: [350, 500] }" class="fefdfafb">
|
<div v-size="{ min: [350, 500] }" class="fefdfafb">
|
||||||
<MkAvatar class="avatar" :user="$i" disable-link />
|
<MkAvatar class="avatar" :user="me" disable-link />
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<MkUserName :user="$i" />
|
<MkUserName :user="me" />
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="preprocess(text).trim()"
|
:text="preprocess(text).trim()"
|
||||||
:lang="lang"
|
:lang="lang"
|
||||||
:author="$i"
|
:author="me"
|
||||||
:i="$i"
|
:i="me"
|
||||||
advanced-mfm
|
advanced-mfm
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<Mfm
|
<Mfm
|
||||||
:text="translation.text"
|
:text="translation.text"
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:i="$i"
|
:i="me"
|
||||||
:lang="targetLang"
|
:lang="targetLang"
|
||||||
:custom-emojis="appearNote.emojis"
|
:custom-emojis="appearNote.emojis"
|
||||||
/>
|
/>
|
||||||
|
@ -211,7 +211,7 @@ import { useRouter } from "@/router";
|
||||||
import { userPage } from "@/filters/user";
|
import { userPage } from "@/filters/user";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { reactionPicker } from "@/scripts/reaction-picker";
|
import { reactionPicker } from "@/scripts/reaction-picker";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { useNoteCapture } from "@/scripts/use-note-capture";
|
import { useNoteCapture } from "@/scripts/use-note-capture";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -269,7 +269,7 @@ const isDeleted = ref(false);
|
||||||
const muted = ref(
|
const muted = ref(
|
||||||
getWordSoftMute(
|
getWordSoftMute(
|
||||||
note.value,
|
note.value,
|
||||||
$i?.id,
|
me?.id,
|
||||||
defaultStore.state.mutedWords,
|
defaultStore.state.mutedWords,
|
||||||
defaultStore.state.mutedLangs,
|
defaultStore.state.mutedLangs,
|
||||||
),
|
),
|
||||||
|
|
|
@ -54,7 +54,7 @@ import XNotification from "@/components/MkNotification.vue";
|
||||||
import XList from "@/components/MkDateSeparatedList.vue";
|
import XList from "@/components/MkDateSeparatedList.vue";
|
||||||
import XNote from "@/components/MkNote.vue";
|
import XNote from "@/components/MkNote.vue";
|
||||||
import { useStream } from "@/stream";
|
import { useStream } from "@/stream";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -73,7 +73,7 @@ const pagination: Paging = {
|
||||||
includeTypes: props.includeTypes ?? undefined,
|
includeTypes: props.includeTypes ?? undefined,
|
||||||
excludeTypes: props.includeTypes
|
excludeTypes: props.includeTypes
|
||||||
? undefined
|
? undefined
|
||||||
: $i.mutingNotificationTypes,
|
: me.mutingNotificationTypes,
|
||||||
unreadOnly: props.unreadOnly,
|
unreadOnly: props.unreadOnly,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
@ -81,7 +81,7 @@ const pagination: Paging = {
|
||||||
const onNotification = (notification) => {
|
const onNotification = (notification) => {
|
||||||
const isMuted = props.includeTypes
|
const isMuted = props.includeTypes
|
||||||
? !props.includeTypes.includes(notification.type)
|
? !props.includeTypes.includes(notification.type)
|
||||||
: $i.mutingNotificationTypes.includes(notification.type);
|
: me.mutingNotificationTypes.includes(notification.type);
|
||||||
if (isMuted || document.visibilityState === "visible") {
|
if (isMuted || document.visibilityState === "visible") {
|
||||||
stream.send("readNotification", {
|
stream.send("readNotification", {
|
||||||
id: notification.id,
|
id: notification.id,
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
class="account _button"
|
class="account _button"
|
||||||
@click="openAccountMenu"
|
@click="openAccountMenu"
|
||||||
>
|
>
|
||||||
<MkAvatar :user="postAccount ?? $i" class="avatar" />
|
<MkAvatar :user="postAccount ?? me" class="avatar" />
|
||||||
</button>
|
</button>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<span
|
<span
|
||||||
|
@ -322,7 +322,7 @@ import MkInfo from "@/components/MkInfo.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account";
|
import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
import { uploadFile } from "@/scripts/upload";
|
import { uploadFile } from "@/scripts/upload";
|
||||||
import { deepClone } from "@/scripts/clone";
|
import { deepClone } from "@/scripts/clone";
|
||||||
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
|
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
|
||||||
|
@ -517,7 +517,7 @@ if (props.mention) {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
props.reply &&
|
props.reply &&
|
||||||
(props.reply.user.username !== $i.username ||
|
(props.reply.user.username !== me.username ||
|
||||||
(props.reply.user.host != null && props.reply.user.host !== host))
|
(props.reply.user.host != null && props.reply.user.host !== host))
|
||||||
) {
|
) {
|
||||||
text.value = `@${props.reply.user.username}${
|
text.value = `@${props.reply.user.username}${
|
||||||
|
@ -539,7 +539,7 @@ if (props.reply && props.reply.text != null) {
|
||||||
: `@${x.username}@${toASCII(otherHost)}`;
|
: `@${x.username}@${toASCII(otherHost)}`;
|
||||||
|
|
||||||
// exclude me
|
// exclude me
|
||||||
if ($i.username === x.username && (x.host == null || x.host === host))
|
if (me.username === x.username && (x.host == null || x.host === host))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// remove duplicates
|
// remove duplicates
|
||||||
|
@ -573,7 +573,7 @@ if (
|
||||||
if (props.reply.visibleUserIds) {
|
if (props.reply.visibleUserIds) {
|
||||||
os.api("users/show", {
|
os.api("users/show", {
|
||||||
userIds: props.reply.visibleUserIds.filter(
|
userIds: props.reply.visibleUserIds.filter(
|
||||||
(uid) => uid !== $i.id && uid !== props.reply.userId,
|
(uid) => uid !== me.id && uid !== props.reply.userId,
|
||||||
),
|
),
|
||||||
}).then((users) => {
|
}).then((users) => {
|
||||||
users.forEach(pushVisibleUser);
|
users.forEach(pushVisibleUser);
|
||||||
|
@ -582,7 +582,7 @@ if (
|
||||||
visibility.value = "private";
|
visibility.value = "private";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.reply.userId !== $i.id) {
|
if (props.reply.userId !== me.id) {
|
||||||
os.api("users/show", { userId: props.reply.userId }).then(
|
os.api("users/show", { userId: props.reply.userId }).then(
|
||||||
(user) => {
|
(user) => {
|
||||||
pushVisibleUser(user);
|
pushVisibleUser(user);
|
||||||
|
@ -611,7 +611,7 @@ const addRe = (s: string) => {
|
||||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||||
useCw.value = true;
|
useCw.value = true;
|
||||||
cw.value =
|
cw.value =
|
||||||
props.reply.user.username === $i.username
|
props.reply.user.username === me.username
|
||||||
? props.reply.cw
|
? props.reply.cw
|
||||||
: addRe(props.reply.cw);
|
: addRe(props.reply.cw);
|
||||||
}
|
}
|
||||||
|
@ -1194,9 +1194,9 @@ function openAccountMenu(ev: MouseEvent) {
|
||||||
{
|
{
|
||||||
withExtraOperation: false,
|
withExtraOperation: false,
|
||||||
includeCurrentAccount: true,
|
includeCurrentAccount: true,
|
||||||
active: postAccount.value != null ? postAccount.value.id : $i.id,
|
active: postAccount.value != null ? postAccount.value.id : me.id,
|
||||||
onChoose: (account) => {
|
onChoose: (account) => {
|
||||||
if (account.id === $i.id) {
|
if (account.id === me.id) {
|
||||||
postAccount.value = null;
|
postAccount.value = null;
|
||||||
} else {
|
} else {
|
||||||
postAccount.value = account;
|
postAccount.value = account;
|
||||||
|
|
|
@ -45,7 +45,6 @@ const isRefreshing = ref(false);
|
||||||
const pullDistance = ref(0);
|
const pullDistance = ref(0);
|
||||||
|
|
||||||
let disabled = false;
|
let disabled = false;
|
||||||
|
|
||||||
let supportPointerDesktop = false;
|
let supportPointerDesktop = false;
|
||||||
let startScreenY: number | null = null;
|
let startScreenY: number | null = null;
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
import { getAccounts } from "@/account";
|
import { getAccounts } from "@/account";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import { api, apiWithDialog, promiseDialog } from "@/os";
|
import { api, apiWithDialog, promiseDialog } from "@/os";
|
||||||
|
@ -149,7 +149,7 @@ async function unsubscribe() {
|
||||||
|
|
||||||
if (isSignedIn && accounts.length >= 2) {
|
if (isSignedIn && accounts.length >= 2) {
|
||||||
apiWithDialog("sw/unregister", {
|
apiWithDialog("sw/unregister", {
|
||||||
i: $i.token,
|
i: me.token,
|
||||||
endpoint,
|
endpoint,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -197,7 +197,7 @@ if (navigator.serviceWorker == null) {
|
||||||
instance.swPublickey &&
|
instance.swPublickey &&
|
||||||
"PushManager" in window &&
|
"PushManager" in window &&
|
||||||
isSignedIn &&
|
isSignedIn &&
|
||||||
$i.token
|
me.token
|
||||||
) {
|
) {
|
||||||
supported.value = true;
|
supported.value = true;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { computed } from "vue";
|
||||||
import type { entities } from "firefish-js";
|
import type { entities } from "firefish-js";
|
||||||
import { pleaseLogin } from "@/scripts/please-login";
|
import { pleaseLogin } from "@/scripts/please-login";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { $i } from "@/reactiveAccount";
|
import { me } from "@/me";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
@ -26,7 +26,7 @@ const props = defineProps<{
|
||||||
const canRenote = computed(
|
const canRenote = computed(
|
||||||
() =>
|
() =>
|
||||||
["public", "home"].includes(props.note.visibility) ||
|
["public", "home"].includes(props.note.visibility) ||
|
||||||
props.note.userId === $i?.id,
|
props.note.userId === me?.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
function quote(): void {
|
function quote(): void {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import XDetails from "@/components/MkReactionsViewer.details.vue";
|
||||||
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
import XReactionIcon from "@/components/MkReactionIcon.vue";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { useTooltip } from "@/scripts/use-tooltip";
|
import { useTooltip } from "@/scripts/use-tooltip";
|
||||||
import { isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn } from "@/me";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
reaction: string;
|
reaction: string;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import type { entities } from "firefish-js";
|
import type { entities } from "firefish-js";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import XReaction from "@/components/MkReactionsViewer.reaction.vue";
|
import XReaction from "@/components/MkReactionsViewer.reaction.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -30,7 +30,7 @@ const reactionsEl = ref<HTMLElement>();
|
||||||
|
|
||||||
const initialReactions = new Set(Object.keys(props.note.reactions));
|
const initialReactions = new Set(Object.keys(props.note.reactions));
|
||||||
|
|
||||||
const isMe = computed(() => isSignedIn && $i.id === props.note.userId);
|
const isMe = computed(() => isSignedIn && me.id === props.note.userId);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -27,7 +27,7 @@ import Ripple from "@/components/MkRipple.vue";
|
||||||
import XDetails from "@/components/MkUsersTooltip.vue";
|
import XDetails from "@/components/MkUsersTooltip.vue";
|
||||||
import { pleaseLogin } from "@/scripts/please-login";
|
import { pleaseLogin } from "@/scripts/please-login";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { $i, isSignedIn } from "@/reactiveAccount";
|
import { isSignedIn, me } from "@/me";
|
||||||
import { useTooltip } from "@/scripts/use-tooltip";
|
import { useTooltip } from "@/scripts/use-tooltip";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
@ -46,7 +46,7 @@ const buttonRef = ref<HTMLElement>();
|
||||||
const canRenote = computed(
|
const canRenote = computed(
|
||||||
() =>
|
() =>
|
||||||
["public", "home"].includes(props.note.visibility) ||
|
["public", "home"].includes(props.note.visibility) ||
|
||||||
props.note.userId === $i.id,
|
props.note.userId === me.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
useTooltip(buttonRef, async (showing) => {
|
useTooltip(buttonRef, async (showing) => {
|
||||||
|
@ -77,7 +77,7 @@ const hasRenotedBefore = ref(false);
|
||||||
if (isSignedIn) {
|
if (isSignedIn) {
|
||||||
os.api("notes/renotes", {
|
os.api("notes/renotes", {
|
||||||
noteId: props.note.id,
|
noteId: props.note.id,
|
||||||
userId: $i.id,
|
userId: me.id,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
hasRenotedBefore.value = res.length > 0;
|
hasRenotedBefore.value = res.length > 0;
|
||||||
|
|
|
@ -52,7 +52,7 @@ export default defineComponent({
|
||||||
flag: true,
|
flag: true,
|
||||||
radio: "firefish",
|
radio: "firefish",
|
||||||
mfm: `Hello world! This is an @example mention. BTW, you are @${
|
mfm: `Hello world! This is an @example mention. BTW, you are @${
|
||||||
this.$i ? this.$i.username : "guest"
|
this.me ? this.me.username : "guest"
|
||||||
}.\nAlso, here is ${config.url} and [example link](${
|
}.\nAlso, here is ${config.url} and [example link](${
|
||||||
config.url
|
config.url
|
||||||
}). for more details, see <https://firefish.dev/firefish/firefish>.\nAs you know #Firefish is open-source software.`,
|
}). for more details, see <https://firefish.dev/firefish/firefish>.\nAs you know #Firefish is open-source software.`,
|
||||||
|
|
|
@ -130,9 +130,7 @@ const searchUsers = ref(
|
||||||
);
|
);
|
||||||
const searchRange = ref(
|
const searchRange = ref(
|
||||||
searchParams.has("since") || searchParams.has("until")
|
searchParams.has("since") || searchParams.has("until")
|
||||||
? `${searchParams.get("since") ?? ""}-${
|
? `${searchParams.get("since") ?? ""}-${searchParams.get("until") ?? ""}`
|
||||||
searchParams.get("until") ?? ""
|
|
||||||
}`
|
|
||||||
: "",
|
: "",
|
||||||
);
|
);
|
||||||
const searchPostsWithFiles = ref(searchParams.get("withFiles") === "1");
|
const searchPostsWithFiles = ref(searchParams.get("withFiles") === "1");
|
||||||
|
|
|
@ -142,7 +142,7 @@ import MkInfo from "@/components/MkInfo.vue";
|
||||||
import { host as configHost } from "@/config";
|
import { host as configHost } from "@/config";
|
||||||
import { byteify, hexify } from "@/scripts/2fa";
|
import { byteify, hexify } from "@/scripts/2fa";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { login } from "@/account";
|
import { signIn } from "@/account";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ function onUsernameChange() {
|
||||||
|
|
||||||
function onLogin(res) {
|
function onLogin(res) {
|
||||||
if (props.autoSet) {
|
if (props.autoSet) {
|
||||||
return login(res.i);
|
return signIn(res.i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,7 +281,7 @@ import MkSwitch from "./form/switch.vue";
|
||||||
import MkCaptcha from "@/components/MkCaptcha.vue";
|
import MkCaptcha from "@/components/MkCaptcha.vue";
|
||||||
import * as config from "@/config";
|
import * as config from "@/config";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { login } from "@/account";
|
import { signIn } from "@/account";
|
||||||
import { instance } from "@/instance";
|
import { instance } from "@/instance";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
@ -470,7 +470,7 @@ function onSubmit(): void {
|
||||||
emit("signup", res);
|
emit("signup", res);
|
||||||
|
|
||||||
if (props.autoSet) {
|
if (props.autoSet) {
|
||||||
login(res.i);
|
signIn(res.i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue