Firefish v1.0.5-dev6
This commit is contained in:
parent
f2215e511d
commit
d52e4b3c6a
313 changed files with 5296 additions and 3678 deletions
|
@ -161,6 +161,9 @@ reservedUsernames: [
|
|||
# deliverJobMaxAttempts: 12
|
||||
# inboxJobMaxAttempts: 8
|
||||
|
||||
# Local address used for outgoing requests
|
||||
#outgoingAddress: 127.0.0.1
|
||||
|
||||
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||
#outgoingAddressFamily: ipv4
|
||||
|
||||
|
|
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"editorconfig.editorconfig",
|
||||
"eg2.vscode-npm-script",
|
||||
"rome.rome",
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin",
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
pipeline:
|
||||
testCommit:
|
||||
image: node:alpine
|
||||
commands:
|
||||
- apk add --no-cache cargo python3 make g++
|
||||
- cp .config/ci.yml .config/default.yml
|
||||
- corepack enable
|
||||
- corepack prepare pnpm@latest --activate
|
||||
- pnpm i --frozen-lockfile
|
||||
- pnpm run build
|
||||
- pnpm run migrate
|
||||
|
||||
services:
|
||||
database:
|
||||
image: postgres:15
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=test
|
||||
redis:
|
||||
image: redis
|
||||
|
||||
branches:
|
||||
include: [ main, beta, develop, feature/* ]
|
|
@ -1,15 +0,0 @@
|
|||
pipeline:
|
||||
publish-docker-latest:
|
||||
image: plugins/kaniko
|
||||
settings:
|
||||
repo: thatonecalculator/firefish
|
||||
tags: latest
|
||||
dockerfile: Dockerfile
|
||||
username:
|
||||
# Secret 'docker_username' needs to be set in the CI settings
|
||||
from_secret: docker_username
|
||||
password:
|
||||
# Secret 'docker_password' needs to be set in the CI settings
|
||||
from_secret: docker_password
|
||||
|
||||
branches: main
|
|
@ -1,14 +0,0 @@
|
|||
pipeline:
|
||||
publish-docker-latest:
|
||||
image: plugins/kaniko
|
||||
settings:
|
||||
repo: thatonecalculator/firefish
|
||||
tags: rc
|
||||
dockerfile: Dockerfile
|
||||
username:
|
||||
# Secret 'docker_username' needs to be set in the CI settings
|
||||
from_secret: docker_username
|
||||
password:
|
||||
# Secret 'docker_password' needs to be set in the CI settings
|
||||
from_secret: docker_password
|
||||
branches: beta
|
|
@ -1,18 +0,0 @@
|
|||
pipeline:
|
||||
publish-docker-tag:
|
||||
image: plugins/kaniko
|
||||
settings:
|
||||
repo: thatonecalculator/firefish
|
||||
# Uses the tag from git for the container tag
|
||||
tags: ${CI_COMMIT_TAG}
|
||||
dockerfile: Dockerfile
|
||||
username:
|
||||
# Secret 'docker_username' needs to be set in the CI settings
|
||||
from_secret: docker_username
|
||||
password:
|
||||
# Secret 'docker_password' needs to be set in the CI settings
|
||||
from_secret: docker_password
|
||||
when:
|
||||
# Push new version when version tag is created
|
||||
event: tag
|
||||
tag: v*
|
|
@ -1,11 +0,0 @@
|
|||
pipeline:
|
||||
docker-build:
|
||||
image: plugins/kaniko
|
||||
settings:
|
||||
repo: thatonecalculator/firefish
|
||||
tags: test
|
||||
dockerfile: Dockerfile
|
||||
no_push: true
|
||||
|
||||
branches:
|
||||
include: [ main, develop, beta ]
|
|
@ -8900,7 +8900,7 @@ Resolve #7540
|
|||
* truncate user information if it is too long
|
||||
|
||||
Some AP software allows for user names or summaries to be very long.
|
||||
Misskey can not handle this and the profile page can not be opened and
|
||||
Misskey cannot handle this and the profile page cannot be opened and
|
||||
no activities from such users can be seen.
|
||||
|
||||
Instead, the user name and summary are cut off after the maximum length
|
||||
|
@ -9902,7 +9902,7 @@ This duplicated processing can be avoided by querying the database directly.
|
|||
|
||||
Misskey will only use ActivityPub follow requests for users that are local
|
||||
and are requesting to follow a remote user. This check is to ensure that
|
||||
this endpoint can not be used by other services or instances.
|
||||
this endpoint cannot be used by other services or instances.
|
||||
|
||||
* fix: missing import
|
||||
|
||||
|
@ -14921,7 +14921,7 @@ Defaults for `local` and `withFiles` are based on the behaviour of the endpoint.
|
|||
|
||||
* fix: define required fields
|
||||
|
||||
- `notes/create`: the default for `text` has been removed because ajv can not handle
|
||||
- `notes/create`: the default for `text` has been removed because ajv cannot handle
|
||||
`default` inside of `anyOf`, see
|
||||
https://ajv.js.org/guide/modifying-data.html#assigning-defaults
|
||||
and the default value cannot be `null` if text is `nullable: false` in the `anyOf`
|
||||
|
@ -15551,7 +15551,7 @@ unnecessarily loaded.
|
|||
* remove duplicate null check
|
||||
|
||||
The variable is checked for null in the lines above and the function
|
||||
returns if so. Therefore, it can not be null at this point.
|
||||
returns if so. Therefore, it cannot be null at this point.
|
||||
|
||||
* simplify `getJsonSchema`
|
||||
|
||||
|
|
17
Dockerfile
17
Dockerfile
|
@ -1,9 +1,14 @@
|
|||
## Install dev and compilation dependencies, build files
|
||||
FROM alpine:3.18 as build
|
||||
FROM node:latest as build
|
||||
WORKDIR /firefish
|
||||
|
||||
# Install compilation dependencies
|
||||
RUN apk add --no-cache --no-progress git alpine-sdk python3 nodejs-current npm rust cargo vips
|
||||
RUN apt-get update && apt-get install -y libvips42 python3 git wget curl build-essential
|
||||
RUN mkdir -m777 /opt/rust /opt/cargo
|
||||
ENV RUSTUP_HOME=/opt/rust CARGO_HOME=/opt/cargo PATH=/opt/cargo/bin:$PATH
|
||||
RUN wget --https-only --secure-protocol=TLSv1_2 -O- https://sh.rustup.rs | sh /dev/stdin -y
|
||||
RUN printf '#!/bin/sh\nexport CARGO_HOME=/opt/cargo\nexec /bin/sh "$@"\n' >/usr/local/bin/sh
|
||||
RUN chmod +x /usr/local/bin/sh
|
||||
|
||||
# Copy only the cargo dependency-related files first, to cache efficiently
|
||||
COPY packages/backend/native-utils/Cargo.toml packages/backend/native-utils/Cargo.toml
|
||||
|
@ -26,7 +31,7 @@ COPY packages/backend/native-utils/package.json packages/backend/native-utils/pa
|
|||
COPY packages/backend/native-utils/npm/linux-x64-musl/package.json packages/backend/native-utils/npm/linux-x64-musl/package.json
|
||||
COPY packages/backend/native-utils/npm/linux-arm64-musl/package.json packages/backend/native-utils/npm/linux-arm64-musl/package.json
|
||||
|
||||
# Configure corepack and pnpm, and install dev mode dependencies for compilation
|
||||
# Configure pnpm, and install dev mode dependencies for compilation
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm i --frozen-lockfile
|
||||
|
||||
# Copy in the rest of the native-utils rust files
|
||||
|
@ -43,11 +48,11 @@ RUN env NODE_ENV=production sh -c "pnpm run --filter '!native-utils' build && pn
|
|||
RUN pnpm i --prod --frozen-lockfile
|
||||
|
||||
## Runtime container
|
||||
FROM alpine:3.18
|
||||
FROM node:latest
|
||||
WORKDIR /firefish
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache --no-progress tini ffmpeg vips-dev zip unzip nodejs-current
|
||||
RUN apt-get update && apt-get install -y libvips-dev zip unzip tini ffmpeg
|
||||
|
||||
COPY . ./
|
||||
|
||||
|
@ -69,5 +74,5 @@ COPY --from=build /firefish/packages/backend/native-utils/built /firefish/packag
|
|||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
ENV NODE_ENV=production
|
||||
VOLUME "/firefish/files"
|
||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
||||
ENTRYPOINT [ "/usr/bin/tini", "--" ]
|
||||
CMD [ "pnpm", "run", "migrateandstart" ]
|
||||
|
|
|
@ -103,4 +103,4 @@ NODE_ENV=production pnpm run migrate
|
|||
|
||||
## Reverse
|
||||
|
||||
You ***cannot*** migrate back to Misskey from Firefish due to re-hashing passwords on signin with argon2. You can migrate from Calckey to FoundKey, although this is not recommended due to FoundKey being end-of-life, and may have some problems with alt-text.
|
||||
You ***cannot*** migrate back to Misskey from Firefish due to re-hashing passwords on signin with argon2. You can migrate from Firefish to FoundKey, although this is not recommended due to FoundKey being end-of-life, and may have some problems with alt-text.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
_lang_: Български
|
||||
cancel: Отмяна
|
||||
cancel: Отмени
|
||||
noNotes: Няма публикации
|
||||
settings: Настройки
|
||||
headlineFirefish: Децентрализирана социална медийна платформа с отворен код, която
|
||||
|
@ -101,6 +101,10 @@ _filters:
|
|||
followersOnly: Само последователи
|
||||
notesAfter: Публикации след
|
||||
fromDomain: От домейн
|
||||
fromUser: От потребител
|
||||
withFile: С файл
|
||||
notesBefore: Публикации преди
|
||||
followingOnly: Само последвани
|
||||
_notification:
|
||||
_types:
|
||||
follow: Нови последователи
|
||||
|
@ -113,10 +117,10 @@ noLists: Нямаш никакви списъци
|
|||
markAsReadAllUnreadNotes: Маркирай всички публикации като прочетени
|
||||
markAsReadAllTalkMessages: Маркирай всички съобщения като прочетени
|
||||
_time:
|
||||
second: Секунд(а/и)
|
||||
hour: Час(а)
|
||||
second: Секунди
|
||||
hour: Часа
|
||||
day: Дни
|
||||
minute: Минут(а/и)
|
||||
minute: Минути
|
||||
create: Създай
|
||||
lists: Списъци
|
||||
reportAbuseOf: Докладвай {name}
|
||||
|
@ -134,9 +138,9 @@ rename: Преименувай
|
|||
customEmojis: Персонализирани емоджита
|
||||
emoji: Емоджи
|
||||
_aboutFirefish:
|
||||
translation: Преведи Calckey
|
||||
translation: Преведи Firefish
|
||||
translatedFrom: Преведено от {x}
|
||||
i18nInfo: Calckey се превежда на различни езици от доброволци. Можете да помогнете
|
||||
i18nInfo: Firefish се превежда на различни езици от доброволци. Можете да помогнете
|
||||
на адрес {link}.
|
||||
image: Изображение
|
||||
recipient: Получател(и)
|
||||
|
@ -222,11 +226,11 @@ _mfm:
|
|||
_messaging:
|
||||
groups: Групи
|
||||
apps: Приложения
|
||||
introFirefish: Добре дошли! Calckey е децентрализирана социална медийна платформа
|
||||
introFirefish: Добре дошли! Firefish е децентрализирана социална медийна платформа
|
||||
с отворен код, която е безплатна завинаги! 🚀
|
||||
monthAndDay: '{day}/{month}'
|
||||
search: Търсене
|
||||
searchPlaceholder: Търсене в Calckey
|
||||
searchPlaceholder: Търсене в Firefish
|
||||
username: Потребителско име
|
||||
password: Парола
|
||||
fetchingAsApObject: Извличане от федивърса
|
||||
|
@ -257,7 +261,7 @@ alreadyFavorited: Вече е добавено в отметки.
|
|||
cantFavorite: Неуспешно добавяне в отметки.
|
||||
copyContent: Копирай съдържанието
|
||||
deleteAndEdit: Изтрий и редактирай
|
||||
editNote: Редактирай бележка
|
||||
editNote: Редактирай публикация
|
||||
edited: Редактирано на {date} {time}
|
||||
addToList: Добави в списък
|
||||
sendMessage: Изпрати съобщение
|
||||
|
@ -422,3 +426,51 @@ _visibility:
|
|||
followers: Последователи
|
||||
explore: Разглеждане
|
||||
theme: Теми
|
||||
wallpaper: Тапет
|
||||
setWallpaper: Задай тапет
|
||||
removeWallpaper: Премахни тапет
|
||||
themeForLightMode: Тема за използване в светъл режим
|
||||
themeForDarkMode: Тема за използване в тъмен режим
|
||||
light: Светло
|
||||
dark: Тъмно
|
||||
darkThemes: Тъмни теми
|
||||
invitations: Покани
|
||||
invitationCode: Код на поканата
|
||||
checking: Проверка...
|
||||
available: Свободно
|
||||
unavailable: Не е свободно
|
||||
tooShort: Твърде кратко
|
||||
tooLong: Твърде дълго
|
||||
weakPassword: Слаба парола
|
||||
strongPassword: Силна парола
|
||||
passwordMatched: Съвпада
|
||||
passwordNotMatched: Не съвпада
|
||||
signinWith: Вписване с {x}
|
||||
aboutX: Относно {x}
|
||||
openInNewTab: Отвори в нов раздел
|
||||
_tutorial:
|
||||
step2_1: Първо, моля, попълнете своя профил.
|
||||
step2_2: Предоставянето на известна информация за това кой сте вие ще улесни другите
|
||||
да разберат дали искат да видят вашите публикации или да ви следват.
|
||||
title: Как се използва Firefish
|
||||
step1_1: Добре дошли!
|
||||
step5_1: Инфопотоци, инфопотоци навсякъде!
|
||||
step3_1: Сега е време да последвате няколко хора!
|
||||
step1_2: Нека да ви настроим. Ще бъдете готови за нула време!
|
||||
openImageInNewTab: Отваряй изображенията в нов раздел
|
||||
showOnRemote: Отвори оригиналната страница
|
||||
lightThemes: Светли теми
|
||||
syncDeviceDarkMode: Синхронизиране на тъмния режим с настройките на устройството
|
||||
text: Текст
|
||||
normalPassword: Средна парола
|
||||
usernameInvalidFormat: Можете да използвате главни и малки букви, цифри и долни черти.
|
||||
signinFailed: Неуспешно вписване. Въведените потребителско име или парола са неправилни.
|
||||
signinRequired: Моля, регистрирайте се или се впишете, преди да продължите
|
||||
start: Започни
|
||||
confirm: Потвърди
|
||||
failedToUpload: Неуспешно качване
|
||||
_preferencesBackups:
|
||||
cannotSave: Неуспешно запазване
|
||||
cannotLoad: Неуспешно зареждане
|
||||
editWidgetsExit: Готово
|
||||
done: Готово
|
||||
|
|
|
@ -2074,7 +2074,7 @@ _relayStatus:
|
|||
accepted: Acceptat
|
||||
rejected: Rebutjat
|
||||
deleted: Eliminat
|
||||
editNote: Edita la nota
|
||||
editNote: Edita la publicació
|
||||
edited: 'Editat a {date} {time}'
|
||||
findOtherInstance: Cercar un altre servidor
|
||||
signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades,
|
||||
|
@ -2182,3 +2182,7 @@ delete2fa: Desactivar 2FA
|
|||
delete2faConfirm: Això suprimirà irreversiblement 2FA en aquest compte. Procedir?
|
||||
addRe: Afegeix "re:" al començament del comentari quant responguis a un missatge amb
|
||||
avís de contingut
|
||||
confirm: Confirmar
|
||||
importZip: Importar ZIP
|
||||
exportZip: Exportar ZIP
|
||||
emojiPackCreator: Creador de paquets Emoji
|
||||
|
|
|
@ -77,7 +77,7 @@ lists: "Listen"
|
|||
noLists: "Du hast keine Listen angelegt"
|
||||
note: "Beitrag"
|
||||
notes: "Beiträge"
|
||||
following: "Folge ich"
|
||||
following: "Folgen"
|
||||
followers: "Folgen mir"
|
||||
followsYou: "Folgt dir"
|
||||
createList: "Liste erstellen"
|
||||
|
@ -474,7 +474,7 @@ invitations: "Einladungen"
|
|||
invitationCode: "Einladungscode"
|
||||
checking: "Wird überprüft …"
|
||||
available: "Verfügbar"
|
||||
unavailable: "Unverfügbar"
|
||||
unavailable: "Nicht verfügbar"
|
||||
usernameInvalidFormat: "Du kannst Klein- und Großbuchstaben, Zahlen sowie Unterstriche
|
||||
verwenden."
|
||||
tooShort: "Zu kurz"
|
||||
|
@ -1945,7 +1945,7 @@ _notification:
|
|||
renote: "Renote"
|
||||
voted: haben bei deiner Umfrage abgestimmt
|
||||
reacted: hat auf deinen Beitrag reagiert
|
||||
renoted: hat Ihren Beitrag geteilt
|
||||
renoted: hat deinen Beitrag geteilt
|
||||
_deck:
|
||||
alwaysShowMainColumn: "Hauptspalte immer zeigen"
|
||||
columnAlign: "Spaltenausrichtung"
|
||||
|
@ -2099,7 +2099,7 @@ customKaTeXMacro: Individuelle KaTeX Makros
|
|||
enableCustomKaTeXMacro: Individuelle KaTeX-Makros aktivieren
|
||||
replayTutorial: Wiederhole die Benutzeranleitung
|
||||
apps: Apps
|
||||
caption: Automatische Untertitelung
|
||||
caption: Automatische Beschreibung
|
||||
pwa: PWA installieren
|
||||
cw: Inhaltswarnung
|
||||
older: älter
|
||||
|
@ -2204,3 +2204,7 @@ deletePasskeysConfirm: Alle Passkeys und Security-Keys werden unwiderruflich von
|
|||
inputNotMatch: Eingabe stimmt nicht überein
|
||||
addRe: Ein "re:" am Anfang des Kommentars hinzufügen, um einem Beitrag mit einer Inhaltswarnung
|
||||
zu antworten
|
||||
confirm: Bestätigen
|
||||
importZip: ZIP Importieren
|
||||
emojiPackCreator: Emoji-Pack Ersteller
|
||||
exportZip: ZIP Exportieren
|
||||
|
|
|
@ -51,7 +51,7 @@ deleted: "Deleted"
|
|||
deleteAndEdit: "Delete and edit"
|
||||
deleteAndEditConfirm: "Are you sure you want to delete this post and edit it? You
|
||||
will lose all reactions, boosts and replies to it."
|
||||
editNote: "Edit note"
|
||||
editNote: "Edit post"
|
||||
edited: "Edited at {date} {time}"
|
||||
addToList: "Add to list"
|
||||
sendMessage: "Send a message"
|
||||
|
@ -337,11 +337,11 @@ emptyDrive: "Your Drive is empty"
|
|||
emptyFolder: "This folder is empty"
|
||||
unableToDelete: "Unable to delete"
|
||||
inputNewFileName: "Enter a new filename"
|
||||
inputNewDescription: "Enter new caption"
|
||||
inputNewDescription: "Enter new description"
|
||||
inputNewFolderName: "Enter a new folder name"
|
||||
circularReferenceFolder: "The destination folder is a subfolder of the folder you
|
||||
wish to move."
|
||||
hasChildFilesOrFolders: "Since this folder is not empty, it can not be deleted."
|
||||
hasChildFilesOrFolders: "Since this folder is not empty, it cannot be deleted."
|
||||
copyUrl: "Copy URL"
|
||||
rename: "Rename"
|
||||
avatar: "Avatar"
|
||||
|
@ -634,8 +634,8 @@ disablePlayer: "Close video player"
|
|||
expandTweet: "Expand tweet"
|
||||
themeEditor: "Theme editor"
|
||||
description: "Description"
|
||||
describeFile: "Add caption"
|
||||
enterFileDescription: "Enter caption"
|
||||
describeFile: "Add description"
|
||||
enterFileDescription: "Enter description"
|
||||
author: "Author"
|
||||
leaveConfirm: "There are unsaved changes. Do you want to discard them?"
|
||||
manage: "Management"
|
||||
|
@ -1054,7 +1054,7 @@ showUpdates: "Show a popup when Firefish updates"
|
|||
recommendedInstances: "Recommended servers"
|
||||
recommendedInstancesDescription: "Recommended servers separated by line breaks to
|
||||
appear in the recommended timeline."
|
||||
caption: "Auto Caption"
|
||||
caption: "Auto description"
|
||||
splash: "Splash Screen"
|
||||
updateAvailable: "There might be an update available!"
|
||||
swipeOnMobile: "Allow swiping between pages"
|
||||
|
@ -1147,6 +1147,9 @@ addRe: "Add \"re:\" at the beginning of comment in reply to a post with a conten
|
|||
showBigPostButton: "Show a bigger post button in the posting form"
|
||||
confirm: "Confirm"
|
||||
emphasizeFollowed: "Highlight the \"Follows you\" sign on your follower info"
|
||||
importZip: "Import ZIP"
|
||||
exportZip: "Export ZIP"
|
||||
emojiPackCreator: "Emoji pack creator"
|
||||
|
||||
_sensitiveMediaDetection:
|
||||
description: "Reduces the effort of server moderation through automatically recognizing
|
||||
|
|
|
@ -572,8 +572,8 @@ updateRemoteUser: "Actualizar información de usuario remoto"
|
|||
deleteAllFiles: "Borrar todos los archivos"
|
||||
deleteAllFilesConfirm: "¿Desea borrar todos los archivos?"
|
||||
removeAllFollowing: "Retener todos los siguientes"
|
||||
removeAllFollowingDescription: "Cancelar todos los siguientes del servidor {host}.
|
||||
Ejecutar en caso de que esta instancia haya dejado de existir."
|
||||
removeAllFollowingDescription: "Al ejecutar esto todas las cuentas de {host} dejarán
|
||||
de seguirse. Por favor, ejecuta esto si el servidor ya no existe."
|
||||
userSuspended: "Este usuario ha sido suspendido."
|
||||
userSilenced: "Este usuario ha sido silenciado."
|
||||
yourAccountSuspendedTitle: "Esta cuenta ha sido suspendida"
|
||||
|
@ -838,7 +838,7 @@ gallery: "Galería"
|
|||
recentPosts: "Posts recientes"
|
||||
popularPosts: "Más vistos"
|
||||
shareWithNote: "Compartir con una publicación"
|
||||
ads: "Banners"
|
||||
ads: "Banners de la comunidad"
|
||||
expiration: "Termina el"
|
||||
memo: "Notas"
|
||||
priority: "Prioridad"
|
||||
|
@ -892,7 +892,7 @@ unmuteThread: "Mostrar hilo"
|
|||
ffVisibility: "Visibilidad de seguidores y seguidos"
|
||||
ffVisibilityDescription: "Puedes configurar quien puede ver a quienes sigues y quienes
|
||||
te siguen"
|
||||
continueThread: "Ver la continuación del hilo"
|
||||
continueThread: "Continuar hilo"
|
||||
deleteAccountConfirm: "La cuenta será borrada irreversiblemente. ¿Está seguro?"
|
||||
incorrectPassword: "La contraseña es incorrecta"
|
||||
voteConfirm: "¿Confirma su voto a {choice}?"
|
||||
|
@ -906,12 +906,12 @@ overridedDeviceKind: "Tipo de dispositivo"
|
|||
smartphone: "Teléfono smartphone"
|
||||
tablet: "Tablet"
|
||||
auto: "Automático"
|
||||
themeColor: "Color del tema"
|
||||
themeColor: "Color de la marquesina del servidor"
|
||||
size: "Tamaño"
|
||||
numberOfColumn: "Cantidad de columnas"
|
||||
searchByGoogle: "Buscar"
|
||||
instanceDefaultLightTheme: "Tema claro por defecto de la instancia"
|
||||
instanceDefaultDarkTheme: "Tema oscuro por defecto de la instancia"
|
||||
instanceDefaultLightTheme: "Tema claro por defecto del servidor"
|
||||
instanceDefaultDarkTheme: "Tema oscuro por defecto del servidor"
|
||||
instanceDefaultThemeDescription: "Ingrese el código del tema en formato objeto"
|
||||
mutePeriod: "Período de silenciamiento"
|
||||
indefinitely: "Sin límite de tiempo"
|
||||
|
@ -968,7 +968,7 @@ beta: "Beta"
|
|||
enableAutoSensitive: "Marcar automáticamente contenido NSFW"
|
||||
enableAutoSensitiveDescription: "Permite la detección y marcado automático de contenido
|
||||
NSFW usando 'Machine Learning' cuando sea posible. Incluso si esta opción está desactivada,
|
||||
puede ser activado para toda la instancia."
|
||||
puede ser activado para todo el servidor."
|
||||
activeEmailValidationDescription: "Habilita la validación estricta de direcciones
|
||||
de correo electrónico, lo cual incluye la revisión de direcciones desechables y
|
||||
si se puede comunicar con éstas. Cuando está deshabilitado, sólo el formato de la
|
||||
|
@ -1085,6 +1085,7 @@ _aboutFirefish:
|
|||
pleaseDonateToHost: También considera donar a tu propio servidor , {host}, para
|
||||
ayudar con los costos de operación.
|
||||
sponsors: Patrocinadores de Firefish
|
||||
misskeyContributors: Contribuidores de Misskey
|
||||
_nsfw:
|
||||
respect: "Ocultar medios NSFW"
|
||||
ignore: "No esconder medios NSFW "
|
||||
|
@ -1177,8 +1178,10 @@ _mfm:
|
|||
fade: Fundido
|
||||
advanced: MFM avanzado
|
||||
play: Reproducir MFM
|
||||
foregroundDescription: Cambiar el color en primer plano del texto.
|
||||
foregroundDescription: Cambiar el color del texto en primer plano.
|
||||
background: Color de fondo
|
||||
positionDescription: Mueve el contenido en una cantidad especificada.
|
||||
fadeDescription: Funde el contenido dentro y fuera.
|
||||
_instanceTicker:
|
||||
none: "No mostrar"
|
||||
remote: "Mostrar a usuarios remotos"
|
||||
|
@ -1197,7 +1200,7 @@ _channel:
|
|||
owned: "Dueño"
|
||||
following: "Siguiendo"
|
||||
usersCount: "{n} participantes"
|
||||
notesCount: "{n} publicaciones"
|
||||
notesCount: "{n} Publicaciones"
|
||||
nameOnly: Nombre solamente
|
||||
nameAndDescription: Nombre y descripción
|
||||
_menuDisplay:
|
||||
|
@ -1301,7 +1304,7 @@ _theme:
|
|||
fgHighlighted: "Texto resaltado"
|
||||
_sfx:
|
||||
note: "Nueva publicación"
|
||||
noteMy: "Nota (a mí mismo)"
|
||||
noteMy: "Publicación propia"
|
||||
notification: "Notificaciones"
|
||||
chat: "Chat"
|
||||
chatBg: "Chat (Fondo)"
|
||||
|
@ -1310,11 +1313,11 @@ _sfx:
|
|||
_ago:
|
||||
future: "Futuro"
|
||||
justNow: "Recién ahora"
|
||||
secondsAgo: "Hace {n} segundo(s)"
|
||||
minutesAgo: "Hace {n} minuto(s)"
|
||||
secondsAgo: "Hace {n} s"
|
||||
minutesAgo: "Hace {n} m"
|
||||
hoursAgo: "Hace {n} hora(s)"
|
||||
daysAgo: "Hace {n} día(s)"
|
||||
weeksAgo: "Hace {n} semana(s)"
|
||||
daysAgo: "Hace {n} d"
|
||||
weeksAgo: "Hace {n} sem"
|
||||
monthsAgo: "Hace {n} mes(es)"
|
||||
yearsAgo: "Hace {n} año(s)"
|
||||
_time:
|
||||
|
@ -1339,15 +1342,15 @@ _tutorial:
|
|||
step5_1: "¡Líneas de tiempo, líneas de tiempo por todas partes!"
|
||||
step5_2: "Tu servidor tiene {timelines} diferentes líneas de tiempo habilitadas."
|
||||
step5_3: "La línea de tiempo Inicio {icon} es donde puedes ver las publicaciones
|
||||
de tus seguidores."
|
||||
de personas que sigues."
|
||||
step5_4: "La línea de tiempo Local {icon} es donde puedes ver las publicaciones
|
||||
de todos los demás en esta instancia."
|
||||
step5_5: "La línea de tiempo {icon} recomendada es donde puedes ver las publicaciones
|
||||
de las instancias que los administradores recomiendan."
|
||||
step5_6: "La línea de tiempo Social {icon} es donde puedes ver las publicaciones
|
||||
de los amigos de tus seguidores."
|
||||
step5_7: "La línea de tiempo Global {icon} es donde puedes ver las publicaciones
|
||||
de todas las demás instancias conectadas."
|
||||
de todos los demás en este servidor."
|
||||
step5_5: "La línea de tiempo {icon} social es una combinación de las líneas de tiempo
|
||||
Inicio y Local."
|
||||
step5_6: "La línea de tiempo {icon} recomendada es donde puedes ver las publicaciones
|
||||
de los servidores que los administradores recomiendan."
|
||||
step5_7: "La línea de tiempo {icon} global es donde puedes ver las publicaciones
|
||||
de todos los demás servidores a los cuales este servidor conecta."
|
||||
step6_1: "Entonces, ¿qué es este lugar?"
|
||||
step6_2: "Bueno, no sólo te has unido a Firefish. Te has unido a un portal del Fediverso,
|
||||
una red interconectada de miles de servidores, llamada \"instancias\""
|
||||
|
@ -1368,6 +1371,26 @@ _2fa:
|
|||
securityKeyInfo: "Se puede configurar el inicio de sesión usando una clave de seguridad
|
||||
de hardware que soporte FIDO2 o con un certificado de huella digital o con un
|
||||
PIN"
|
||||
chromePasskeyNotSupported: Contraseñas de Chrome no están soportadas.
|
||||
removeKeyConfirm: ¿Realmente deseas borrar la clave {name}?
|
||||
step3Title: Ingresa un código de autorización
|
||||
renewTOTP: Reconfigurar la aplicación autorizadora
|
||||
whyTOTPOnlyRenew: La aplicación autorizadora no puede ser quitada mientras la clave
|
||||
de seguridad siga registrada.
|
||||
renewTOTPConfirm: Esto causará que los códigos de verificación de la aplicación
|
||||
anterior dejen de funcionar
|
||||
renewTOTPOk: Reconfigurar
|
||||
securityKeyNotSupported: Tu navegador no soporta claves de seguridad.
|
||||
step2Click: Presionar este código QR te permitirá registrar la autorización 2FA
|
||||
a tu clave de seguridad o aplicación autorizadora.
|
||||
registerTOTPBeforeKey: Por favor configura una aplicación autorizadora para registrar
|
||||
una clave de seguridad o de paso.
|
||||
securityKeyName: Ingresa el nombre de la clave
|
||||
tapSecurityKey: Por favor, espera al navegador para registrar la clave de seguridad
|
||||
o de paso
|
||||
renewTOTPCancel: Cancelar
|
||||
token: Token 2FA
|
||||
removeKey: Quitar clave de seguridad
|
||||
_permissions:
|
||||
"read:account": "Ver información de la cuenta"
|
||||
"write:account": "Editar información de la cuenta"
|
||||
|
@ -1383,7 +1406,7 @@ _permissions:
|
|||
"write:messaging": "Administrar chat"
|
||||
"read:mutes": "Ver usuarios silenciados"
|
||||
"write:mutes": "Administrar usuarios silenciados"
|
||||
"write:notes": "Crear/borrar notas"
|
||||
"write:notes": "Crear o borrar publicaciones"
|
||||
"read:notifications": "Ver notificaciones"
|
||||
"write:notifications": "Administrar notificaciones"
|
||||
"read:reactions": "Ver reacciones"
|
||||
|
@ -1405,16 +1428,19 @@ _auth:
|
|||
shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?"
|
||||
shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder
|
||||
a su cuenta?"
|
||||
permissionAsk: "Esta aplicación requiere los siguientes permisos"
|
||||
permissionAsk: "Esta aplicación requiere los siguientes permisos:"
|
||||
pleaseGoBack: "Por favor, vuelve a la aplicación"
|
||||
callback: "Volviendo a la aplicación"
|
||||
denied: "Acceso denegado"
|
||||
copyAsk: 'Por favor, pega el siguiente código de autorización en la aplicación:'
|
||||
allPermissions: Acceso completo
|
||||
_antennaSources:
|
||||
all: "Todas las notas"
|
||||
homeTimeline: "Notas de los usuarios que sigues"
|
||||
users: "Notas de un usuario o varios"
|
||||
userList: "Notas de los usuarios de una lista"
|
||||
userGroup: "Notas de los usuarios de una grupo"
|
||||
all: "Todas las publicaciones"
|
||||
homeTimeline: "Publicaciones de los usuarios que sigues"
|
||||
users: "Publicaciones de usuarios específicos"
|
||||
userList: "Publicaciones de una lista de usuarios específica"
|
||||
userGroup: "Publicaciones de usuarios de un grupo"
|
||||
instances: Publicaciones de todos los usuarios en un servidor
|
||||
_weekday:
|
||||
sunday: "Domingo"
|
||||
monday: "Lunes"
|
||||
|
@ -1431,24 +1457,28 @@ _widgets:
|
|||
trends: "Tendencias"
|
||||
clock: "Reloj"
|
||||
rss: "Lector RSS"
|
||||
rssTicker: "Ticker-RSS"
|
||||
rssTicker: "Marquesina RSS"
|
||||
activity: "Actividad"
|
||||
photos: "Fotos"
|
||||
digitalClock: "Reloj digital"
|
||||
unixClock: "Reloj UNIX"
|
||||
federation: "Federación"
|
||||
instanceCloud: "Nube de palabras de la instancia"
|
||||
instanceCloud: "Nube de servidores"
|
||||
postForm: "Formulario"
|
||||
slideshow: "Diapositivas"
|
||||
button: "Botón"
|
||||
onlineUsers: "Usuarios en linea"
|
||||
onlineUsers: "Usuarios en línea"
|
||||
jobQueue: "Cola de trabajos"
|
||||
serverMetric: "Estadísticas del servidor"
|
||||
aiscript: "Consola de AiScript"
|
||||
aichan: "indigo"
|
||||
userList: Lista Usuarios
|
||||
userList: Lista de usuarios
|
||||
_userList:
|
||||
chooseList: Seleccione una lista
|
||||
serverInfo: Información del servidor
|
||||
meiliStatus: Estado del servidor
|
||||
meiliSize: Tamaño del índice
|
||||
meiliIndexCount: Publicaciones indizadas
|
||||
_cw:
|
||||
hide: "Ocultar"
|
||||
show: "Ver más"
|
||||
|
@ -1478,18 +1508,18 @@ _poll:
|
|||
remainingSeconds: "Quedan {s} segundos para que finalice"
|
||||
_visibility:
|
||||
public: "Público"
|
||||
publicDescription: "Visible para todos los usuarios"
|
||||
home: "Inicio"
|
||||
publicDescription: "Tu publicación será visible en todas las líneas de tiempo"
|
||||
home: "Sin listar (Inicio)"
|
||||
homeDescription: "Visible sólo en la linea de tiempo de inicio"
|
||||
followers: "Seguidores"
|
||||
followersDescription: "Visible sólo para tus seguidores"
|
||||
followersDescription: "Hacer sólo visible sólo para tus seguidores y usuarios mencionados"
|
||||
specified: "Mensaje directo"
|
||||
specifiedDescription: "Visible sólo para los usuarios elegidos"
|
||||
localOnly: "Solo local"
|
||||
localOnlyDescription: "Oculto para usuarios remotos"
|
||||
_postForm:
|
||||
replyPlaceholder: "Responder a esta nota"
|
||||
quotePlaceholder: "Citar esta nota"
|
||||
replyPlaceholder: "Responder a esta publicación..."
|
||||
quotePlaceholder: "Citar esta publicación..."
|
||||
channelPlaceholder: "Postear en el canal"
|
||||
_placeholders:
|
||||
a: "¿Qué haces?"
|
||||
|
@ -1514,7 +1544,7 @@ _profile:
|
|||
locationDescription: Si ingresas tu ciudad primero, el tiempo local tuyo será visible
|
||||
para otros usuarios.
|
||||
_exportOrImport:
|
||||
allNotes: "Todas las notas"
|
||||
allNotes: "Todas las publicaciones"
|
||||
followingList: "Siguiendo"
|
||||
muteList: "Silenciados"
|
||||
blockingList: "Bloqueados"
|
||||
|
@ -1527,10 +1557,10 @@ _charts:
|
|||
usersIncDec: "Variación de usuarios"
|
||||
usersTotal: "Total de usuarios"
|
||||
activeUsers: "Cantidad de usuarios activos"
|
||||
notesIncDec: "Variación de la cantidad de notas"
|
||||
localNotesIncDec: "Variación de la cantidad de notas locales"
|
||||
remoteNotesIncDec: "Variación de la cantidad de notas remotas"
|
||||
notesTotal: "Total de notas"
|
||||
notesIncDec: "Diferencia en la cantidad de publicaciones"
|
||||
localNotesIncDec: "Diferencia en la cantidad de publicaciones locales"
|
||||
remoteNotesIncDec: "Diferencia en el número de publicaciones remotas"
|
||||
notesTotal: "Total de publicaciones"
|
||||
filesIncDec: "Variación de cantidad de archivos"
|
||||
filesTotal: "Total de archivos"
|
||||
storageUsageIncDec: "Variación de uso del almacenamiento"
|
||||
|
@ -1539,8 +1569,8 @@ _instanceCharts:
|
|||
requests: "Pedidos"
|
||||
users: "Variación de usuarios"
|
||||
usersTotal: "Total acumulado de usuarios"
|
||||
notes: "Variación de la cantidad de notas"
|
||||
notesTotal: "Total acumulado de la cantidad de notas"
|
||||
notes: "Diferencia en el número de publicaciones"
|
||||
notesTotal: "Total acumulado de publicaciones"
|
||||
ff: "Variación de cantidad de seguidos/seguidores"
|
||||
ffTotal: "Total acumulado de cantidad de seguidos/seguidores"
|
||||
cacheSize: "Variación del tamaño de la caché"
|
||||
|
@ -1627,10 +1657,10 @@ _pages:
|
|||
id: "Lienzo ID"
|
||||
width: "Ancho"
|
||||
height: "Altura"
|
||||
note: "Nota embebida"
|
||||
note: "Publicación incrustada"
|
||||
_note:
|
||||
id: "Id de la nota"
|
||||
idDescription: "Pega la URL de la nota para configurarla"
|
||||
id: "ID de la publicación"
|
||||
idDescription: "Puedes también pegar la URL de la publicación aquí."
|
||||
detailed: "Ver Detalles"
|
||||
switch: "Interruptor"
|
||||
_switch:
|
||||
|
@ -1853,7 +1883,7 @@ _notification:
|
|||
youGotMention: "Mención de {name}"
|
||||
youGotReply: "Respuesta de {name}"
|
||||
youGotQuote: "Citado por {name}"
|
||||
youRenoted: "Renotado por {name}"
|
||||
youRenoted: "Impulsado por {name}"
|
||||
youGotPoll: "Encuestado por {name}"
|
||||
youGotMessagingMessageFromUser: "{name} comenzó un chat contigo"
|
||||
youGotMessagingMessageFromGroup: "Tienes un chat de {name}"
|
||||
|
@ -1868,7 +1898,7 @@ _notification:
|
|||
follow: "Siguiendo"
|
||||
mention: "Menciones"
|
||||
reply: "Respuestas"
|
||||
renote: "Renotar"
|
||||
renote: "Impulsos"
|
||||
quote: "Citar"
|
||||
reaction: "Reacción"
|
||||
pollVote: "Votado en la encuesta"
|
||||
|
@ -1880,7 +1910,10 @@ _notification:
|
|||
_actions:
|
||||
followBack: "Te sigue de vuelta"
|
||||
reply: "Responder"
|
||||
renote: "Renotar"
|
||||
renote: "Impulsos"
|
||||
renoted: impulsó tu publicación
|
||||
reacted: reaccionó a tu publicación
|
||||
voted: votó en tu encuesta
|
||||
_deck:
|
||||
alwaysShowMainColumn: "Siempre mostrar la columna principal"
|
||||
columnAlign: "Alinear columnas"
|
||||
|
@ -1892,9 +1925,9 @@ _deck:
|
|||
swapDown: "Mover abajo"
|
||||
stackLeft: "Apilar a la izquierda"
|
||||
popRight: "Sacar a la derecha"
|
||||
profile: "Perfil"
|
||||
newProfile: "Nuevo perfil"
|
||||
deleteProfile: "Eliminar perfil"
|
||||
profile: "Espacio de trabajo"
|
||||
newProfile: "Nuevo espacio de trabajo"
|
||||
deleteProfile: "Eliminar espacio de trabajo"
|
||||
introduction: "¡Crea la interfaz perfecta para tí organizando las columnas libremente!"
|
||||
introduction2: "Presiona en la + de la derecha de la pantalla para añadir nuevas
|
||||
columnas donde quieras."
|
||||
|
@ -1905,10 +1938,13 @@ _deck:
|
|||
widgets: "Widgets"
|
||||
notifications: "Notificaciones"
|
||||
tl: "Linea de tiempo"
|
||||
antenna: "Antenas"
|
||||
antenna: "Antena"
|
||||
list: "Listas"
|
||||
mentions: "Menciones"
|
||||
direct: "Mensaje directo"
|
||||
direct: "Mensajes directos"
|
||||
channel: Canal
|
||||
renameProfile: Renombrar espacio de trabajo
|
||||
nameAlreadyExists: Este nombre de espacio de trabajo ya existe.
|
||||
manageGroups: Administrar grupos
|
||||
replayTutorial: Repetir Tutorial
|
||||
privateMode: Modo privado
|
||||
|
@ -1923,11 +1959,13 @@ breakFollowConfirm: ¿Estás seguro de que quieres eliminar el seguidor?
|
|||
subscribePushNotification: Habilitar notificaciones
|
||||
unsubscribePushNotification: Desactivar notificaciones
|
||||
pushNotificationAlreadySubscribed: Las notificaciones ya están activados
|
||||
pushNotificationNotSupported: Su navegador o instancia no admite notificaciones
|
||||
pushNotificationNotSupported: Su navegador o servidor no admite notificaciones
|
||||
moveAccount: ¡Mover cuenta!
|
||||
moveFrom: Mueve a esta cuenta de una cuenta antigua
|
||||
moveFromLabel: 'La cuenta que estás moviendo de:'
|
||||
moveAccountDescription: ''
|
||||
moveAccountDescription: 'Este proceso es irreversible. Asegúrate de configurar un
|
||||
alias para ésta cuenta en tu cuenta nueva antes de comenzar. Por favor, ingresa
|
||||
la etiqueta de la cuenta en el formato siguiente: @persona@servidor.tld'
|
||||
license: Licencia
|
||||
noThankYou: No gracias
|
||||
userSaysSomethingReason: '{name} dijo {reason}'
|
||||
|
@ -1938,7 +1976,7 @@ caption: Auto Subtítulos
|
|||
showAds: Mostrar banners
|
||||
enterSendsMessage: Presione "RETORNO" en los mensajes para enviar el mensaje (para
|
||||
apagarlo es Ctrl + RETORNO)
|
||||
recommendedInstances: Instancias Recomendadas
|
||||
recommendedInstances: Servidores recomendados
|
||||
instanceSecurity: Seguridad del servidor
|
||||
seperateRenoteQuote: Separar botones de Impulsar y Citar
|
||||
_messaging:
|
||||
|
@ -1995,6 +2033,10 @@ _filters:
|
|||
fromUser: Del usuario
|
||||
fromDomain: Desde el dominio
|
||||
notesAfter: Publicaciones posteriores
|
||||
followingOnly: Sólo seguidos
|
||||
withFile: Con archivo
|
||||
followersOnly: Sólo seguidores
|
||||
notesBefore: Publicaciones anteriores
|
||||
userSaysSomethingReasonReply: '{name} respondió a una publicación que contiene {reason}'
|
||||
userSaysSomethingReasonQuote: '{name} citó una publicación que contiene {reason}'
|
||||
privateModeInfo: Al activar, solo servidores autorizados podrán federar con tu servidor.
|
||||
|
@ -2022,3 +2064,103 @@ remindMeLater: Recordar nuevamente
|
|||
removeQuote: Eliminar cita
|
||||
removeRecipient: Eliminar destinatario
|
||||
removeMember: Eliminar miembro
|
||||
_skinTones:
|
||||
light: Claro
|
||||
dark: Obscuro
|
||||
yellow: Amarillo
|
||||
medium: Medio
|
||||
mediumLight: Claro medio
|
||||
mediumDark: Obscuro medio
|
||||
secureModeInfo: Al pedir a otros servidores, no mandar si no hay prueba de confianza.
|
||||
enableIdenticonGeneration: Activar la generación de Identicon
|
||||
sendModMail: Enviar aviso de moderación
|
||||
reactionPickerSkinTone: Tono de piel preferido en emojis
|
||||
_dialog:
|
||||
charactersExceeded: '¡Límite de caracteres excedido! Actual: {current}/Límite: {max}'
|
||||
charactersBelow: '¡Caracteres insuficientes! Actual: {current}/Límite: {min}'
|
||||
expandOnNoteClick: Abrir publicación al hacer click
|
||||
_experiments:
|
||||
enablePostImports: Habilitar importación de publicaciones
|
||||
title: Experimentos
|
||||
postImportsCaption: Permite a los usuarios importar sus publicaciones desde Firefish,
|
||||
Misskey, Mastodon, Akkoma y Pleroma. Puede causar una bajada en el rendimiento
|
||||
del servidor si la cola de trabajos está atascada.
|
||||
showUpdates: Mostrar una notificación emergente cuando Firefish se actualice
|
||||
recommendedInstancesDescription: Servidores recomendados separador por saltos de línea
|
||||
para que aparezcan el la línea de tiempo recomendados.
|
||||
swipeOnMobile: Permitir el pase entre páginas
|
||||
addRe: Añadir "re:" al comienzo del comentario en una respuesta a una publicación
|
||||
sin aviso de contenido
|
||||
showAdminUpdates: Avisar si hay una nueva versión de Firefish disponible (sólo adminsitrador)
|
||||
_feeds:
|
||||
rss: RSS
|
||||
copyFeed: Copiar feed
|
||||
atom: Atom
|
||||
jsonFeed: Feed JSON
|
||||
secureMode: Modo seguro (Recuperación Autorizada)
|
||||
splash: Pantalla de bienvenida
|
||||
moveToLabel: 'Cuenta a la cual estás migrando:'
|
||||
alt: ALT
|
||||
video: Video
|
||||
audio: Audio
|
||||
swipeOnDesktop: Permitir el pase de páginas del estilo móvil en el escritorio
|
||||
enableCustomKaTeXMacro: Habilitar macros KaTeX personalizadas
|
||||
noteId: ID de publicación
|
||||
preventAiLearning: Prevenir el uso por parte de bots de IA
|
||||
isLocked: Esta cuenta requiere aprobación de seguidores
|
||||
origin: Origen
|
||||
newer: reciente
|
||||
older: antiguo
|
||||
objectStorageS3ForcePathStyle: Usar URL de punto final basada en rutas
|
||||
objectStorageS3ForcePathStyleDesc: Activa esto para construir puntos finales URL en
|
||||
el formato 's3.amazonaws.com/<bucket>/' en lugar de '<bucket>.s3.amazonaws.com'.
|
||||
customSplashIconsDescription: URL para los iconos de la pantalla de bienvenida separadas
|
||||
por saltos de línea para ser mostrados al azar cada vez que el usuario carga/recarga
|
||||
la página. Por favor, asegúrate que las imágenes sean URL estáticas, preferentemente
|
||||
a 192x192.
|
||||
updateAvailable: ¡Quizá hay una actualización disponible!
|
||||
moveTo: Mover la cuenta actual a una cuenta nueva
|
||||
moveFromDescription: 'Esto pondrá un alias en tu cuenta antigua para así poder migrar
|
||||
desde esa cuenta a la nueva. Haz esto ANTES de migrar tu cuenta antigua. Por favor,
|
||||
ingresa la etiqueta de la cuenta con el formato siguiente: @persona@servidor.tld'
|
||||
defaultReaction: Emoji por defecto para reaccionar a las publicaciones entrantes y
|
||||
salientes
|
||||
indexFromDescription: Deja en blanco para indizar todas las publicaciones
|
||||
deletePasskeys: Borrar claves de paso
|
||||
deletePasskeysConfirm: Esto borrará irreversiblemente todas las claves de paso y de
|
||||
seguridad en esta cuenta, ¿Proceder?
|
||||
inputNotMatch: Las entradas no coinciden
|
||||
indexFrom: Indizar desde la ID de la publicación en adelante
|
||||
indexPosts: Indizar publicaciones
|
||||
isModerator: Moderador
|
||||
isAdmin: Administrador
|
||||
isPatron: Mecenas de Firefish
|
||||
logoImageUrl: URL de la imagen del logotipo
|
||||
xl: XL
|
||||
migrationConfirm: "¿Estás absolutamente seguro de que quieres migrar a tu cuenta a
|
||||
{account}? Una vez hecho esto, no podrás revertir el cambio, ni tampoco usar tu
|
||||
cuenta normalmente.\nTambién, asegúrate de que has configurado ésta cuenta como
|
||||
la cuenta desde la cual estás migrando."
|
||||
indexNotice: Indizando ahora. Esto puede llevar bastante tiempo, por favor, no reinicies
|
||||
el servidor por lo menos hasta dentro de una hora.
|
||||
customKaTeXMacro: Macros KaTeX personalizadas
|
||||
customKaTeXMacroDescription: '¡Configura macros para escribir expresiones matemáticas
|
||||
fácilmente! La notación es conforme la las definiciones de comandos LaTeX y puede
|
||||
ser escrita como \nuevocomando{\ nombre}{contenido} o \nuevocomando{\nombre}[número
|
||||
de argumentos]{contenido}. Por ejemplo, \nuevocomando{\añadir}[2]{#1 + #2} expanderá
|
||||
\añadir{3}{foo} a 3 + foo. Las llaves que contienen al nombre de la macro serán
|
||||
cambiadas a paréntesis o corchetes. Esto afecta a los corchetes usados para argumentos.
|
||||
Una (y sólo una) macro puede ser definida por línea, y no podrás saltar la línea
|
||||
en medio de la definición. Líneas erróneas son ignoradas. Sólo funciones de sustitución
|
||||
simple son soportadas; sintaxis avanzada, como ramificación condicional no puede
|
||||
ser usada aquí.'
|
||||
signupsDisabled: Los registros en esta instancia están desactivados, pero, ¡siempre
|
||||
podrás registrarte en otro servidor! Si tienes un código de invitación para este
|
||||
servidor, por favor, rellena el campo siguiente.
|
||||
preventAiLearningDescription: Pedir a los modelos de IA no analizar el contenido de
|
||||
publicas, como publicaciones e imágenes.
|
||||
noGraze: Por favor desactiva la extensión de navegador "Graze for Mastodon" ya que
|
||||
interfiere con Firefish.
|
||||
silencedWarning: Esta página se muestra debido a que estos usuarios son de servidores
|
||||
que tu administrador ha silenciado, ya que son presumiblemente fuente de spam.
|
||||
isBot: Esta cuenta es un bot
|
||||
|
|
|
@ -53,7 +53,7 @@ sendMessage: "Envoyer un message"
|
|||
copyUsername: "Copier le nom d’utilisateur·rice"
|
||||
searchUser: "Chercher un·e utilisateur·rice"
|
||||
reply: "Répondre"
|
||||
loadMore: "Afficher plus"
|
||||
loadMore: "Charger plus"
|
||||
showMore: "Afficher plus"
|
||||
showLess: "Fermer"
|
||||
youGotNewFollower: "Vous suit"
|
||||
|
@ -120,8 +120,8 @@ reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer,
|
|||
« + » pour ajouter."
|
||||
rememberNoteVisibility: "Se souvenir des paramètres de visibilité des publications"
|
||||
attachCancel: "Supprimer le fichier attaché"
|
||||
markAsSensitive: "Marquer comme sensible"
|
||||
unmarkAsSensitive: "Supprimer le marquage comme sensible"
|
||||
markAsSensitive: "Marquer comme sensible (NSFW)"
|
||||
unmarkAsSensitive: "Supprimer le marquage comme sensible (NSFW)"
|
||||
enterFileName: "Entrer le nom du fichier"
|
||||
mute: "Masquer"
|
||||
unmute: "Ne plus masquer"
|
||||
|
@ -245,7 +245,7 @@ currentPassword: "Mot de passe actuel"
|
|||
newPassword: "Nouveau mot de passe"
|
||||
newPasswordRetype: "Répéter le nouveau mot de passe"
|
||||
attachFile: "Joindre un fichier"
|
||||
more: "Plus"
|
||||
more: "Plus !"
|
||||
featured: "Tendances"
|
||||
usernameOrUserId: "Nom d’utilisateur·rice ou ID utilisateur"
|
||||
noSuchUser: "Utilisateur·rice non trouvé·e"
|
||||
|
@ -318,7 +318,7 @@ copyUrl: "Copier l’URL"
|
|||
rename: "Renommer"
|
||||
avatar: "Avatar"
|
||||
banner: "Bannière"
|
||||
nsfw: "Contenu sensible"
|
||||
nsfw: "Contenu sensible (NSFW)"
|
||||
whenServerDisconnected: "Lorsque la connexion au serveur est perdue"
|
||||
disconnectedFromServer: "Déconnecté·e du serveur"
|
||||
reload: "Rafraîchir"
|
||||
|
@ -516,11 +516,11 @@ showFeaturedNotesInTimeline: "Afficher les publications des Tendances dans le fi
|
|||
d'actualité"
|
||||
objectStorage: "Stockage d'objets"
|
||||
useObjectStorage: "Utiliser le stockage d'objets"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBaseUrl: "URL racine"
|
||||
objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement
|
||||
d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez
|
||||
l’adresse accessible au public selon le guide de service que vous allez utiliser.\n
|
||||
Ex: 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>'
|
||||
Ex : 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>'
|
||||
pour GCS."
|
||||
objectStorageBucket: "Bucket"
|
||||
objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le
|
||||
|
@ -591,7 +591,7 @@ divider: "Séparateur"
|
|||
addItem: "Ajouter un élément"
|
||||
relays: "Relais"
|
||||
addRelay: "Ajouter un relais"
|
||||
inboxUrl: "Inbox URL"
|
||||
inboxUrl: "URL de boîte de récéption"
|
||||
addedRelays: "Relais ajoutés"
|
||||
serviceworkerInfo: "Devrait être activé pour les notifications push."
|
||||
deletedNote: "Publication supprimée"
|
||||
|
@ -729,7 +729,7 @@ noCrawleDescription: "Demandez aux moteurs de recherche de ne pas indexer votre
|
|||
lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre publication
|
||||
sur \"Abonné-e-s\", vos publications sont visibles par tous, même si vous exigez
|
||||
que les demandes d'abonnement soient approuvées manuellement."
|
||||
alwaysMarkSensitive: "Marquer les médias comme contenu sensible par défaut"
|
||||
alwaysMarkSensitive: "Marquer les médias comme contenu sensible (NSFW) par défaut"
|
||||
loadRawImages: "Affichage complet des images jointes au lieu des vignettes"
|
||||
disableShowingAnimatedImages: "Désactiver l'animation des images"
|
||||
verificationEmailSent: "Un e-mail de vérification a été envoyé. Veuillez accéder au
|
||||
|
@ -839,7 +839,7 @@ gallery: "Galerie"
|
|||
recentPosts: "Publications récentes"
|
||||
popularPosts: "Publications populaires"
|
||||
shareWithNote: "Partager dans une publication"
|
||||
ads: "Bannière communautaire"
|
||||
ads: "Bannières communautaires"
|
||||
expiration: "Échéance"
|
||||
memo: "Pense-bête"
|
||||
priority: "Priorité"
|
||||
|
@ -986,8 +986,8 @@ _plugin:
|
|||
manage: "Gestion des plugins"
|
||||
_registry:
|
||||
scope: "Portée"
|
||||
key: "Clé "
|
||||
keys: "Clé "
|
||||
key: "Clé"
|
||||
keys: "Clés"
|
||||
domain: "Domaine"
|
||||
createKey: "Créer une clé"
|
||||
_aboutFirefish:
|
||||
|
@ -1011,8 +1011,8 @@ _aboutFirefish:
|
|||
le lien ci-dessus pour avoir votre nom affiché ici !
|
||||
misskeyContributors: Contributeurs Misskey
|
||||
_nsfw:
|
||||
respect: "Cacher les médias marqués comme contenu sensible"
|
||||
ignore: "Afficher les médias sensibles"
|
||||
respect: "Cacher les médias marqués comme contenu sensible (NSFW)"
|
||||
ignore: "Afficher les médias sensibles (NSFW)"
|
||||
force: "Cacher tous les médias"
|
||||
_mfm:
|
||||
cheatSheet: "Antisèche MFM"
|
||||
|
@ -1021,7 +1021,7 @@ _mfm:
|
|||
dummy: "La Fédiverse s'agrandit avec Firefish"
|
||||
mention: "Mentionner"
|
||||
mentionDescription: "Vous pouvez afficher un utilisateur spécifique en indiquant
|
||||
une arobase suivie d'un nom d'utilisateur"
|
||||
le symbole d'arobase (@) suivie d'un nom d'utilisateur."
|
||||
hashtag: "Hashtags"
|
||||
hashtagDescription: "Vous pouvez afficher les hashtags en utilisant un croisillon
|
||||
et du texte."
|
||||
|
@ -1034,7 +1034,7 @@ _mfm:
|
|||
small: "Diminuer l'emphase"
|
||||
smallDescription: "Le contenu peut être affiché en petit et fin."
|
||||
center: "Centrer"
|
||||
centerDescription: "Le contenu peut être centré"
|
||||
centerDescription: "Centre le contenu sur la page."
|
||||
inlineCode: "Code (inline)"
|
||||
inlineCodeDescription: "Affiche la coloration syntaxique des lignes de code."
|
||||
blockCode: "Bloc de code"
|
||||
|
@ -1105,7 +1105,7 @@ _mfm:
|
|||
background: Couleur d'arrière-plan
|
||||
plain: Simple
|
||||
_instanceTicker:
|
||||
none: "Cacher "
|
||||
none: "Cacher"
|
||||
remote: "Montrer pour les utilisateur·ice·s distant·e·s"
|
||||
always: "Toujours afficher"
|
||||
_serverDisconnectedBehavior:
|
||||
|
@ -1170,17 +1170,17 @@ _theme:
|
|||
color: "Couleur"
|
||||
refProp: "Appeler une propriété"
|
||||
refConst: "Appeler une constante"
|
||||
key: "Clé "
|
||||
key: "Clé"
|
||||
func: "Fonction"
|
||||
funcKind: "Type de fonction"
|
||||
argument: "Argument"
|
||||
basedProp: "Nom de la propriété référencée"
|
||||
alpha: "Transparence"
|
||||
darken: "Sombre"
|
||||
darken: "Assombrir"
|
||||
lighten: "Clair"
|
||||
inputConstantName: "Insérez un nom de constante"
|
||||
importInfo: "Vous pouvez importer un thème vers l’éditeur de thèmes en saisissant
|
||||
son code ici."
|
||||
son code ici"
|
||||
deleteConstantConfirm: "Êtes-vous sûr·e de vouloir supprimer la constante {const}
|
||||
?"
|
||||
keys:
|
||||
|
@ -1252,9 +1252,9 @@ _time:
|
|||
day: "j"
|
||||
_tutorial:
|
||||
title: "Comment utiliser Firefish"
|
||||
step1_1: "Bienvenue!"
|
||||
step1_2: "On va vous installer. Vous serez opérationnel en un rien de temps"
|
||||
step2_1: "Tout d'abord, remplissez votre profil"
|
||||
step1_1: "Bienvenue !"
|
||||
step1_2: "On va vous installer. Vous serez opérationnel en un rien de temps !"
|
||||
step2_1: "Tout d'abord, remplissez votre profil."
|
||||
step2_2: "En fournissant quelques informations sur qui vous êtes, il sera plus facile
|
||||
pour les autres de savoir s'ils veulent voir vos publcations ou vous suivre."
|
||||
step3_1: "Maintenant il est temps de suivre des gens !"
|
||||
|
@ -1265,7 +1265,7 @@ _tutorial:
|
|||
step4_2: "Pour votre première publication, certaines personnes aiment faire une
|
||||
{introduction} ou un simple 'Bonjour tout le monde !'"
|
||||
step5_1: "Des fils, des fils d’actualité partout !"
|
||||
step5_2: "Votre serveur a {timelines} fils différents disponibles !"
|
||||
step5_2: "Votre serveur a {timelines} fils différents activés."
|
||||
step5_3: "Le fil {icon} Principal est l'endroit où vous pouvez voir les publications
|
||||
de vos abonnements."
|
||||
step5_4: "La fil {icon} Local est l'endroit où vous pouvez voir les publications
|
||||
|
@ -1357,7 +1357,7 @@ _permissions:
|
|||
_auth:
|
||||
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
|
||||
shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre
|
||||
compte?"
|
||||
compte ?"
|
||||
permissionAsk: "Cette application nécessite les autorisations suivantes :"
|
||||
pleaseGoBack: "Veuillez retourner à l’application"
|
||||
callback: "Retour vers l’application"
|
||||
|
@ -1412,7 +1412,7 @@ _widgets:
|
|||
rssTicker: Bandeau RSS
|
||||
_cw:
|
||||
hide: "Masquer"
|
||||
show: "Afficher plus …"
|
||||
show: "Afficher contenu"
|
||||
chars: "{count} caractères"
|
||||
files: "{count} fichiers"
|
||||
_poll:
|
||||
|
@ -1422,8 +1422,8 @@ _poll:
|
|||
canMultipleVote: "Autoriser le multi-choix"
|
||||
expiration: "Fin du sondage"
|
||||
infinite: "Illimité"
|
||||
at: "Choisir une date"
|
||||
after: "Choisir la durée"
|
||||
at: "Expire le..."
|
||||
after: "Expire après..."
|
||||
deadlineDate: "Date de fin"
|
||||
deadlineTime: "Heure de fin"
|
||||
duration: "Durée"
|
||||
|
@ -1468,7 +1468,7 @@ _profile:
|
|||
metadataEdit: "Éditer les informations supplémentaires"
|
||||
metadataDescription: "Vous pouvez afficher jusqu'à quatre informations supplémentaires
|
||||
dans votre profil. Vous pouvez ajouter une balise {a} ou une balise {l} avec {rel}
|
||||
pour vérifier le lien sur votre profil!"
|
||||
pour vérifier le lien sur votre profil !"
|
||||
metadataLabel: "Étiquette"
|
||||
metadataContent: "Contenu"
|
||||
changeAvatar: "Changer l'image de profil"
|
||||
|
@ -1503,7 +1503,7 @@ _instanceCharts:
|
|||
usersTotal: "Total cumulé du nombre d'utilisateur·rice·s"
|
||||
notes: "Variation du nombre de publications"
|
||||
notesTotal: "Nombre total cumulé des publications"
|
||||
ff: "Variation des abonné·e·s / abonnements"
|
||||
ff: "Variation des abonnements / abonné·e·s "
|
||||
ffTotal: "Total cumulé du nombre d'abonné·e·s / abonnements"
|
||||
cacheSize: "Variation de la taille du cache"
|
||||
cacheSizeTotal: "Total cumulé de la taille du cache"
|
||||
|
@ -1812,7 +1812,7 @@ _relayStatus:
|
|||
accepted: "Accepté"
|
||||
rejected: "Refusée"
|
||||
_notification:
|
||||
fileUploaded: "Le fichier a été téléversé !"
|
||||
fileUploaded: "Le fichier a été téléversé"
|
||||
youGotMention: "{name} vous a mentionné"
|
||||
youGotReply: "Réponse de {name}"
|
||||
youGotQuote: "Cité·e par {name}"
|
||||
|
@ -1993,7 +1993,7 @@ type: Type
|
|||
speed: Vitesse
|
||||
slow: Lent
|
||||
move: Déplacer
|
||||
showAds: Afficher les bannières communautaire/publicités
|
||||
showAds: Afficher les bannières communautaire (publicités)
|
||||
enterSendsMessage: Appuyer sur Entrée pendant la rédaction pour envoyer le message
|
||||
(sinon Ctrl+Entrée)
|
||||
allowedInstancesDescription: Noms des serveurs autorisés pour la fédération, chacun
|
||||
|
@ -2071,14 +2071,14 @@ customKaTeXMacro: Macros KaTeX personnalisées
|
|||
enableCustomKaTeXMacro: Activer les macros KaTeX personnalisées
|
||||
noteId: ID des publications
|
||||
customKaTeXMacroDescription: "Définissez des macros pour écrire des expressions mathématiques
|
||||
simplement ! La notation se conforme aux définitions de commandes LaTeX et s'écrit
|
||||
simplement ! La notation se conforme aux définitions de commandes LaTeX et s'écrit
|
||||
\\newcommand{\\·name}{content} ou \\newcommand{\\name}[number of arguments]{content}.
|
||||
Par exemple, \\newcommand{\\add}[2]{#1 + #2} étendra \\add{3}{foo} en 3 + foo. Les
|
||||
accolades entourant le nom de la macro peuvent être changés pour des parenthèses
|
||||
ou des crochets. Cela affectera les types de parenthèses utilisées pour les arguments.
|
||||
Une (et une seule) macro peut être définie par ligne, et vous ne pouvez pas couper
|
||||
la ligne au milieu d'une définition. Les lignes invalides sont simplement ignorées.
|
||||
Seulement de simples fonctions de substitution de chaines sont supportées; la syntaxe
|
||||
Seulement de simples fonctions de substitution de chaines sont supportées ; la syntaxe
|
||||
avancée, telle que la ramification conditionnelle, ne peut pas être utilisée ici."
|
||||
enableRecommendedTimeline: Activer le fil recommandé
|
||||
silenceThisInstance: Masquer ce serveur
|
||||
|
@ -2197,7 +2197,7 @@ _skinTones:
|
|||
objectStorageS3ForcePathStyle: Utiliser des URL d'endpoints basées sur le chemin
|
||||
objectStorageS3ForcePathStyleDesc: Activez cette option pour construire les URL d'endpoints
|
||||
au format 's3.amazonaws.com/<bucket>/' au lieu de '<bucket>.s3.amazonaws.com'.
|
||||
delete2fa: Supprimer 2FA
|
||||
delete2fa: Désativer A2F
|
||||
deletePasskeys: Supprimer les clés d'accès
|
||||
delete2faConfirm: Cela supprimera de manière irréversible la double authentification
|
||||
sur ce compte. Souhaitez-vous continuer ?
|
||||
|
@ -2206,3 +2206,7 @@ deletePasskeysConfirm: Cela supprimera de manière irréversible toutes les clé
|
|||
et les clés de sécurité sur ce compte. Souhaitez-vous continuer ?
|
||||
addRe: Ajouter "re:" au début d’un avertissement de contenu (CW) en réponse à une
|
||||
publication avec un avertissement de contenu
|
||||
confirm: Confirmer
|
||||
importZip: Importer ZIP
|
||||
exportZip: Exporter ZIP
|
||||
emojiPackCreator: Créateur de pack d’emoji
|
||||
|
|
|
@ -310,7 +310,7 @@ emptyDrive: "Drive kosong"
|
|||
emptyFolder: "Folder kosong"
|
||||
unableToDelete: "Tidak dapat menghapus"
|
||||
inputNewFileName: "Masukkan nama berkas yang baru"
|
||||
inputNewDescription: "Masukkan keterangan disini"
|
||||
inputNewDescription: "Masukkan deskripsi baru"
|
||||
inputNewFolderName: "Masukkan nama folder yang baru"
|
||||
circularReferenceFolder: "Folder tujuan adalah subfolder dari folder yang ingin kamu
|
||||
pindahkan."
|
||||
|
@ -598,8 +598,8 @@ disablePlayer: "Tutup pemutar video"
|
|||
expandTweet: "Perluas utas"
|
||||
themeEditor: "Penyunting tema"
|
||||
description: "Deskripsi"
|
||||
describeFile: "Tambahkan keterangan"
|
||||
enterFileDescription: "Masukkan keterangan"
|
||||
describeFile: "Tambahkan deskripsi"
|
||||
enterFileDescription: "Masukkan deskripsi"
|
||||
author: "Pembuat"
|
||||
leaveConfirm: "Ada perubahan yang belum disimpan. Apakah kamu ingin membuangnya?"
|
||||
manage: "Manajemen"
|
||||
|
@ -1904,7 +1904,7 @@ recommended: Direkomendasikan
|
|||
silenceThisInstance: Bisukan server ini
|
||||
hiddenTags: Tagar Tersembunyi
|
||||
preferencesBackups: Preferensi cadangan
|
||||
editNote: Sunting catatan
|
||||
editNote: Sunting kiriman
|
||||
deleted: Dihapus
|
||||
edited: Disunting pada {date} {time}
|
||||
selectInstance: Pilih server
|
||||
|
@ -2019,7 +2019,7 @@ showAdminUpdates: Indikasi versi Firefish baru tersedia (hanya admin)
|
|||
indexFrom: Indeks dari Post ID berikutnya
|
||||
noteId: ID Postingan
|
||||
findOtherInstance: Cari server lain
|
||||
caption: Keterangan Otomatis
|
||||
caption: Deskripsi itomatis
|
||||
splash: Layar Percik
|
||||
migration: Migrasi
|
||||
moveTo: Pindahkan akun sekarang ke akun baru
|
||||
|
@ -2165,3 +2165,7 @@ delete2faConfirm: Ini akan menghapus 2FA secara permanen pada akun ini. Lanjutka
|
|||
deletePasskeysConfirm: Ini akan menghapus semua passkeys dan kunci keamanan pada akun
|
||||
ini secara permanen. Lanjutkan?
|
||||
addRe: Tambahkan "re:" pada awal komentar balasan postingan dengan peringatan konten
|
||||
confirm: Konfirmasi
|
||||
importZip: Impor ZIP
|
||||
exportZip: Ekspor ZIP
|
||||
emojiPackCreator: Pembuat paket emoji
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
_lang_: "日本語"
|
||||
headlineFirefish: "ずっと無料でオープンソースの非中央集権型ソーシャルメディアプラットフォーム🚀"
|
||||
introFirefish: "ようこそ!Firefishは、オープンソースの非中央集権型ソーシャルメディアプラットフォームです。\nいま起こっていることを共有したり、あなたについて皆に発信しましょう📡\n\
|
||||
introFirefish: "ようこそ!Firefishは、オープンソースの非中央集権型ソーシャルメディアプラットフォームです。\nいま起こっていることを共有したり、あなたについて皆に発信したりしましょう📡\n\
|
||||
「リアクション」機能で、皆の投稿に素早く反応を追加できます👍\n新しい世界を探検しよう🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "検索"
|
||||
|
@ -303,7 +303,7 @@ emptyDrive: "ドライブは空です"
|
|||
emptyFolder: "フォルダーは空です"
|
||||
unableToDelete: "削除できません"
|
||||
inputNewFileName: "新しいファイル名を入力してください"
|
||||
inputNewDescription: "新しい説明を入力してください"
|
||||
inputNewDescription: "新しい説明を入力"
|
||||
inputNewFolderName: "新しいフォルダ名を入力してください"
|
||||
circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。"
|
||||
hasChildFilesOrFolders: "このフォルダは空でないため、削除できません。"
|
||||
|
@ -577,7 +577,7 @@ disablePlayer: "プレイヤーを閉じる"
|
|||
expandTweet: "ツイートを展開する"
|
||||
themeEditor: "テーマエディター"
|
||||
description: "説明"
|
||||
describeFile: "説明を付ける"
|
||||
describeFile: "説明を追加"
|
||||
enterFileDescription: "説明を入力"
|
||||
author: "作者"
|
||||
leaveConfirm: "未保存の変更があります。破棄しますか?"
|
||||
|
@ -849,7 +849,7 @@ filter: "フィルタ"
|
|||
controlPanel: "コントロールパネル"
|
||||
manageAccounts: "アカウントを管理"
|
||||
makeReactionsPublic: "リアクション一覧を公開する"
|
||||
makeReactionsPublicDescription: "あなたがしたリアクション一覧を誰でも見れるようにします。"
|
||||
makeReactionsPublicDescription: "あなたがしたリアクション一覧を誰でも見られるようにします。"
|
||||
classic: "中央寄せ"
|
||||
muteThread: "スレッドをミュート"
|
||||
unmuteThread: "スレッドのミュートを解除"
|
||||
|
@ -949,7 +949,7 @@ customSplashIconsDescription: "ユーザがページをロード/リロードす
|
|||
showUpdates: "Firefishの更新時にポップアップを表示する"
|
||||
recommendedInstances: "おすすめサーバー"
|
||||
recommendedInstancesDescription: "おすすめタイムラインに表示するサーバーを改行区切りで入力してください。"
|
||||
caption: "自動キャプション"
|
||||
caption: "自動で説明をつける"
|
||||
splash: "スプラッシュスクリーン"
|
||||
updateAvailable: "アップデートがありますよ!"
|
||||
swipeOnDesktop: "デスクトップでモバイルスタイルのスワイプを可能にする"
|
||||
|
@ -1996,3 +1996,6 @@ _emojiModPerm:
|
|||
add: "追加"
|
||||
mod: "追加と変更"
|
||||
full: "全て許可"
|
||||
importZip: ZIPをインポート
|
||||
emojiPackCreator: 絵文字パックの作者
|
||||
exportZip: ZIPをエクスポート
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
_lang_: "日本語 (関西弁)"
|
||||
headlineFirefish: "ノートでつながるネットワーク"
|
||||
introFirefish: "ようお越し!Misskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作って、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素早く反応を追加したりもできるで✌\nほな新しい世界を探検しよか🚀"
|
||||
headlineFirefish: "ずっとタダでオープンソースの非中央集権型ソーシャルメディアプラットフォーム!🚀"
|
||||
introFirefish: "おいでやす。Firefishは、オープンソースの分散型ソーシャルメディアプラットフォームどす。\nいま起きたはるもんを共有したり、あんさんについて皆に発信したりしとくれやす👘\n\
|
||||
「リアクション」機能があるさかい、皆の投稿に素早う反応を送ることもできます🎎\nほんなら、新しい世界を探検しまひょか🎴"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "探す"
|
||||
notifications: "通知"
|
||||
|
@ -13,10 +13,10 @@ ok: "OKや"
|
|||
gotIt: "ほい"
|
||||
cancel: "やめとく"
|
||||
enterUsername: "ユーザー名を入れてや"
|
||||
renotedBy: "{user}がRenote"
|
||||
noNotes: "ノートはあらへん"
|
||||
renotedBy: "{user}がブースト"
|
||||
noNotes: "投稿はありまへん"
|
||||
noNotifications: "通知はあらへん"
|
||||
instance: "インスタンス"
|
||||
instance: "サーバー"
|
||||
settings: "設定"
|
||||
basicSettings: "基本設定"
|
||||
otherSettings: "その他の設定"
|
||||
|
@ -44,7 +44,7 @@ copyContent: "内容をコピー"
|
|||
copyLink: "リンクをコピー"
|
||||
delete: "ほかす"
|
||||
deleteAndEdit: "ほかして直す"
|
||||
deleteAndEditConfirm: "このノートをほかして書き直すんか?このノートへのリアクション、Renote、返信も全部消えてまうで。"
|
||||
deleteAndEditConfirm: "この投稿をほかして書き直すんか?この投稿へのリアクション、ブースト、返信もみんな消えてまうで。"
|
||||
addToList: "リストに入れたる"
|
||||
sendMessage: "メッセージを送る"
|
||||
copyUsername: "ユーザー名をコピー"
|
||||
|
@ -64,26 +64,28 @@ import: "インポート"
|
|||
export: "エクスポート"
|
||||
files: "ファイル"
|
||||
download: "ダウンロード"
|
||||
driveFileDeleteConfirm: "ファイル「{name}」を消してしもうてええか?このファイルを添付したノートも消えてまうで。"
|
||||
driveFileDeleteConfirm: "ファイル「{name}」を消してしもうてええか?このファイルを添付した投稿も消えてまうで。"
|
||||
unfollowConfirm: "{name}のフォローを解除してもええんか?"
|
||||
exportRequested: "エクスポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。"
|
||||
importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。"
|
||||
lists: "リスト"
|
||||
noLists: "リストなんてあらへんで"
|
||||
note: "ノート"
|
||||
notes: "ノート"
|
||||
note: "投稿"
|
||||
notes: "投稿"
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
followsYou: "フォローされとるで"
|
||||
createList: "リスト作る"
|
||||
manageLists: "リストの管理"
|
||||
error: "エラー"
|
||||
somethingHappened: "なんかアカンことが起こったで"
|
||||
somethingHappened: "なんやアカンことが起きたで"
|
||||
retry: "もっぺんやる?"
|
||||
pageLoadError: "ページの読み込みに失敗してしもうたで…"
|
||||
pageLoadError: "ページの読み込みに失敗してもた… えろうすんまへん"
|
||||
pageLoadErrorDescription: "これは普通、ネットワークかブラウザキャッシュが原因やからね。キャッシュをクリアするか、もうちっとだけ待ってくれへんか?"
|
||||
serverIsDead: "The server is not responding. Please wait for a while before trying again."
|
||||
youShouldUpgradeClient: "To display this page, please reload and use a new version client. "
|
||||
serverIsDead: "The server is not responding. Please wait for a while before trying
|
||||
again."
|
||||
youShouldUpgradeClient: "To display this page, please reload and use a new version
|
||||
client. "
|
||||
enterListName: "リスト名を入れてや"
|
||||
privacy: "プライバシー"
|
||||
makeFollowManuallyApprove: "自分が認めた人だけがこのアカウントをフォローできるようにする"
|
||||
|
@ -94,13 +96,13 @@ followRequests: "フォロー申請"
|
|||
unfollow: "フォローやめる"
|
||||
followRequestPending: "フォロー許してくれるん待っとる"
|
||||
enterEmoji: "絵文字を入れてや"
|
||||
renote: "Renote"
|
||||
unrenote: "Renoteやめる"
|
||||
renoted: "Renoteしたで。"
|
||||
cantRenote: "この投稿はRenoteできへんらしい。"
|
||||
cantReRenote: "Renote自体はRenoteできへんで。"
|
||||
renote: "ブースト"
|
||||
unrenote: "ブーストやめる"
|
||||
renoted: "ブーストしたで。"
|
||||
cantRenote: "この投稿はブーストでけへんらしい。"
|
||||
cantReRenote: "ブースト自体はブーストでけへんで。"
|
||||
quote: "引用"
|
||||
pinnedNote: "ピン留めされとるノート"
|
||||
pinnedNote: "ピン留めされとる投稿"
|
||||
pinned: "ピン留めしとく"
|
||||
you: "あんた"
|
||||
clickToShow: "押したら見えるで"
|
||||
|
@ -138,12 +140,13 @@ addEmoji: "絵文字を追加"
|
|||
settingGuide: "ええ感じの設定"
|
||||
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
||||
cacheRemoteFilesDescription: "この設定を切っとくと、リモートファイルをキャッシュせず直リンクするようになるで。サーバーの容量は節約できるけど、サムネイルが作られんくなるから通信量が増えるで。"
|
||||
flagAsBot: "Botやで"
|
||||
flagAsBotDescription: "もしこのアカウントがプログラムによって運用されるんやったら、このフラグをオンにしてたのむで。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったもんになるんやで。"
|
||||
flagAsCat: "Catやで"
|
||||
flagAsCatDescription: "ワレ、猫ちゃんならこのフラグをつけてみ?"
|
||||
flagAsBot: "ワイはBotや 🤖"
|
||||
flagAsBotDescription: "もしこのアカウントがプログラムによって運用されるんやったら、このフラグをオンにしてたのむで。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Firefishのシステム上での扱いがBotに合ったもんになったりするんやで。"
|
||||
flagAsCat: "ワイはCatや 🐯"
|
||||
flagAsCatDescription: "自分、猫ちゃんならこのフラグつけてみ?"
|
||||
flagShowTimelineReplies: "It will display the reply to the note in the timeline. "
|
||||
flagShowTimelineRepliesDescription: "It will display the reply to notes other than the user notes in the timeline when you turn it on. "
|
||||
flagShowTimelineRepliesDescription: "It will display the reply to notes other than
|
||||
the user notes in the timeline when you turn it on. "
|
||||
autoAcceptFollowed: "フォローしとるユーザーからのフォローリクエストを勝手に許可しとく"
|
||||
addAccount: "アカウントを追加"
|
||||
loginFailed: "ログインに失敗してしもうた…"
|
||||
|
@ -162,7 +165,7 @@ selectUser: "ユーザーを選ぶ"
|
|||
recipient: "宛先"
|
||||
annotation: "注釈"
|
||||
federation: "連合"
|
||||
instances: "インスタンス"
|
||||
instances: "サーバー"
|
||||
registeredAt: "初観測"
|
||||
latestRequestSentAt: "ちょっと前のリクエスト送信"
|
||||
latestRequestReceivedAt: "ちょっと前のリクエスト受信"
|
||||
|
@ -172,7 +175,7 @@ charts: "チャート"
|
|||
perHour: "1時間ごと"
|
||||
perDay: "1日ごと"
|
||||
stopActivityDelivery: "アクティビティの配送をやめる"
|
||||
blockThisInstance: "このインスタンスをブロック"
|
||||
blockThisInstance: "このサーバーをブロック"
|
||||
operations: "操作"
|
||||
software: "ソフトウェア"
|
||||
version: "バージョン"
|
||||
|
@ -182,23 +185,23 @@ jobQueue: "ジョブキュー"
|
|||
cpuAndMemory: "CPUとメモリ"
|
||||
network: "ネットワーク"
|
||||
disk: "ディスク"
|
||||
instanceInfo: "インスタンス情報"
|
||||
instanceInfo: "サーバー情報"
|
||||
statistics: "統計"
|
||||
clearQueue: "キューにさいなら"
|
||||
clearQueueConfirmTitle: "キューをクリアしまっか?"
|
||||
clearQueueConfirmText: "未配達の投稿は配送されなくなるで。通常この操作を行う必要はあらへんや。"
|
||||
clearCachedFiles: "キャッシュにさいなら"
|
||||
clearCachedFilesConfirm: "キャッシュされとるリモートファイルをみんなほかしてええか?"
|
||||
blockedInstances: "インスタンスブロック"
|
||||
blockedInstancesDescription: "ブロックしたいインスタンスのホストを改行で区切って設定してな。ブロックされてもうたインスタンスとはもう金輪際やり取りできひんくなるで。"
|
||||
blockedInstances: "ブロックしたサーバー"
|
||||
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定してな。ブロックされてもうたサーバーとはもう金輪際やり取りできんくなるで。"
|
||||
muteAndBlock: "ミュートとブロック"
|
||||
mutedUsers: "ミュートしたユーザー"
|
||||
blockedUsers: "ブロックしたユーザー"
|
||||
noUsers: "ユーザーはおらへん"
|
||||
editProfile: "プロフィールをいじる"
|
||||
noteDeleteConfirm: "このノートを削除しまっか?"
|
||||
noteDeleteConfirm: "この投稿を削除しまっか?"
|
||||
pinLimitExceeded: "これ以上ピン留めできひん"
|
||||
intro: "Misskeyのインストールが完了してん!管理者アカウントを作ってや。"
|
||||
intro: "Firefishのインストールが完了してん!管理者アカウントを作ってや。"
|
||||
done: "でけた"
|
||||
processing: "処理しとる"
|
||||
preview: "プレビュー"
|
||||
|
@ -213,9 +216,9 @@ all: "みんな"
|
|||
subscribing: "購読しとる"
|
||||
publishing: "配信しとる"
|
||||
notResponding: "応答してへんで"
|
||||
instanceFollowing: "インスタンスのフォロー"
|
||||
instanceFollowers: "インスタンスのフォロワー\n"
|
||||
instanceUsers: "インスタンスのユーザー"
|
||||
instanceFollowing: "サーバーのフォロー"
|
||||
instanceFollowers: "サーバーのフォロワー"
|
||||
instanceUsers: "このサーバーの利用者"
|
||||
changePassword: "パスワード変える"
|
||||
security: "セキュリティ"
|
||||
retypedNotMatch: "そやないねん。"
|
||||
|
@ -239,7 +242,8 @@ saved: "保存したで!"
|
|||
messaging: "チャット"
|
||||
upload: "アップロード"
|
||||
keepOriginalUploading: "Retain the original image. "
|
||||
keepOriginalUploadingDescription: "When uploading the clip, the original version will be retained. Turning it of then uploading will produce images for public use. "
|
||||
keepOriginalUploadingDescription: "When uploading the clip, the original version will
|
||||
be retained. Turning it of then uploading will produce images for public use. "
|
||||
fromDrive: "ドライブから"
|
||||
fromUrl: "URLから"
|
||||
uploadFromUrl: "URLアップロード"
|
||||
|
@ -286,7 +290,7 @@ emptyDrive: "ドライブにはなんも残っとらん"
|
|||
emptyFolder: "ふぉろだーにはなんも残っとらん"
|
||||
unableToDelete: "消そうおもってんけどな、あかんかったわ"
|
||||
inputNewFileName: "今度のファイル名は何にするん?"
|
||||
inputNewDescription: "新しいキャプションを入力しましょ"
|
||||
inputNewDescription: "新しい説明文を入力しまひょ"
|
||||
inputNewFolderName: "今度のフォルダ名は何にするん?"
|
||||
circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーや。"
|
||||
hasChildFilesOrFolders: "このフォルダ、まだなんか入っとるから消されへん"
|
||||
|
@ -305,8 +309,8 @@ unwatch: "ウォッチやめる"
|
|||
accept: "ええで"
|
||||
reject: "あかん"
|
||||
normal: "ええ感じ"
|
||||
instanceName: "インスタンス名"
|
||||
instanceDescription: "インスタンスの紹介"
|
||||
instanceName: "サーバー名"
|
||||
instanceDescription: "サーバーの紹介"
|
||||
maintainerName: "管理者の名前"
|
||||
maintainerEmail: "管理者のメールアドレス"
|
||||
tosUrl: "利用規約のURL"
|
||||
|
@ -336,9 +340,9 @@ basicInfo: "基本情報"
|
|||
pinnedUsers: "ピン留めしたユーザー"
|
||||
pinnedUsersDescription: "「みつける」ページとかにピン留めしたいユーザーをここに書けばええんやで。他ん人との名前は改行で区切ればええんやで。"
|
||||
pinnedPages: "ピン留めページ"
|
||||
pinnedPagesDescription: "インスタンスのいっちゃん上にピン留めしたいページのパスを改行で区切って記述してな"
|
||||
pinnedPagesDescription: "サーバーのいっちゃん上にピン留めしたいページのパスを、改行で区切って記述してな。"
|
||||
pinnedClipId: "ピン留めするクリップのID"
|
||||
pinnedNotes: "ピン留めされとるノート"
|
||||
pinnedNotes: "ピン留めされとる投稿"
|
||||
hcaptcha: "hCaptcha(キャプチャ)"
|
||||
enableHcaptcha: "hCaptcha(キャプチャ)をつけとく"
|
||||
hcaptchaSiteKey: "サイトキー"
|
||||
|
@ -355,8 +359,8 @@ antennaSource: "受信ソース(このソースは食われへん)"
|
|||
antennaKeywords: "受信キーワード"
|
||||
antennaExcludeKeywords: "除外キーワード"
|
||||
antennaKeywordsDescription: "スペースで区切ったるとAND指定で、改行で区切ったるとOR指定や"
|
||||
notifyAntenna: "新しいノートを通知すんで"
|
||||
withFileAntenna: "なんか添付されたノートだけ"
|
||||
notifyAntenna: "新しい投稿を通知すんで"
|
||||
withFileAntenna: "ファイルが添付された投稿のみ"
|
||||
enableServiceworker: "ServiceWorkerをつこて"
|
||||
antennaUsersDescription: "ユーザー名を改行で区切ったってな"
|
||||
caseSensitive: "大文字と小文字は別もんや"
|
||||
|
@ -377,7 +381,7 @@ exploreFediverse: "Fediverseを探ってみる"
|
|||
popularTags: "人気のタグ"
|
||||
userList: "リスト"
|
||||
about: "情報"
|
||||
aboutFirefish: "Misskeyってなんや?"
|
||||
aboutFirefish: "Firefishってなんやねん?"
|
||||
administrator: "管理者"
|
||||
token: "トークン"
|
||||
twoStepAuthentication: "二段階認証"
|
||||
|
@ -420,7 +424,7 @@ text: "テキスト"
|
|||
enable: "有効にするで"
|
||||
next: "次"
|
||||
retype: "もっかい入力"
|
||||
noteOf: "{user}のノート"
|
||||
noteOf: "{user}の投稿"
|
||||
inviteToGroup: "グループに招く"
|
||||
quoteAttached: "引用付いとるで"
|
||||
quoteQuestion: "引用として添付してもええか?"
|
||||
|
@ -478,12 +482,13 @@ accountSettings: "アカウントの設定"
|
|||
promotion: "宣伝"
|
||||
promote: "宣伝"
|
||||
numberOfDays: "日数"
|
||||
hideThisNote: "このノートは表示せんでいい"
|
||||
showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示してや"
|
||||
hideThisNote: "この投稿は表示せんでいい"
|
||||
showFeaturedNotesInTimeline: "タイムラインにおすすめの投稿を表示してや"
|
||||
objectStorage: "オブジェクトストレージ"
|
||||
useObjectStorage: "オブジェクトストレージを使う"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBaseUrlDesc: "参照に使うにURLやで。CDNやProxyを使用してるんならそのURL、S3: 'https://<bucket>.s3.amazonaws.com'、GCSとかなら: 'https://storage.googleapis.com/<bucket>'。"
|
||||
objectStorageBaseUrlDesc: "参照に使うにURLやで。CDNやProxyを使用してるんならそのURL、S3: 'https://<bucket>.s3.amazonaws.com'、GCSとかなら:
|
||||
'https://storage.googleapis.com/<bucket>'。"
|
||||
objectStorageBucket: "Bucket"
|
||||
objectStorageBucketDesc: "使ってるサービスのbucket名を選んでな"
|
||||
objectStoragePrefix: "Prefix"
|
||||
|
@ -500,7 +505,7 @@ objectStorageSetPublicRead: "アップロードした時に'public-read'を設
|
|||
serverLogs: "サーバーログ"
|
||||
deleteAll: "全て削除してや"
|
||||
showFixedPostForm: "タイムラインの上の方で投稿できるようにやってくれへん?"
|
||||
newNoteRecived: "新しいノートがあるで"
|
||||
newNoteRecived: "新しい投稿があるで"
|
||||
sounds: "サウンド"
|
||||
listen: "聴く"
|
||||
none: "なし"
|
||||
|
@ -523,7 +528,7 @@ sort: "仕分ける"
|
|||
ascendingOrder: "小さい順"
|
||||
descendingOrder: "大きい順"
|
||||
scratchpad: "スクラッチパッド"
|
||||
scratchpadDescription: "スクラッチパッドではAiScriptを色々試すことができるんや。Misskeyに対して色々できるコードを書いて動かしてみたり、結果を見たりできるで。"
|
||||
scratchpadDescription: "スクラッチパッドではAiScriptを色々試すことができるんや。Firefishに対して色々できるコードを書いて動かしてみたり、結果を見たりできるで。"
|
||||
output: "出力"
|
||||
script: "スクリプト"
|
||||
disablePagesScript: "Pagesのスクリプトを無効にしてや"
|
||||
|
@ -531,7 +536,7 @@ updateRemoteUser: "リモートユーザー情報の更新してくれん?"
|
|||
deleteAllFiles: "すべてのファイルを削除"
|
||||
deleteAllFilesConfirm: "ホンマにすべてのファイルを削除するん?消したもんはもう戻ってこんのやで?"
|
||||
removeAllFollowing: "フォローを全解除"
|
||||
removeAllFollowingDescription: "{host}からのフォローをすべて解除するで。そのインスタンスが消えて無くなった時とかには便利な機能やで。"
|
||||
removeAllFollowingDescription: "{host}からのフォローをすべて解除するで。そのサーバーが消えて無くなった時とかに便利な機能やで。"
|
||||
userSuspended: "このユーザーは...凍結されとる。"
|
||||
userSilenced: "このユーザーは...サイレンスされとる。"
|
||||
yourAccountSuspendedTitle: "あんたのアカウント凍結されとるで"
|
||||
|
@ -555,8 +560,8 @@ disablePlayer: "プレイヤーを閉じる"
|
|||
expandTweet: "ツイートを展開する"
|
||||
themeEditor: "テーマエディター"
|
||||
description: "説明"
|
||||
describeFile: "キャプションを付ける"
|
||||
enterFileDescription: "キャプションを入力"
|
||||
describeFile: "画像説明文を付ける"
|
||||
enterFileDescription: ""
|
||||
author: "作者"
|
||||
leaveConfirm: "未保存の変更があるで!ほかしてええか?"
|
||||
manage: "管理"
|
||||
|
@ -595,7 +600,7 @@ testEmail: "配信テスト"
|
|||
wordMute: "ワードミュート"
|
||||
regexpError: "正規表現エラー"
|
||||
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが出てきたで:"
|
||||
instanceMute: "インスタンスミュート"
|
||||
instanceMute: "サーバーミュート"
|
||||
userSaysSomething: "{name}が何か言ったようやで"
|
||||
makeActive: "使うで"
|
||||
display: "表示"
|
||||
|
@ -621,20 +626,20 @@ sample: "サンプル"
|
|||
abuseReports: "通報"
|
||||
reportAbuse: "通報"
|
||||
reportAbuseOf: "{name}を通報する"
|
||||
fillAbuseReportDescription: "細かい通報理由を書いてなー。対象ノートがある時はそのURLも書いといてなー。"
|
||||
fillAbuseReportDescription: "細かい通報理由を書いてなー。特定の投稿を通報するなら、そのURLも書いといてなー。"
|
||||
abuseReported: "無事内容が送信されたみたいやで。おおきに〜。"
|
||||
reporter: "通報者"
|
||||
reporteeOrigin: "通報先"
|
||||
reporterOrigin: "通報元"
|
||||
forwardReport: "リモートインスタンスに通報を転送するで"
|
||||
forwardReportIsAnonymous: "リモートインスタンスからはあんたの情報は見れへんくって、匿名のシステムアカウントとして表示されるで。"
|
||||
forwardReport: "リモートサーバーに通報を転送するで"
|
||||
forwardReportIsAnonymous: "リモートサーバーからはあんたの情報は見れへんくて、匿名のシステムアカウントとして表示されるで。"
|
||||
send: "送信"
|
||||
abuseMarkAsResolved: "対応したで"
|
||||
openInNewTab: "新しいタブで開く"
|
||||
openInSideView: "サイドビューで開く"
|
||||
defaultNavigationBehaviour: "デフォルトのナビゲーション"
|
||||
editTheseSettingsMayBreakAccount: "このへんの設定をようわからんままイジるとアカウントが壊れて使えんくなるかも知れへんで?"
|
||||
instanceTicker: "ノートのインスタンス情報"
|
||||
instanceTicker: "投稿のサーバー情報"
|
||||
waitingFor: "{x}を待っとるで"
|
||||
random: "ランダム"
|
||||
system: "システム"
|
||||
|
@ -645,16 +650,16 @@ createNew: "新しく作るで"
|
|||
optional: "任意"
|
||||
createNewClip: "新しいクリップを作るで"
|
||||
unclip: "クリップ解除するで"
|
||||
confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれとるで。ノートをこのクリップから除外したる?"
|
||||
confirmToUnclipAlreadyClippedNote: "この投稿はすでにクリップ「{name}」に含まれとるで。投稿をこのクリップから除外したる?"
|
||||
public: "パブリック"
|
||||
i18nInfo: "Firefishは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。"
|
||||
manageAccessTokens: "アクセストークンの管理"
|
||||
accountInfo: "アカウント情報"
|
||||
notesCount: "ノートの数やで"
|
||||
notesCount: "投稿の数やで"
|
||||
repliesCount: "返信した数やで"
|
||||
renotesCount: "Renoteした数やで"
|
||||
renotesCount: "ブーストした数やで"
|
||||
repliedCount: "返信された数やで"
|
||||
renotedCount: "Renoteされた数やで"
|
||||
renotedCount: "ブーストされた数やで"
|
||||
followingCount: "フォロー数やで"
|
||||
followersCount: "フォロワー数やで"
|
||||
sentReactionsCount: "リアクションした数やで"
|
||||
|
@ -666,15 +671,15 @@ no: "いいえ"
|
|||
driveFilesCount: "ドライブのファイル数"
|
||||
driveUsage: "ドライブ使用量やで"
|
||||
noCrawle: "クローラーによるインデックスを拒否するで"
|
||||
noCrawleDescription: "検索エンジンにあんたのユーザーページ、ノート、Pagesとかのコンテンツを登録(インデックス)せぇへんように頼むで。"
|
||||
lockedAccountInfo: "フォローを承認制にしとっても、ノートの公開範囲を「フォロワー」にせぇへん限り、誰でもあんたのノートを見れるで。"
|
||||
noCrawleDescription: "検索エンジンにあんたのプロフィール、投稿、ページとかのコンテンツを登録(インデックス)せぇへんように頼むで。"
|
||||
lockedAccountInfo: "フォローを承認制にしとっても、投稿の公開範囲を「フォロワー」にせん限り、誰でもあんたの投稿を見れるで。"
|
||||
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで"
|
||||
loadRawImages: "添付画像のサムネイルをオリジナル画質にするで"
|
||||
disableShowingAnimatedImages: "アニメーション画像を再生しやへんで"
|
||||
verificationEmailSent: "無事確認のメールを送れたで。メールに書いてあるリンクにアクセスして、設定を完了してなー。"
|
||||
notSet: "未設定"
|
||||
emailVerified: "メールアドレスは確認されたで"
|
||||
noteFavoritesCount: "お気に入りノートの数やで"
|
||||
noteFavoritesCount: "お気に入り投稿の数やで"
|
||||
pageLikesCount: "Pageにええやんと思った数"
|
||||
pageLikedCount: "Pageにええやんと思ってくれた数"
|
||||
contact: "連絡先"
|
||||
|
@ -684,7 +689,7 @@ experimentalFeatures: "実験的機能やで"
|
|||
developer: "開発者やで"
|
||||
makeExplorable: "アカウントを見つけやすくするで"
|
||||
makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らんくなるで。"
|
||||
showGapBetweenNotesInTimeline: "タイムラインのノートを放して表示するで"
|
||||
showGapBetweenNotesInTimeline: "タイムライン上の投稿を離して表示するで"
|
||||
duplicate: "複製"
|
||||
left: "左"
|
||||
center: "中央"
|
||||
|
@ -696,9 +701,10 @@ showTitlebar: "タイトルバーを見せる"
|
|||
clearCache: "キャッシュをほかす"
|
||||
onlineUsersCount: "{n}人が起きとるで"
|
||||
nUsers: "{n}ユーザー"
|
||||
nNotes: "{n}ノート"
|
||||
nNotes: "{n}投稿"
|
||||
sendErrorReports: "エラーリポートを送る"
|
||||
sendErrorReportsDescription: "オンにしたら、なんか変なことが起きたときにエラーの詳細がMisskeyに共有されて、ソフトウェアの品質向上に役立てられるんや。エラー情報には、OSのバージョン、ブラウザの種類、行動履歴などが含まれるで。"
|
||||
sendErrorReportsDescription: "オンにしたら、なんやけったいなことが起きたときにエラーの詳細がFirefishに共有されて、ソフトウェアの品質向上に役立てられるんや。\n\
|
||||
エラー情報には、OSのバージョン、ブラウザの種類、行動履歴などが含まれるで。"
|
||||
myTheme: "マイテーマ"
|
||||
backgroundColor: "背景"
|
||||
accentColor: "アクセント"
|
||||
|
@ -722,7 +728,7 @@ capacity: "容量"
|
|||
inUse: "使用中"
|
||||
editCode: "コードを編集"
|
||||
apply: "適用"
|
||||
receiveAnnouncementFromInstance: "インスタンスからのお知らせを受け取る"
|
||||
receiveAnnouncementFromInstance: "サーバーからのお知らせを受け取る"
|
||||
emailNotification: "メール通知"
|
||||
publish: "公開"
|
||||
inChannelSearch: "チャンネル内検索"
|
||||
|
@ -737,7 +743,7 @@ unlikeConfirm: "いいね解除するんか?"
|
|||
fullView: "フルビュー"
|
||||
quitFullView: "フルビュー解除"
|
||||
addDescription: "説明を追加するで"
|
||||
userPagePinTip: "個々のノートのメニューから「ピン留め」を選んどくと、ここにノートを表示しておけるで。"
|
||||
userPagePinTip: "個々の投稿のメニューから「ピン留め」を選んどくと、ここにそいつを表示しておけるで。"
|
||||
notSpecifiedMentionWarning: "宛先に含まれてへんメンションがあるで"
|
||||
info: "情報"
|
||||
userInfo: "ユーザー情報やで"
|
||||
|
@ -750,7 +756,7 @@ active: "アクティブ"
|
|||
offline: "オフライン"
|
||||
notRecommended: "あんま推奨しやんで"
|
||||
botProtection: "Botプロテクション"
|
||||
instanceBlocking: "インスタンスブロック"
|
||||
instanceBlocking: "連合の管理"
|
||||
selectAccount: "アカウントを選んでなー"
|
||||
switchAccount: "アカウントを変えるで"
|
||||
enabled: "有効"
|
||||
|
@ -767,7 +773,7 @@ postToGallery: "ギャラリーへ投稿"
|
|||
gallery: "ギャラリー"
|
||||
recentPosts: "最近の投稿"
|
||||
popularPosts: "人気の投稿"
|
||||
shareWithNote: "ノートで共有"
|
||||
shareWithNote: "投稿で共有"
|
||||
ads: "広告"
|
||||
expiration: "期限"
|
||||
memo: "メモ"
|
||||
|
@ -789,7 +795,7 @@ hashtags: "ハッシュタグ"
|
|||
troubleshooting: "トラブルシューティング"
|
||||
useBlurEffect: "UIにぼかし効果を使うで"
|
||||
learnMore: "詳しく"
|
||||
misskeyUpdated: "Misskeyが更新されたで!\nモデレーターの人らに感謝せなあかんで"
|
||||
misskeyUpdated: "Firefishが更新されたで!\nモデレーターの人らに感謝やね"
|
||||
whatIsNew: "更新情報を見るで"
|
||||
translate: "翻訳"
|
||||
translatedFrom: "{x}から翻訳するで"
|
||||
|
@ -834,7 +840,7 @@ cannotUploadBecauseInappropriate: "不適切な内容を含むかもしれへん
|
|||
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いでアップロードできまへん。"
|
||||
beta: "ベータ"
|
||||
enableAutoSensitive: "自動NSFW判定"
|
||||
enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、インスタンスによっては自動で設定されることがあるで。"
|
||||
enableAutoSensitiveDescription: "いけるときは、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、サーバーによっては自動で設定されることがあるで。"
|
||||
activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかとかを判定して積極的に行うで。オフにすると単に文字列として正しいかどうかだけチェックするで。"
|
||||
navbar: "ナビゲーションバー"
|
||||
shuffle: "シャッフルするで"
|
||||
|
@ -868,14 +874,15 @@ _registry:
|
|||
domain: "ドメイン"
|
||||
createKey: "キーを作る"
|
||||
_aboutFirefish:
|
||||
about: "Misskeyはsyuiloが2014年からずっと作ってはる、オープンソースなソフトウェアや。"
|
||||
about: "Firefishは、ThatOneCalculatorが2022年にMisskeyをいじって作った、オープンなソースのソフトウェアや。"
|
||||
contributors: "主な貢献者"
|
||||
allContributors: "全ての貢献者"
|
||||
source: "ソースコード"
|
||||
translation: "Misskeyを翻訳"
|
||||
donate: "Misskeyに寄付"
|
||||
translation: "Firefishを翻訳"
|
||||
donate: "Firefishに寄付"
|
||||
morePatrons: "他にもぎょうさんの人からサポートしてもろてんねん。ほんまおおきに🥰"
|
||||
patrons: "支援者"
|
||||
misskeyContributors: フォーク元のMisskeyを作らはった人ら
|
||||
_mfm:
|
||||
cheatSheet: "MFMチートシート"
|
||||
mention: "メンション"
|
||||
|
@ -896,6 +903,7 @@ _mfm:
|
|||
blur: "ぼかし"
|
||||
font: "フォント"
|
||||
rotate: "回転"
|
||||
intro: MFMは、MisskeyやFirefish、Akkomaなどの様々な場所で使用できるマークアップ言語なんよ。ここでは、MFMで使用可能な構文一覧が確認できるで。
|
||||
_instanceTicker:
|
||||
none: "表示せん"
|
||||
remote: "リモートユーザーに表示"
|
||||
|
@ -958,7 +966,7 @@ _theme:
|
|||
hashtag: "ハッシュタグ"
|
||||
mention: "メンション"
|
||||
mentionMe: "うち宛てのメンション"
|
||||
renote: "Renote"
|
||||
renote: "ブースト"
|
||||
modalBg: "モーダルの背景"
|
||||
divider: "分割線"
|
||||
scrollbarHandle: "スクロールバーの取っ手"
|
||||
|
@ -985,8 +993,8 @@ _theme:
|
|||
accentLighten: "アクセント (明るめ)"
|
||||
fgHighlighted: "強調されとる文字"
|
||||
_sfx:
|
||||
note: "ノート"
|
||||
noteMy: "ノート(自分)"
|
||||
note: "投稿"
|
||||
noteMy: "投稿(自分)"
|
||||
notification: "通知"
|
||||
chat: "チャット"
|
||||
_ago:
|
||||
|
@ -1017,8 +1025,8 @@ _permissions:
|
|||
_auth:
|
||||
permissionAsk: "このアプリは次の権限を要求しとるで"
|
||||
_antennaSources:
|
||||
all: "みんなのノート"
|
||||
homeTimeline: "フォローしとるユーザーのノート"
|
||||
all: "みんなの投稿"
|
||||
homeTimeline: "フォローしとるユーザーの投稿"
|
||||
_weekday:
|
||||
sunday: "日曜日"
|
||||
monday: "月曜日"
|
||||
|
@ -1072,7 +1080,7 @@ _profile:
|
|||
name: "名前"
|
||||
username: "ユーザー名"
|
||||
_exportOrImport:
|
||||
allNotes: "全てのノート"
|
||||
allNotes: "すべての投稿"
|
||||
followingList: "フォロー"
|
||||
muteList: "ミュート"
|
||||
blockingList: "ブロック"
|
||||
|
@ -1082,10 +1090,10 @@ _charts:
|
|||
apRequest: "リクエスト"
|
||||
usersTotal: "ユーザーの合計"
|
||||
activeUsers: "アクティブユーザー数"
|
||||
notesIncDec: "ノートの増減"
|
||||
localNotesIncDec: "ローカルのノートの増減"
|
||||
remoteNotesIncDec: "リモートのノートの増減"
|
||||
notesTotal: "ノートの合計"
|
||||
notesIncDec: "投稿の増減"
|
||||
localNotesIncDec: "ローカルの投稿の増減"
|
||||
remoteNotesIncDec: "リモートの投稿の増減"
|
||||
notesTotal: "投稿の合計"
|
||||
filesIncDec: "ファイルの増減"
|
||||
filesTotal: "ファイルの合計"
|
||||
storageUsageIncDec: "ストレージ使用量の増減"
|
||||
|
@ -1094,8 +1102,8 @@ _instanceCharts:
|
|||
requests: "リクエスト"
|
||||
users: "ユーザーの増減"
|
||||
usersTotal: "ユーザーの累積"
|
||||
notes: "ノートの増減"
|
||||
notesTotal: "ノートの累積"
|
||||
notes: "投稿の増減"
|
||||
notesTotal: "投稿の累積"
|
||||
ff: "フォロー/フォロワーの増減"
|
||||
ffTotal: "フォロー/フォロワーの累積"
|
||||
cacheSize: "キャッシュサイズの増減"
|
||||
|
@ -1165,9 +1173,9 @@ _pages:
|
|||
id: "キャンバスID"
|
||||
width: "幅"
|
||||
height: "高さ"
|
||||
note: "ノート埋め込み"
|
||||
note: "投稿の埋め込み"
|
||||
_note:
|
||||
id: "ノートID"
|
||||
id: "投稿のID"
|
||||
detailed: "詳細な表示"
|
||||
switch: "スイッチ"
|
||||
_switch:
|
||||
|
@ -1385,14 +1393,14 @@ _notification:
|
|||
all: "すべて"
|
||||
follow: "フォロー"
|
||||
mention: "メンション"
|
||||
renote: "Renote"
|
||||
renote: "ブースト"
|
||||
quote: "引用"
|
||||
reaction: "リアクション"
|
||||
receiveFollowRequest: "フォロー許可してほしいみたいやで"
|
||||
followRequestAccepted: "フォローが受理されたで"
|
||||
_actions:
|
||||
reply: "返事"
|
||||
renote: "Renote"
|
||||
renote: "ブースト"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "いつもメインカラムを表示"
|
||||
columnAlign: "カラムの寄せ"
|
||||
|
@ -1413,3 +1421,28 @@ _deck:
|
|||
list: "リスト"
|
||||
mentions: "あんた宛て"
|
||||
direct: "ダイレクト"
|
||||
_experiments:
|
||||
postImportsCaption:
|
||||
ユーザーが過去の投稿をFirefish・Misskey・Mastodon・Akkoma・Pleromaからインポートできるようにするで。キューが溜まっとるときにインポートするとサーバーに負荷がかかるかもしれんね。
|
||||
searchPlaceholder: Firefishを検索
|
||||
addInstance: サーバーを追加
|
||||
editNote: 投稿を編集
|
||||
edited: '編集済み: {date} {time}'
|
||||
deleted: 削除済み
|
||||
noThankYou: いらんわ
|
||||
_tutorial:
|
||||
step3_1: ほな、何人かフォローしてみまひょ
|
||||
step1_1: おこしやす
|
||||
step1_2: 使い始める前に、いくつか設定を済ませまひょ。すぐできますえ。
|
||||
step2_1: 最初に、あんさんのプロフィールを作りまひょ
|
||||
step2_2: プロフィールを設定しはることで、他ん人があんさんの投稿を見たり、フォローしたりするときの助けになってます。
|
||||
_postForm:
|
||||
_placeholders:
|
||||
b: なんかおましたか?
|
||||
e: ここに書いとくれやす
|
||||
c: なに考えとりまっか?
|
||||
d: なんや言いたいんちゃいますか?
|
||||
f: あんさん書くんを待っとるんどす...
|
||||
flagSpeakAsCat: 猫弁で話す
|
||||
flagSpeakAsCatDescription: 猫モードが有効の場合にオンにすると、ワレの投稿の「な」を「にゃ」に変換するで。
|
||||
welcomeBackWithName: おおきに、{name}はん
|
||||
|
|
|
@ -44,7 +44,7 @@ lists: Lister
|
|||
listsDesc: Lister lar deg lage tidslinjer med utvalgte brukere. De kan hentes frem
|
||||
fra tidslinje-siden.
|
||||
deleted: Slettet
|
||||
editNote: Rediger notat
|
||||
editNote: Rediger post
|
||||
followsYou: Følger deg
|
||||
createList: Lag liste
|
||||
newer: nyere
|
||||
|
@ -155,7 +155,7 @@ drive: Disk
|
|||
renameFile: Omdøp fil
|
||||
folderName: Katalognavn
|
||||
createFolder: Opprett katalog
|
||||
inputNewDescription: Oppgi ny bildetekst
|
||||
inputNewDescription: Skriv ny beskrivelse
|
||||
inputNewFolderName: Oppgi nytt katalognavn
|
||||
copyUrl: Kopier URL
|
||||
hcaptchaSiteKey: hCaptcha-nøkkel for nettstedet
|
||||
|
@ -409,7 +409,7 @@ nothing: Ikke noe å se her
|
|||
deleteAllFilesConfirm: Er du sikker på at du vil slette alle filer?
|
||||
updateRemoteUser: Oppdater informasjon om ekstern bruker
|
||||
deleteAllFiles: Slett alle filer
|
||||
enterFileDescription: Legg til bildetekst
|
||||
enterFileDescription: Skriv inn beskrivelse
|
||||
leaveConfirm: Det er ulagrede endringer. Vil du forkaste dem?
|
||||
enableAll: Slå på alle
|
||||
generateAccessToken: Generer adgangstegn
|
||||
|
@ -506,7 +506,7 @@ yourAccountSuspendedDescription: Denne kontoen er suspendert fordi den har brutt
|
|||
useCw: Skjul innhold
|
||||
enablePlayer: Åpne videospiller
|
||||
disablePlayer: Lukk videospiller
|
||||
describeFile: Legg til tekst
|
||||
describeFile: Legg til beskrivelse
|
||||
author: Forfatter
|
||||
useFullReactionPicker: Bruk reaksjonsvelger i full størrelse
|
||||
width: Bredde
|
||||
|
@ -718,3 +718,405 @@ alwaysMarkSensitive: Merk som "Sensitivt innhold" som standard
|
|||
verificationEmailSent: En verifiserings-epost er sendt. Følg lenken i eposten for
|
||||
å fullføre verifiseringen.
|
||||
newNoteRecived: Det er nye poster
|
||||
scratchpadDescription: Kladdeblokka gir deg et miljø for å eksperimentere med AiScript.
|
||||
Du kan skrive, kjøre og sjekke resultatene av at koden interagerer med Firefish.
|
||||
disablePagesScript: Slå av AiScript på Sider
|
||||
expandTweet: Ekspander tweet
|
||||
public: Offentlig
|
||||
clearCache: Slett mellomlager
|
||||
onlineUsersCount: '{n} brukere er innlogget'
|
||||
nNotes: '{n} poster'
|
||||
sendErrorReports: Send feilmeldinger
|
||||
deleteConfirm: Virkelig slette?
|
||||
latestVersion: Nyeste versjon
|
||||
receiveAnnouncementFromInstance: Motta varsler fra denne tjeneren
|
||||
inChannelSearch: Søk i kanal
|
||||
selectAccount: Velg konto
|
||||
switch: Bytt
|
||||
instanceDefaultDarkTheme: Standard mørkt tema på tjeneren
|
||||
oneDay: En dag
|
||||
driveCapOverrideCaption: Tilbakestill kapasiteten til standardverdien ved å legge
|
||||
inn en verdi på 0 eller lavere.
|
||||
sendModMail: Send modereringsvarsel
|
||||
enableServerMachineStats: Slå på hardware-statistikk for tjeneren
|
||||
_gallery:
|
||||
liked: Likte poster
|
||||
unlike: Fjern lik
|
||||
my: Mitt galleri
|
||||
like: Lik
|
||||
_preferencesBackups:
|
||||
loadFile: Last fra fil
|
||||
cannotSave: Lagring feilet
|
||||
deleteConfirm: Vil du slette sikkerhetskopien "{name}"?
|
||||
saveConfirm: Lagre sikkerhetskopi som "{name}"?
|
||||
noBackups: Ingen sikkerhetskopier er tatt. Du kan ta en backup av klientinnstillingene
|
||||
dine på denne tjeneren ved å trykke "Lag ny sikkerhetskopi".
|
||||
applyConfirm: Ønsker du å laste inn sikkerhetskopien "{name}" på denne enheten?
|
||||
Eksisterende innstillinger vil bli overskrevet.
|
||||
save: Lagre endringer
|
||||
nameAlreadyExists: En sikkerhetskopi med navnet "{name}" finnes allerede. Skriv
|
||||
inn et annet navn.
|
||||
createdAt: 'Opprettet: {date} {time}'
|
||||
apply: Bruk på denne enheten
|
||||
renameConfirm: Endre navn på sikkerhetskopien fra "{old}" to "{new}"?
|
||||
list: Opprettede sikkerhetskopier
|
||||
saveNew: Ta ny sikkerhetskopi
|
||||
inputName: Gi sikkerhetskopien et navn
|
||||
updatedAt: 'Oppdatert: {date} {time}'
|
||||
cannotLoad: Innlasting feilet
|
||||
invalidFile: Ugyldig filformat
|
||||
_ad:
|
||||
back: Tilbake
|
||||
reduceFrequencyOfThisAd: Vis annonsen sjeldnere
|
||||
_mfm:
|
||||
cheatSheet: Jukseark for tekstmarkering (MFM)
|
||||
stop: Stopp animert markeringsspråk (MFM)
|
||||
warn: Markeringsspråket (MFM) kan inneholde bevegelige eller blinkende animasjoner
|
||||
alwaysPlay: Alltid spill av animert tekstmarkering (MFM)
|
||||
play: Spill animert markeringsspråk (MFM)
|
||||
intro: MFM er et markeringsspråk som burkes av Misskey, Firefish, Akkoma og andre.
|
||||
Her kan du se en liste over tilgjengelig MFM-syntaks.
|
||||
reactionPickerSkinTone: Foretrukket hudfarge i emojier
|
||||
switchUi: Visningsoppsett
|
||||
usageAmount: Bruk
|
||||
memo: Memo
|
||||
priority: Prioritet
|
||||
high: Høy
|
||||
secureMode: Sikker modus (Autorisert henting)
|
||||
requireAdminForView: Du må logge inn på en administratorkonto for å se dette.
|
||||
typeToConfirm: Skriv inn {x} for å bekrefte
|
||||
replayTutorial: Kjør introduksjon på nytt
|
||||
moveTo: Flytt denne kontoen til en ny konto
|
||||
objectStorageBucketDesc: Skriv inn navnet på bøtta hos lagringstjenesten.
|
||||
notRecommended: Ikke anbefalt
|
||||
voteConfirm: Bekreft din stemme på "{choice}"?
|
||||
oneHour: En time
|
||||
_plugin:
|
||||
installWarn: Ikke installer utvidelser du ikke stoler på.
|
||||
install: Installer innstikk
|
||||
manage: Oppsett av innstikk
|
||||
preventAiLearning: Hindre tråling fra AI-boter
|
||||
reporterOrigin: Kilden til den som rapporterer
|
||||
center: Sentrert
|
||||
wide: Bred
|
||||
value: Verdi
|
||||
createdAt: Opprettet
|
||||
active: Aktiv
|
||||
hideOnlineStatus: Skjul om du er pålogget
|
||||
troubleshooting: Problemløsing
|
||||
useBlurEffect: Bruk diffuseringseffekter i brukergrensesnittet
|
||||
learnMore: Lær mer
|
||||
usernameInfo: Et navn som identifiserer din konto på denne tjeneren. Du kan bruke
|
||||
alfabetet (a-z,A-Z), sifre (0-9) og understrek (_). Brukernavn kan ikke endres senere.
|
||||
resolved: Løst
|
||||
unresolved: Uløst
|
||||
welcomeBackWithName: Velkommen tilbake, {name}
|
||||
clickToFinishEmailVerification: Klikk [{ok}] for å fullføre epost-verifisering.
|
||||
cropImage: Beskjær bilde
|
||||
numberOfPageCacheDescription: En økning i dette tallet vil gjøre brukeropplevelsen
|
||||
bedre, men gi mer jobb til tjeneren og kreve mer minne.
|
||||
logoutConfirm: Vil du logge ut?
|
||||
numberOfPageCache: Antall mellomlagrede sider
|
||||
lastActiveDate: Sist brukt
|
||||
refreshInterval: 'Oppdateringsintervall '
|
||||
swipeOnDesktop: Tillat mobil-lignende sveiping på skrivebords-PC
|
||||
migration: Migrering
|
||||
useDrawerReactionPickerForMobile: Vis reaksjosnvelger som en skuff på mobil
|
||||
numberOfColumn: Antall kolonner
|
||||
searchByGoogle: Søk
|
||||
oneWeek: En uke
|
||||
file: Fil
|
||||
recentNHours: Siste {n} timer
|
||||
noEmailServerWarning: E-post-tjener er ikke konfigurert.
|
||||
thereIsUnresolvedAbuseReportWarning: Det er uløste rapporter.
|
||||
colored: I farger
|
||||
recommendedInstancesDescription: Anbefalte tjenere skilt med linjeskift for visning
|
||||
i anbefalt-tidslinjen.
|
||||
caption: Automatisk beskrivelse
|
||||
updateAvailable: En oppdatering kan være tilgjengelig!
|
||||
accentColor: Uthevet farge
|
||||
textColor: Skriftfarge
|
||||
saveAs: Lagre som...
|
||||
swipeOnMobile: Tillat sveiping mellom sider
|
||||
_accountDelete:
|
||||
inProgress: Sletting pågår
|
||||
remote: Ekstern
|
||||
total: Total
|
||||
registry: Register
|
||||
closeAccount: Avslutt konto
|
||||
currentVersion: Nåværende versjon
|
||||
fullView: Full visning
|
||||
gallery: Galleri
|
||||
emailNotConfiguredWarning: E-post-adresse er ikke satt.
|
||||
allowedInstancesDescription: Tjenernavn for tjenere som skal hvitelistes. En per linje.
|
||||
(Vil bare bli brukt i privat modus).
|
||||
previewNoteText: Forhåndsvisning
|
||||
recentNDays: Siste {n} dager
|
||||
indexPosts: Indekser poster
|
||||
objectStorageUseProxy: Koble til gjennom en mellomtjener
|
||||
objectStorageUseProxyDesc: Skru av dette dersom du ikke vil bruke mellomtjenere for
|
||||
API-oppkoblinger
|
||||
masterVolume: Hovedvolum
|
||||
script: Skript
|
||||
divider: Skille
|
||||
addItem: Legg til element
|
||||
manage: Oppsett
|
||||
notificationType: Varseltype
|
||||
useBlurEffectForModal: Bruk diffus-effekt for modale brukergrensesnitt-elementer
|
||||
driveFilesCount: Antall filer på Disk
|
||||
showGapBetweenNotesInTimeline: Legg inn et tomrom mellom postene i tidslinjen
|
||||
newVersionOfClientAvailable: En nyere versjon av klienten er tilgjengelig.
|
||||
capacity: Kapasitet
|
||||
inUse: Brukt
|
||||
publish: Publiser
|
||||
quickAction: Hurtigvalg
|
||||
privateMode: Privat modus
|
||||
customCss: Egendefinert CSS
|
||||
allowedInstances: Hvitelistede tjenere
|
||||
lastCommunication: Siste kommunikasjon
|
||||
breakFollowConfirm: Er du sikker på at du vil fjerne følgeren?
|
||||
filter: Filter
|
||||
makeReactionsPublicDescription: Dette vil gjøre listen over dine tidligere reaksjoner
|
||||
synlige for alle.
|
||||
indefinitely: Permanent
|
||||
tenMinutes: 10 minutter
|
||||
_email:
|
||||
_follow:
|
||||
title: Du har en ny følger
|
||||
_receiveFollowRequest:
|
||||
title: Du har mottatt en følgeforespørsel
|
||||
_registry:
|
||||
key: Nøkkel
|
||||
scope: Omfang
|
||||
domain: Domene
|
||||
createKey: Opprettet nøkkel
|
||||
keys: Nøkler
|
||||
sendErrorReportsDescription: "Detaljert feilinformasjon vli bli delt med utviklerne
|
||||
av Firefish, noe som hjelper til med feilretting og forbedring av programmet.\n
|
||||
- Dette inkluderer informasjon som f.eks. versjonen på operativsystemet og nettleseren
|
||||
din, og aktiviteten din i Firefish."
|
||||
_aboutFirefish:
|
||||
translation: Oversett Firefish
|
||||
donate: Donér til Firefish
|
||||
donateTitle: Liker du Firefish?
|
||||
pleaseDonateToFirefish: Du kan vurdere å donere en slant til Firefish for å støtte
|
||||
videre utvikling og feilretting.
|
||||
donateHost: Donér til {host}
|
||||
morePatrons: Vi er også takknemlige for bidragene fra mange andre som ikke er listet
|
||||
her. Takk til dere alle! 🥰
|
||||
contributors: Hovedutviklere
|
||||
source: Kildekode
|
||||
allContributors: Alle bidragsytere
|
||||
misskeyContributors: Misskeys bidragsytere
|
||||
pleaseDonateToHost: Du kan også vurdere å donere til hjemme-tjeneren din, {host},
|
||||
for å hjelpe dem med driftskostnadene for tjenesten.
|
||||
about: Firefish ble opprettet av ThatOneCalculator i 2022, basert på Misskey.
|
||||
sponsors: Firefishs sponsorer
|
||||
patrons: Firefishs patroner
|
||||
patronsList: Listen er kronologisk, ikke etter donert beløp. Doner med lenken over
|
||||
for å få navnet ditt her!
|
||||
isBot: Denne kontoen er en bot
|
||||
_nsfw:
|
||||
respect: Skjul NSFW-merket media
|
||||
force: Skjul alle media
|
||||
ignore: Ikke skjul NSFW-media
|
||||
disableAnimatedMfm: Slå av animert markeringsspråk
|
||||
objectStorageBucket: Bøtte
|
||||
scratchpad: Kladdeblokk
|
||||
plugins: Innstikk
|
||||
createNew: Lag ny
|
||||
makeExplorable: Gjør kontoen synlig i "Utforsk"
|
||||
needReloadToApply: Siden må lastes på nytt for at denne endringen skal tre inn.
|
||||
customCssWarn: Bruk denne innstillingen bare hvis du vet hva den gjør. Feil innstilling
|
||||
kan få klienten til å ikke fungere som den skal.
|
||||
low: Lav
|
||||
global: Global
|
||||
recommended: Anbefalt
|
||||
instanceSecurity: Tjenersikkerhet
|
||||
squareAvatars: Vis firkantede avatarer
|
||||
deleteAccount: Slett konto
|
||||
customKaTeXMacro: Egne KaTeX-makroer
|
||||
size: Størrelse
|
||||
fast: Raskt
|
||||
showAdminUpdates: Indikerer at en ny versjon av Firefish er tilgjengelig (bare admin)
|
||||
moveAccount: Flytt konto!
|
||||
license: Lisens
|
||||
wordMute: Ordstumming
|
||||
reporteeOrigin: Kilden til den som rapporteres
|
||||
accountInfo: Kontoinformasjon
|
||||
driveUsage: Brukt diskplass
|
||||
noCrawle: Stopp robot-indeksering
|
||||
noCrawleDescription: Be søkemotorer om å ikke indeksere din profil, poster, Sider
|
||||
etc.
|
||||
narrow: Smal
|
||||
reloadToApplySetting: Denne innstillingen aktiveres ikke før du laster siden på nytt.
|
||||
Vil du gjøre det nå?
|
||||
showTitlebar: Vis tittellinje
|
||||
nUsers: '{n} brukere'
|
||||
myTheme: Mitt tema
|
||||
backgroundColor: Bakgrunnsfarge
|
||||
advanced: Avansert
|
||||
updatedAt: Oppdatert
|
||||
editCode: Rediger kode
|
||||
addDescription: Legg til beskrivelse
|
||||
userPagePinTip: Du kan vise poster her ved å klikke "Fest til profil" fra menyen til
|
||||
en post.
|
||||
unknown: Ukjent
|
||||
onlineStatus: Påkoblet status
|
||||
online: Pålogget
|
||||
offline: Ikke pålogget
|
||||
instanceBlocking: Innstillinger for føderering
|
||||
accounts: Kontoer
|
||||
noBotProtectionWarning: Bot-beskyttelse er ikke konfigurert.
|
||||
configure: Konfigurer
|
||||
postToGallery: Lag ny galleripost
|
||||
recentPosts: Nylige sider
|
||||
popularPosts: Populære sider
|
||||
shareWithNote: Del med post
|
||||
expiration: Frist
|
||||
middle: Medium
|
||||
sent: Sendt
|
||||
makeReactionsPublic: La reaksjonshistorikken være offentlig
|
||||
classic: Sentrert
|
||||
muteThread: Stum en tråd
|
||||
ffVisibilityDescription: Lar deg konfigurere hvem som kan se hvem du følger og hvem
|
||||
som følger deg.
|
||||
continueThread: Fortsett tråd
|
||||
deleteAccountConfirm: Dette vil slette kontoen, og det går ikke å omgjøre etterpå.
|
||||
Fortsette?
|
||||
hide: Skjul
|
||||
ffVisibility: Synlighet av følgere og folk du følger
|
||||
leaveGroup: Forlat gruppe
|
||||
leaveGroupConfirm: Er du sikker på at du vil forlate "{name}"?
|
||||
overridedDeviceKind: Enhetstype
|
||||
smartphone: Smarttelefon
|
||||
tablet: Nettbrett
|
||||
auto: Automatisk
|
||||
image: Bilde
|
||||
video: Video
|
||||
driveCapOverrideLabel: Endre brukerens lagringskapasitet
|
||||
isSystemAccount: Denne kontoen er opprettet og kontrollert av systemet. Ikke moderer,
|
||||
rediger, slett eller på annen måte endre noe ved denne kontoen. Tjeneren kan slutte
|
||||
å virke som den skal.
|
||||
document: Dokumentasjon
|
||||
statusbar: Statuslinje
|
||||
pleaseSelect: Velg en innstilling
|
||||
reverse: Reverser
|
||||
slow: Sakte
|
||||
moveFromLabel: 'Kontoen du flytter fra:'
|
||||
silencedWarning: Denne siden vises fordi disse brukerne er fra tjenere administratoren
|
||||
din har stummet, så de kan potensielt inneholde spam.
|
||||
ads: Samfunnsbanner
|
||||
_forgotPassword:
|
||||
contactAdmin: Denne tjeneren støtter ikke bruk av e-post-adresser for gjenoppretting
|
||||
av passord. Kontakt administratoren for tjeneren.
|
||||
enterEmail: Skriv inn e-post-adressen du brukte da du registrerte kontoen. Du vil
|
||||
motta en e-post med en lenke som lar deg endre passordet.
|
||||
ifNoEmail: Dersom du ikke oppga en e-post-adresse da du registrerte kontoen, kontakt
|
||||
administrator i stedet.
|
||||
breakFollow: Slett følger
|
||||
unmuteThread: Fjern stumming av tråden
|
||||
incorrectPassword: Feil passord.
|
||||
logoImageUrl: URL til logo-bilde
|
||||
apps: Apper
|
||||
audio: Lyd
|
||||
moveToLabel: 'Kontoen du flytter til:'
|
||||
moveFrom: Flytt fra en annen konto til denne kontoen
|
||||
migrationConfirm: "Er du helt sikker på at du ønsker å flytte kontoen din til {account}?
|
||||
Når du har gjort dette kan du ikke omgjøre det, og du vil ikke kunne bruke kontoen
|
||||
normalt etterpå.\nPass på at du setter den kontoen du er innlogget på her som kontoen
|
||||
du flytter fra."
|
||||
jumpToSpecifiedDate: Hopp til en gitt dato
|
||||
showingPastTimeline: Du ser nå en gammel tidslinje
|
||||
noMaintainerInformationWarning: Eierinformasjon er ikke konfigueret.
|
||||
notSpecifiedMentionWarning: Denne posten inneholder nevnelser av brukere som ikke
|
||||
er inkludert som mottakere
|
||||
saveConfirm: Lagre endringer?
|
||||
clear: Tøm
|
||||
switchAccount: Bytt konto
|
||||
enabled: Påslått
|
||||
disabled: Avslått
|
||||
user: Bruker
|
||||
administration: Konfigurasjon
|
||||
invalidValue: Ugyldig verdi.
|
||||
youAreRunningUpToDateClient: Du bruker nyeste versjon av klienten.
|
||||
noteId: Post-id
|
||||
noGraze: Slå av "Graze for Mastodon"-utdivdelsen i nettleseren. Den vil forstyrre
|
||||
Firefish.
|
||||
isModerator: Moderator
|
||||
isAdmin: Administrator
|
||||
objectStorageS3ForcePathStyle: Bruk sti-baserte URL-er til endepunktene
|
||||
objectStorageS3ForcePathStyleDesc: Skru på dette for å lage endpunkts-URL-er i formatet
|
||||
's3.amazonaws.com/<bøtte>/' i stedet for '<bøtte>.s3.amazonaws.com'.
|
||||
output: Utputt
|
||||
forwardReport: Videresend rapport til ekstern tjener
|
||||
forwardReportIsAnonymous: I stedet for din konto vil en anonym systemkonto bli vist
|
||||
som rapportør på den eksterne tjeneren.
|
||||
optional: Valgfritt
|
||||
manageAccessTokens: Styr adgangstegn
|
||||
experimentalFeatures: Eksperimentelle funksjoner
|
||||
developer: Utvikler
|
||||
duplicate: Dupliser
|
||||
left: Venstre
|
||||
makeExplorableDescription: Dersom du slår av denne vil kontoen din ikke dukke opp
|
||||
under "Utforsk".
|
||||
apply: Bruk
|
||||
emailNotification: Epostvarsler
|
||||
useReactionPickerForContextMenu: Åpne reaksjonsvelger med høyreklikk
|
||||
typingUsers: '{users} skriver'
|
||||
markAllAsRead: Marker alle som lest
|
||||
goBack: Tilbake
|
||||
info: Om
|
||||
userInfo: Brukerinformasjon
|
||||
hideOnlineStatusDescription: Å skjule hvorvidt du er pålogget vil redusere enkelheten
|
||||
av enkelte funksjoner slik som søk.
|
||||
privateModeInfo: Bare hvitelistede tjenere kan federere med din tjener om du slår
|
||||
på denne. Alle poster vil bli skjult for andre.
|
||||
received: Mottatt
|
||||
searchResult: Søkeresultater
|
||||
hashtags: Emneknagger
|
||||
keepCw: Behold innholdsadvarsler
|
||||
misskeyUpdated: Firefish er oppdatert!
|
||||
whatIsNew: Vis endringer
|
||||
translate: Oversett
|
||||
translatedFrom: Oversatt fra {x}
|
||||
itsOn: Påslått
|
||||
itsOff: Avslått
|
||||
emailRequiredForSignup: Krev e-post-adresse for registrering
|
||||
unread: Ulest
|
||||
controlPanel: Kontrollpanel
|
||||
manageAccounts: Styr kontoer
|
||||
mutePeriod: Periode for stumming
|
||||
instanceDefaultLightTheme: Standard lyst tema på tjeneren
|
||||
reflectMayTakeTime: Det kan ta litt tid før endringen inntrer.
|
||||
failedToFetchAccountInformation: Klarte ikke å hente kontoinformasjon
|
||||
cropImageAsk: Ønsker du å beskjære dette bildet?
|
||||
recommendedInstances: Anbefalte tjenere
|
||||
moveAccountDescription: Denne prosessen er irreversibel! Vær sikker på at du har satt
|
||||
opp et alias for denne kontoen på den nye kontoen før du fortsetter. Skriv inn navnet
|
||||
på kontoen på formen @person@server.com
|
||||
moveFromDescription: Dette vil sette opp et alias for din gamle kontoen slik at du
|
||||
kan flytte fra den gamle kontoen til denne. Gjør dette FØR du flytter fra den gamle
|
||||
kontoen. Skriv inn den gamle kontoen på formen @person@server.com
|
||||
defaultReaction: Standard emoji-reaksjon for utgående og innkommende poster
|
||||
indexFrom: Indekser poster fra post-id og fremover
|
||||
indexNotice: Indekserer. Dette vil sannsynligvis ta litt tid, ikke restart tjeneren
|
||||
før det har gått minst en time.
|
||||
indexFromDescription: La stå tom for å indeksere alle poster
|
||||
customKaTeXMacroDescription: 'Sett opp makroer for å skrive matematiske uttrykk enkelt.
|
||||
Notasjonen følger LaTeX-kommandoer og er skrevet som \newcommand{\ navn}{uttrykk}
|
||||
eller \newcommand{\navn}{antall argumenter}{uttrykk}. For eksempel vil \newcommand{\add}{2}{#1
|
||||
+ #2} vil ekspandere \add{3}{foo} til 3 + foo. Klammeparentesene rundt makroen kan
|
||||
også endres til parenteser eller hakeparenteser. Dette påvirker hvilken parentestype
|
||||
du bruker for argumenter. En og bare en makro kan defineres pr linje, og du kan
|
||||
ikke ha linjeskift inni definisjonen. Linjer som ikke inneholder gyldige makroer
|
||||
vil bli ignorert. Bare enkle streng-erstatnings-makroer er støttet; avansert syntaks
|
||||
f.eks. med flykontroll er ikke tillatt.'
|
||||
signupsDisabled: Registreringer av nye konti på denne tjeneren er slått av, men du
|
||||
kan alltids registrere deg på en annen tjener! Hvis du har en invitasjonskode for
|
||||
denne tjeneren, skriv den inn under.
|
||||
findOtherInstance: Finn en annen tjener
|
||||
preventAiLearningDescription: Ber tredjeparts AI-språkmodeller om å ikke bruke innhold
|
||||
du laster opp, sliks om poster og bilder.
|
||||
enableCustomKaTeXMacro: Slå på egne KaTeX-makroer
|
||||
showPopup: Varsle brukere med oppsprettsvindu
|
||||
|
|
|
@ -304,7 +304,7 @@ emptyDrive: "Диск пуст"
|
|||
emptyFolder: "Папка пуста"
|
||||
unableToDelete: "Удаление невозможно"
|
||||
inputNewFileName: "Введите имя нового файла"
|
||||
inputNewDescription: "Введите новую подпись"
|
||||
inputNewDescription: "Введите новое описание"
|
||||
inputNewFolderName: "Пожалуйста, введите новое имя папки"
|
||||
circularReferenceFolder: "Вы пытаетесь переместить папку внутрь себя."
|
||||
hasChildFilesOrFolders: "Эта папка не пуста и не может быть удалена."
|
||||
|
@ -513,7 +513,7 @@ objectStorageBaseUrlDesc: "URL используемый для примера.
|
|||
CDN или прокси, если вы используете любой из них.\nДля S3 используйте 'https://<bucket>.s3.amazonaws.com',
|
||||
а для GCS и подобных сервисов используйте 'https://storage.googleapis.com/<bucket>',
|
||||
и т.п."
|
||||
objectStorageBucket: "Bucket"
|
||||
objectStorageBucket: "Хранилище (Bucket)"
|
||||
objectStorageBucketDesc: "Укажите название контейнера (Bucket) который используется
|
||||
на выбранном сервисе."
|
||||
objectStoragePrefix: "Префикс"
|
||||
|
@ -596,8 +596,8 @@ disablePlayer: "Выключить проигрыватель"
|
|||
expandTweet: "Развернуть твит"
|
||||
themeEditor: "Редактор темы оформления"
|
||||
description: "Описание"
|
||||
describeFile: "Добавить подпись"
|
||||
enterFileDescription: "Введите подпись"
|
||||
describeFile: "Добавить описание"
|
||||
enterFileDescription: "Введите описание"
|
||||
author: "Автор"
|
||||
leaveConfirm: "Вы не сохранили изменения. Хотите выйти и потерять их?"
|
||||
manage: "Управление"
|
||||
|
@ -826,7 +826,7 @@ gallery: "Галерея"
|
|||
recentPosts: "Недавние публикации"
|
||||
popularPosts: "Популярные публикации"
|
||||
shareWithNote: "Поделиться постом"
|
||||
ads: "Реклама"
|
||||
ads: "Баннеры сообщества"
|
||||
expiration: "Опрос длится"
|
||||
memo: "Памятка"
|
||||
priority: "Приоритет"
|
||||
|
@ -1001,6 +1001,7 @@ _aboutFirefish:
|
|||
pleaseDonateToHost: Также не забудьте поддержать ваш домашний сервер {host}, чтобы
|
||||
помочь с его операционными расходами.
|
||||
donateHost: Пожертвовать на {host}
|
||||
misskeyContributors: Контрибьюторы Misskey
|
||||
_nsfw:
|
||||
respect: "Скрывать содержимое не для всех"
|
||||
ignore: "Показывать содержимое не для всех"
|
||||
|
@ -1024,7 +1025,7 @@ _mfm:
|
|||
boldDescription: "Выделяет текст, делая буквы жирнее."
|
||||
small: "Мелкий шрифт"
|
||||
smallDescription: "Делает текст маленьким и незаметным."
|
||||
center: "Выровнять элементы по центру"
|
||||
center: "По центру"
|
||||
centerDescription: "Так можно выровнять что-то по центру."
|
||||
inlineCode: "Программа (в тексте)"
|
||||
inlineCodeDescription: "Подсвечивает фрагмент программы внутри сплошного текста."
|
||||
|
@ -1267,8 +1268,8 @@ _tutorial:
|
|||
step6_1: "Итак, что это за место?"
|
||||
step6_2: "Ну, вы не просто присоединились к Firefish. Вы вошли в Fediverse, взаимосвязанную
|
||||
сеть из тысяч серверов."
|
||||
step6_3: "Каждый сервер работает по-своему, и не на всех серверах работает Firefish.
|
||||
Но этот работает! Это немного сложно, но вы быстро разберетесь."
|
||||
step6_3: "Каждый сервер работает по-своему, и не все сервера работают на базе Firefish.
|
||||
Но этот работает! Это сложновато, но вы быстро разберетесь."
|
||||
step6_4: "Теперь идите, изучайте и развлекайтесь!"
|
||||
_2fa:
|
||||
alreadyRegistered: "Двухфакторная аутентификация уже настроена."
|
||||
|
@ -1475,7 +1476,7 @@ _charts:
|
|||
remoteNotesIncDec: "Изменения числа постов с других сайтов"
|
||||
notesTotal: "Общее количество постов"
|
||||
filesIncDec: "Изменения числа файлов"
|
||||
filesTotal: "Суммарное количество файлов"
|
||||
filesTotal: "Общее количество файлов"
|
||||
storageUsageIncDec: "Изменения заполнения хранилища"
|
||||
storageUsageTotal: "Суммарное заполнение хранилища"
|
||||
_instanceCharts:
|
||||
|
@ -1903,12 +1904,12 @@ customMOTDDescription: Пользовательские сообщения дл
|
|||
загружает / перезагружает страницу.
|
||||
recommendedInstancesDescription: Рекомендуемые инстансы, разделенные разрывами строк,
|
||||
должны отображаться на рекомендуемой ленте.
|
||||
caption: Автоматическая подпись
|
||||
caption: Автоматическое описание
|
||||
splash: Заставка
|
||||
updateAvailable: Возможно, доступно обновление!
|
||||
move: Переместить
|
||||
swipeOnDesktop: Разрешить свайпы в мобильном стиле на десктопе
|
||||
showAds: Показывать рекламу
|
||||
showAds: Показывать баннеры сообщества
|
||||
noEmailServerWarning: Почтовый сервер не настроен.
|
||||
type: Тип
|
||||
numberOfPageCacheDescription: Увеличение этого числа повысит удобство для пользователей,
|
||||
|
@ -2137,3 +2138,19 @@ donationLink: Ссылка на страницу для взносов
|
|||
isLocked: Этот аккаунт имеет одобрение запросов на подписку
|
||||
removeRecipient: Удалить получателя
|
||||
removeMember: Удалить участника
|
||||
confirm: Подтвердить
|
||||
importZip: Импортировать ZIP
|
||||
exportZip: Экспортировать ZIP
|
||||
emojiPackCreator: Генератор паков эмодзи
|
||||
objectStorageS3ForcePathStyle: Использовать путь вместо домена в URL
|
||||
objectStorageS3ForcePathStyleDesc: Включите, если хотите, чтобы URL был в формате
|
||||
's3.amazonaws.com/<bucket>/' вместо '<bucket>.s3.amazonaws.com'.
|
||||
origin: Источник
|
||||
deletePasskeys: Удалить passkey
|
||||
delete2faConfirm: Двухфакторная аутентификация на этом аккаунте будет безвозвратно
|
||||
удалена. Продолжить?
|
||||
delete2fa: Отключить двухфакторную аутентификацию
|
||||
deletePasskeysConfirm: Это действие безвозвратно удалит все passkey и ключи безопасности
|
||||
на этом аккаунте. Продолжить?
|
||||
inputNotMatch: Введённые данные не совпадают
|
||||
addRe: Добавить "re:" в начале комментария в ответ на запись с предупреждением о содержимом
|
||||
|
|
|
@ -305,7 +305,7 @@ emptyDrive: "Диск порожній"
|
|||
emptyFolder: "Тека порожня"
|
||||
unableToDelete: "Видалення неможливе"
|
||||
inputNewFileName: "Введіть ім'я нового файлу"
|
||||
inputNewDescription: "Введіть новий заголовок"
|
||||
inputNewDescription: "Введіть новий опис"
|
||||
inputNewFolderName: "Введіть ім'я нової теки"
|
||||
circularReferenceFolder: "Ви намагаєтесь перемістити папку в її підпапку."
|
||||
hasChildFilesOrFolders: "Ця тека не порожня і не може бути видалена."
|
||||
|
@ -591,8 +591,8 @@ disablePlayer: "Закрити відеоплеєр"
|
|||
expandTweet: "Розгорнути твіт"
|
||||
themeEditor: "Редактор тем"
|
||||
description: "Опис"
|
||||
describeFile: "Додати підпис"
|
||||
enterFileDescription: "Введіть підпис"
|
||||
describeFile: "Додати опис"
|
||||
enterFileDescription: "Введіть опис"
|
||||
author: "Автор"
|
||||
leaveConfirm: "Зміни не збережені. Ви дійсно хочете скасувати зміни?"
|
||||
manage: "Управління"
|
||||
|
@ -1985,7 +1985,7 @@ showUpdates: Показувати спливаюче вікно при онов
|
|||
updateAvailable: Можливо, є доступне оновлення!
|
||||
recommendedInstancesDescription: Рекомендовані сервери відокремлюються переведенням
|
||||
рядка, щоб з'явитися на стрічці рекомендацій.
|
||||
caption: Автоматичний підпис
|
||||
caption: Автоматичний опис
|
||||
showAdminUpdates: Вказати, що доступна нова версія Firefish (тільки для адміністратора)
|
||||
defaultReaction: Емодзі реакція за замовчуванням для вихідних і вхідних записів
|
||||
license: Ліцензія
|
||||
|
@ -2151,3 +2151,7 @@ deletePasskeysConfirm: Це видалить усі ключ-паролі і к
|
|||
записі без можливости відмінити цю дію. Продовжити?
|
||||
addRe: Додати "re:" на початку коментаря у відповідь на запис із попередженням про
|
||||
вміст
|
||||
confirm: Підтвердити
|
||||
importZip: Імпортувати ZIP
|
||||
exportZip: Експортувати ZIP
|
||||
emojiPackCreator: Генератор паків емодзі
|
||||
|
|
|
@ -290,7 +290,7 @@ emptyDrive: "网盘中无文件"
|
|||
emptyFolder: "此文件夹中无文件"
|
||||
unableToDelete: "无法删除"
|
||||
inputNewFileName: "请输入新文件名"
|
||||
inputNewDescription: "请输入新标题"
|
||||
inputNewDescription: "请输入新描述"
|
||||
inputNewFolderName: "请输入新文件夹名"
|
||||
circularReferenceFolder: "目标文件夹是您要移动的文件夹的子文件夹。"
|
||||
hasChildFilesOrFolders: "此文件夹中有文件,无法删除。"
|
||||
|
@ -561,8 +561,8 @@ disablePlayer: "关闭播放器"
|
|||
expandTweet: "展开帖子"
|
||||
themeEditor: "主题编辑器"
|
||||
description: "描述"
|
||||
describeFile: "添加标题"
|
||||
enterFileDescription: "输入标题"
|
||||
describeFile: "添加描述"
|
||||
enterFileDescription: "输入描述"
|
||||
author: "作者"
|
||||
leaveConfirm: "存在未保存的更改。要放弃更改吗?"
|
||||
manage: "管理"
|
||||
|
@ -776,7 +776,7 @@ gallery: "图库"
|
|||
recentPosts: "最新发布"
|
||||
popularPosts: "热门投稿"
|
||||
shareWithNote: "在帖子中分享"
|
||||
ads: "广告"
|
||||
ads: "社区横幅"
|
||||
expiration: "截止时间"
|
||||
memo: "便笺"
|
||||
priority: "优先级"
|
||||
|
@ -825,7 +825,7 @@ unmuteThread: "取消静音帖子串"
|
|||
ffVisibility: "关注/关注者 可见性"
|
||||
ffVisibilityDescription: "您可以设置谁可以看到您的关注/关注者信息。"
|
||||
continueThread: "查看更多帖子"
|
||||
deleteAccountConfirm: "将不可逆的删除账号,是否继续?"
|
||||
deleteAccountConfirm: "这将不可逆转地删除账号,是否继续?"
|
||||
incorrectPassword: "密码错误。"
|
||||
voteConfirm: "确定投给 “{choice}” ?"
|
||||
hide: "隐藏"
|
||||
|
@ -994,6 +994,7 @@ _aboutFirefish:
|
|||
pleaseDonateToFirefish: 请考虑赞助 Firefish 以支持其开发。
|
||||
pleaseDonateToHost: 也请考虑赞助您的主服务器 {host},以帮助支持其运营成本。
|
||||
donateHost: 赞助 {host}
|
||||
misskeyContributors: Misskey 贡献者
|
||||
_nsfw:
|
||||
respect: "隐藏敏感内容"
|
||||
ignore: "不隐藏敏感内容"
|
||||
|
@ -1857,7 +1858,7 @@ seperateRenoteQuote: 单独的转发和引用按钮
|
|||
customSplashIcons: 自定义启动屏幕图标(urls)
|
||||
alt: 替代文字
|
||||
pushNotificationNotSupported: 您的浏览器或者服务器不支持推送通知
|
||||
showAds: 显示广告
|
||||
showAds: 显示社区横幅
|
||||
enterSendsMessage: 按回车键发送信息(关闭则是 Ctrl + Retun 发送)
|
||||
recommendedInstances: 推荐服务器
|
||||
updateAvailable: 可能有可用更新!
|
||||
|
@ -1876,7 +1877,7 @@ clipsDesc: 便签就像可共享的分类书签。您可以从各个帖子的菜
|
|||
privateModeInfo: 当启用时,只有白名单上的服务器可以与您的服务器联合,所有的帖子都会对公共时间线隐藏。
|
||||
allowedInstancesDescription: 要列入联合白名单的服务器的主机名,一行一个(仅适用于私密模式)。
|
||||
breakFollowConfirm: 确定要移除关注者吗?
|
||||
caption: 自动显示说明文字
|
||||
caption: 自动显示描述文字
|
||||
newer: 更新的
|
||||
older: 更旧的
|
||||
noInstances: 没有服务器
|
||||
|
@ -1967,3 +1968,16 @@ removeQuote: 移除引用
|
|||
removeRecipient: 移除接收者
|
||||
removeMember: 移除成员
|
||||
origin: 起源
|
||||
confirm: 确认
|
||||
importZip: 导入 ZIP
|
||||
exportZip: 导出 ZIP
|
||||
emojiPackCreator: 表情包创建工具
|
||||
objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 's3.amazonaws.com/<bucket>/' 而非 '<bucket>.s3.amazonaws.com'
|
||||
的端点 URL。
|
||||
objectStorageS3ForcePathStyle: 使用基于路径的端点 URL
|
||||
delete2fa: 禁用 2FA
|
||||
deletePasskeysConfirm: 这将不可逆转地删除此账号上的所有通行密钥和安全密钥。是否继续?
|
||||
inputNotMatch: 输入不匹配
|
||||
deletePasskeys: 删除通行密钥
|
||||
delete2faConfirm: 这将不可逆转地删除此账户上的 2FA。是否继续?
|
||||
addRe: 在回复有内容警告的帖子时,在评论开头添加 "re:"
|
||||
|
|
1
neko/UPSTREAM_COMMIT_ID
Normal file
1
neko/UPSTREAM_COMMIT_ID
Normal file
|
@ -0,0 +1 @@
|
|||
f309d17667d827f44e9d914f43ddc7af2aef8268
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "firefish",
|
||||
"version": "1.0.5-dev5",
|
||||
"version": "1.0.5-dev6",
|
||||
"codename": "aqua",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
export class IncreaseHostCharLimit1692374635734 {
|
||||
name = "IncreaseHostCharLimit1692374635734";
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_file" ALTER COLUMN "userHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER COLUMN "host" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ALTER COLUMN "userHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_publickey" ALTER COLUMN "keyId" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "emoji" ALTER COLUMN "host" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "userHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "replyUserHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "renoteUserHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER COLUMN "host" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER COLUMN "iconUrl" TYPE character varying(4096)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER COLUMN "faviconUrl" TYPE character varying(4096)`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "poll" ALTER COLUMN "userHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "abuse_user_report" ALTER COLUMN "targetUserHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "abuse_user_report" ALTER COLUMN "reporterHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "following" ALTER COLUMN "followeeHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "following" ALTER COLUMN "followerHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "follow_request" ALTER COLUMN "followeeHost" TYPE character varying(512)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "follow_request" ALTER COLUMN "followerHost" TYPE character varying(512)`,
|
||||
);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "drive_file" ALTER COLUMN "userHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user" ALTER COLUMN "host" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ALTER COLUMN "userHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_publickey" ALTER COLUMN "keyId" TYPE character varying(256)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "emoji" ALTER COLUMN "host" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "userHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "replyUserHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "note" ALTER COLUMN "renoteUserHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER COLUMN "host" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER COLUMN "iconUrl" TYPE character varying(256)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "instance" ALTER COLUMN "faviconUrl" TYPE character varying(256)`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "poll" ALTER COLUMN "userHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "abuse_user_report" ALTER COLUMN "targetUserHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "abuse_user_report" ALTER COLUMN "reporterHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "following" ALTER COLUMN "followeeHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "following" ALTER COLUMN "followerHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "follow_request" ALTER COLUMN "followeeHost" TYPE character varying(128)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "follow_request" ALTER COLUMN "followerHost" TYPE character varying(128)`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -78,6 +78,7 @@ export type Source = {
|
|||
fingerprint?: string;
|
||||
};
|
||||
|
||||
outgoingAddress?: string;
|
||||
outgoingAddressFamily?: "ipv4" | "ipv6" | "dual";
|
||||
|
||||
deliverJobConcurrency?: number;
|
||||
|
|
|
@ -92,7 +92,7 @@ export async function downloadUrl(url: string, path: string): Promise<void> {
|
|||
logger.succ(`Download finished: ${chalk.cyan(url)}`);
|
||||
}
|
||||
|
||||
function isPrivateIp(ip: string): boolean {
|
||||
export function isPrivateIp(ip: string): boolean {
|
||||
for (const net of config.allowedPrivateNetworks || []) {
|
||||
const cidr = new IPCIDR(net);
|
||||
if (cidr.contains(ip)) {
|
||||
|
|
|
@ -99,6 +99,7 @@ const _http = new http.Agent({
|
|||
keepAlive: true,
|
||||
keepAliveMsecs: 30 * 1000,
|
||||
lookup: cache.lookup,
|
||||
localAddress: config.outgoingAddress,
|
||||
} as http.AgentOptions);
|
||||
|
||||
/**
|
||||
|
@ -108,6 +109,7 @@ const _https = new https.Agent({
|
|||
keepAlive: true,
|
||||
keepAliveMsecs: 30 * 1000,
|
||||
lookup: cache.lookup,
|
||||
localAddress: config.outgoingAddress,
|
||||
} as https.AgentOptions);
|
||||
|
||||
const maxSockets = Math.max(256, config.deliverJobConcurrency || 128);
|
||||
|
@ -123,6 +125,7 @@ export const httpAgent = config.proxy
|
|||
maxFreeSockets: 256,
|
||||
scheduling: "lifo",
|
||||
proxy: config.proxy,
|
||||
localAddress: config.outgoingAddress,
|
||||
})
|
||||
: _http;
|
||||
|
||||
|
@ -137,6 +140,7 @@ export const httpsAgent = config.proxy
|
|||
maxFreeSockets: 256,
|
||||
scheduling: "lifo",
|
||||
proxy: config.proxy,
|
||||
localAddress: config.outgoingAddress,
|
||||
})
|
||||
: _https;
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ export class AbuseUserReport {
|
|||
//#region Denormalized fields
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
@ -79,7 +79,7 @@ export class AbuseUserReport {
|
|||
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
|
|
@ -39,7 +39,7 @@ export class DriveFile {
|
|||
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "The host of owner. It will be null if the user in local.",
|
||||
})
|
||||
|
|
|
@ -20,7 +20,7 @@ export class Emoji {
|
|||
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
})
|
||||
public host: string | null;
|
||||
|
|
|
@ -50,7 +50,7 @@ export class Following {
|
|||
//#region Denormalized fields
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
@ -72,7 +72,7 @@ export class Following {
|
|||
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
|
|
@ -20,7 +20,7 @@ export class Instance {
|
|||
*/
|
||||
@Index({ unique: true })
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
comment: "The host of the Instance.",
|
||||
})
|
||||
public host: string;
|
||||
|
@ -149,13 +149,13 @@ export class Instance {
|
|||
public maintainerEmail: string | null;
|
||||
|
||||
@Column("varchar", {
|
||||
length: 256,
|
||||
length: 4096,
|
||||
nullable: true,
|
||||
})
|
||||
public iconUrl: string | null;
|
||||
|
||||
@Column("varchar", {
|
||||
length: 256,
|
||||
length: 4096,
|
||||
nullable: true,
|
||||
})
|
||||
public faviconUrl: string | null;
|
||||
|
|
|
@ -217,7 +217,7 @@ export class Note {
|
|||
//#region Denormalized fields
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
@ -231,7 +231,7 @@ export class Note {
|
|||
public replyUserId: User["id"] | null;
|
||||
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
@ -245,7 +245,7 @@ export class Note {
|
|||
public renoteUserId: User["id"] | null;
|
||||
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
|
|
@ -58,7 +58,7 @@ export class Poll {
|
|||
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
|
|
@ -242,7 +242,7 @@ export class UserProfile {
|
|||
//#region Denormalized fields
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment: "[Denormalized]",
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ export class UserPublickey {
|
|||
|
||||
@Index({ unique: true })
|
||||
@Column("varchar", {
|
||||
length: 256,
|
||||
length: 512,
|
||||
})
|
||||
public keyId: string;
|
||||
|
||||
|
|
|
@ -214,7 +214,7 @@ export class User {
|
|||
|
||||
@Index()
|
||||
@Column("varchar", {
|
||||
length: 128,
|
||||
length: 512,
|
||||
nullable: true,
|
||||
comment:
|
||||
"The host of the User. It will be null if the origin of the user is local.",
|
||||
|
|
|
@ -122,19 +122,31 @@ export default class DeliverManager {
|
|||
)
|
||||
.forEach((recipe) => inboxes.add(recipe.to.inbox!));
|
||||
|
||||
// Validate Inboxes first
|
||||
const validInboxes = [];
|
||||
for (const inbox of inboxes) {
|
||||
try {
|
||||
validInboxes.push({
|
||||
inbox,
|
||||
host: new URL(inbox).host,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
console.error(`Invalid Inbox ${inbox}`);
|
||||
}
|
||||
}
|
||||
|
||||
const instancesToSkip = await skippedInstances(
|
||||
// get (unique) list of hosts
|
||||
Array.from(
|
||||
new Set(Array.from(inboxes).map((inbox) => new URL(inbox).host)),
|
||||
),
|
||||
Array.from(new Set(validInboxes.map((valid) => valid.host))),
|
||||
);
|
||||
|
||||
// deliver
|
||||
for (const inbox of inboxes) {
|
||||
for (const valid of validInboxes) {
|
||||
// skip instances as indicated
|
||||
if (instancesToSkip.includes(new URL(inbox).host)) continue;
|
||||
if (instancesToSkip.includes(valid.host)) continue;
|
||||
|
||||
deliver(this.actor, this.activity, inbox);
|
||||
deliver(this.actor, this.activity, valid.inbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import remove from "./remove/index.js";
|
|||
import block from "./block/index.js";
|
||||
import flag from "./flag/index.js";
|
||||
import move from "./move/index.js";
|
||||
import type { IObject } from "../type.js";
|
||||
import type { IObject, IActivity } from "../type.js";
|
||||
import { extractDbHost } from "@/misc/convert-host.js";
|
||||
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
||||
|
||||
|
@ -106,6 +106,8 @@ async function performOneActivity(
|
|||
} else if (isMove(activity)) {
|
||||
await move(actor, activity);
|
||||
} else {
|
||||
apLogger.warn(`unrecognized activity type: ${(activity as any).type}`);
|
||||
apLogger.warn(
|
||||
`Unrecognized activity type: ${(activity as IActivity).type}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,13 +68,13 @@ export class LdSignature {
|
|||
...options,
|
||||
"@context": "https://w3id.org/identity/v1",
|
||||
};
|
||||
delete transformedOptions["type"];
|
||||
delete transformedOptions["id"];
|
||||
delete transformedOptions["signatureValue"];
|
||||
transformedOptions.type = undefined;
|
||||
transformedOptions.id = undefined;
|
||||
transformedOptions.signatureValue = undefined;
|
||||
const canonizedOptions = await this.normalize(transformedOptions);
|
||||
const optionsHash = this.sha256(canonizedOptions);
|
||||
const transformedData = { ...data };
|
||||
delete transformedData["signature"];
|
||||
transformedData.signature = undefined;
|
||||
const cannonidedData = await this.normalize(transformedData);
|
||||
if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`);
|
||||
const documentHash = this.sha256(cannonidedData);
|
||||
|
|
|
@ -231,6 +231,21 @@ export async function createPerson(
|
|||
}
|
||||
}
|
||||
|
||||
let notesCount: number | undefined;
|
||||
|
||||
if (typeof person.outbox === "string") {
|
||||
try {
|
||||
let data = await fetch(person.outbox, {
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
let json_data = JSON.parse(await data.text());
|
||||
|
||||
notesCount = json_data.totalItems;
|
||||
} catch (e) {
|
||||
notesCount = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Create user
|
||||
let user: IRemoteUser;
|
||||
try {
|
||||
|
@ -274,6 +289,14 @@ export async function createPerson(
|
|||
isCollectionOrOrderedCollection(person.following)
|
||||
? person.following.totalItems
|
||||
: undefined,
|
||||
notesCount:
|
||||
notesCount !== undefined
|
||||
? notesCount
|
||||
: person.outbox &&
|
||||
typeof person.outbox !== "string" &&
|
||||
isCollectionOrOrderedCollection(person.outbox)
|
||||
? person.outbox.totalItems
|
||||
: undefined,
|
||||
featured: person.featured ? getApId(person.featured) : undefined,
|
||||
uri: person.id,
|
||||
tags,
|
||||
|
@ -472,6 +495,21 @@ export async function updatePerson(
|
|||
}
|
||||
}
|
||||
|
||||
let notesCount: number | undefined;
|
||||
|
||||
if (typeof person.outbox === "string") {
|
||||
try {
|
||||
let data = await fetch(person.outbox, {
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
let json_data = JSON.parse(await data.text());
|
||||
|
||||
notesCount = json_data.totalItems;
|
||||
} catch (e) {
|
||||
notesCount = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const updates = {
|
||||
lastFetchedAt: new Date(),
|
||||
inbox: person.inbox,
|
||||
|
@ -495,6 +533,14 @@ export async function updatePerson(
|
|||
isCollectionOrOrderedCollection(person.following)
|
||||
? person.following.totalItems
|
||||
: undefined,
|
||||
notesCount:
|
||||
notesCount !== undefined
|
||||
? notesCount
|
||||
: person.outbox &&
|
||||
typeof person.outbox !== "string" &&
|
||||
isCollectionOrOrderedCollection(person.outbox)
|
||||
? person.outbox.totalItems
|
||||
: undefined,
|
||||
featured: person.featured,
|
||||
emojis: emojiNames,
|
||||
name: truncate(person.name, nameLength),
|
||||
|
@ -554,7 +600,7 @@ export async function updatePerson(
|
|||
{
|
||||
followerSharedInbox:
|
||||
person.sharedInbox ||
|
||||
(person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||
(person.endpoints ? person.endpoints.sharedInbox : null),
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -663,7 +709,7 @@ export async function updateFeatured(userId: User["id"], resolver?: Resolver) {
|
|||
? collection.items
|
||||
: collection.orderedItems;
|
||||
const items = await Promise.all(
|
||||
toArray(unresolvedItems).map((x) => resolver.resolve(x)),
|
||||
toArray(unresolvedItems).map((x) => resolver?.resolve(x)),
|
||||
);
|
||||
|
||||
// Resolve and regist Notes
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import define from "../../../define.js";
|
||||
import { createImportCustomEmojisJob } from "@/queue/index.js";
|
||||
import { ApiError } from "../../../error.js";
|
||||
import ms from "ms";
|
||||
|
||||
export const meta = {
|
||||
tags: ["admin", "emoji"],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as sanitizeHtml from "sanitize-html";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import define from "../../define.js";
|
||||
import { Users, UserProfiles } from "@/models/index.js";
|
||||
import { ApiError } from "../../error.js";
|
||||
|
|
|
@ -24,7 +24,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
recursiveNesting: {
|
||||
message: "It can not be structured like nesting folders recursively.",
|
||||
message: "It cannot be structured like nesting folders recursively.",
|
||||
code: "NO_SUCH_PARENT_FOLDER",
|
||||
id: "ce104e3a-faaf-49d5-b459-10ff0cbbcaa1",
|
||||
},
|
||||
|
|
|
@ -18,7 +18,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
pinLimitExceeded: {
|
||||
message: "You can not pin notes any more.",
|
||||
message: "You cannot pin notes any more.",
|
||||
code: "PIN_LIMIT_EXCEEDED",
|
||||
id: "72dab508-c64d-498f-8740-a8eec1ba385a",
|
||||
},
|
||||
|
|
|
@ -15,9 +15,7 @@ export const paramDef = {
|
|||
|
||||
export default define(meta, paramDef, async () => {
|
||||
let tag_name;
|
||||
await fetch(
|
||||
"https://codeberg.org/api/v1/repos/firefish/firefish/releases?draft=false&pre-release=false&page=1&limit=1",
|
||||
)
|
||||
await fetch("https://git.joinfirefish.org/api/v4/projects/7/releases")
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
tag_name = data[0].tag_name;
|
||||
|
|
|
@ -48,7 +48,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
groupAccessDenied: {
|
||||
message: "You can not read messages of groups that you have not joined.",
|
||||
message: "You cannot read messages of groups that you have not joined.",
|
||||
code: "GROUP_ACCESS_DENIED",
|
||||
id: "a053a8dd-a491-4718-8f87-50775aad9284",
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@ export const meta = {
|
|||
|
||||
errors: {
|
||||
recipientIsYourself: {
|
||||
message: "You can not send a message to yourself.",
|
||||
message: "You cannot send a message to yourself.",
|
||||
code: "RECIPIENT_IS_YOURSELF",
|
||||
id: "17e2ba79-e22a-4cbc-bf91-d327643f4a7e",
|
||||
},
|
||||
|
@ -46,7 +46,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
groupAccessDenied: {
|
||||
message: "You can not send messages to groups that you have not joined.",
|
||||
message: "You cannot send messages to groups that you have not joined.",
|
||||
code: "GROUP_ACCESS_DENIED",
|
||||
id: "d96b3cca-5ad1-438b-ad8b-02f931308fbd",
|
||||
},
|
||||
|
|
|
@ -52,7 +52,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
cannotReRenote: {
|
||||
message: "You can not Renote a pure Renote.",
|
||||
message: "You cannot Renote a pure Renote.",
|
||||
code: "CANNOT_RENOTE_TO_A_PURE_RENOTE",
|
||||
id: "fd4cc33e-2a37-48dd-99cc-9b806eb2031a",
|
||||
},
|
||||
|
@ -64,7 +64,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
cannotReplyToPureRenote: {
|
||||
message: "You can not reply to a pure Renote.",
|
||||
message: "You cannot reply to a pure Renote.",
|
||||
code: "CANNOT_REPLY_TO_A_PURE_RENOTE",
|
||||
id: "3ac74a84-8fd5-4bb0-870f-01804f82ce15",
|
||||
},
|
||||
|
|
|
@ -70,7 +70,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
cannotReRenote: {
|
||||
message: "You can not Renote a pure Renote.",
|
||||
message: "You cannot Renote a pure Renote.",
|
||||
code: "CANNOT_RENOTE_TO_A_PURE_RENOTE",
|
||||
id: "fd4cc33e-2a37-48dd-99cc-9b806eb2031a",
|
||||
},
|
||||
|
@ -82,7 +82,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
cannotReplyToPureRenote: {
|
||||
message: "You can not reply to a pure Renote.",
|
||||
message: "You cannot reply to a pure Renote.",
|
||||
code: "CANNOT_REPLY_TO_A_PURE_RENOTE",
|
||||
id: "3ac74a84-8fd5-4bb0-870f-01804f82ce15",
|
||||
},
|
||||
|
@ -130,7 +130,7 @@ export const meta = {
|
|||
},
|
||||
|
||||
cannotPrivateRenote: {
|
||||
message: "You can not perform a private renote.",
|
||||
message: "You cannot perform a private renote.",
|
||||
code: "CANNOT_PRIVATE_RENOTE",
|
||||
id: "19a50f1c-84fa-4e33-81d3-17834ccc0ad8",
|
||||
},
|
||||
|
@ -140,6 +140,18 @@ export const meta = {
|
|||
code: "NOT_LOCAL_USER",
|
||||
id: "b907f407-2aa0-4283-800b-a2c56290b822",
|
||||
},
|
||||
|
||||
cannotChangeVisibility: {
|
||||
message: "You cannot change the visibility of a note.",
|
||||
code: "CANNOT_CHANGE_VISIBILITY",
|
||||
id: "2917fd0b-da04-41de-949f-146835a006c6",
|
||||
},
|
||||
|
||||
cannotQuoteOwnNote: {
|
||||
message: "You cannot quote your own note.",
|
||||
code: "CANNOT_QUOTE_OWN_NOTE",
|
||||
id: "070eee98-5f8a-4eca-9dc0-830b4d4e52ac",
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
@ -268,6 +280,10 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
throw e;
|
||||
});
|
||||
|
||||
if (ps.renoteId === note.id) {
|
||||
throw new ApiError(meta.errors.cannotQuoteOwnNote);
|
||||
}
|
||||
|
||||
if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) {
|
||||
throw new ApiError(meta.errors.cannotReRenote);
|
||||
}
|
||||
|
@ -523,7 +539,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
update.cw = null;
|
||||
}
|
||||
if (ps.visibility !== note.visibility) {
|
||||
update.visibility = ps.visibility;
|
||||
// update.visibility = ps.visibility;
|
||||
throw new ApiError(meta.errors.cannotChangeVisibility);
|
||||
}
|
||||
if (ps.localOnly !== note.localOnly) {
|
||||
update.localOnly = ps.localOnly;
|
||||
|
|
|
@ -85,6 +85,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
|
||||
query
|
||||
.andWhere("note.text ILIKE :q", { q: `%${sqlLikeEscape(ps.query)}%` })
|
||||
.andWhere("note.visibility = 'public'")
|
||||
.innerJoinAndSelect("note.user", "user")
|
||||
.leftJoinAndSelect("user.avatar", "avatar")
|
||||
.leftJoinAndSelect("user.banner", "banner")
|
||||
|
|
|
@ -10,7 +10,7 @@ export const meta = {
|
|||
kind: "write:user-groups",
|
||||
|
||||
description:
|
||||
"Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.",
|
||||
"Leave a group. The owner of a group cannot leave. They must transfer ownership or delete the group instead.",
|
||||
|
||||
errors: {
|
||||
noSuchGroup: {
|
||||
|
|
|
@ -11,7 +11,7 @@ export const meta = {
|
|||
kind: "write:user-groups",
|
||||
|
||||
description:
|
||||
"Removes a specified user from a group. The owner can not be removed.",
|
||||
"Removes a specified user from a group. The owner cannot be removed.",
|
||||
|
||||
errors: {
|
||||
noSuchGroup: {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import * as sanitizeHtml from "sanitize-html";
|
||||
import * as mfm from "mfm-js";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import { publishAdminStream } from "@/services/stream.js";
|
||||
import { AbuseUserReports, Users } from "@/models/index.js";
|
||||
import { AbuseUserReports, UserProfiles, Users } from "@/models/index.js";
|
||||
import { genId } from "@/misc/gen-id.js";
|
||||
import { sendEmail } from "@/services/send-email.js";
|
||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
||||
import { getUser } from "../../common/getters.js";
|
||||
import { ApiError } from "../../error.js";
|
||||
import define from "../../define.js";
|
||||
import { toHtml } from "@/mfm/to-html.js";
|
||||
|
||||
export const meta = {
|
||||
tags: ["users"],
|
||||
|
@ -84,6 +86,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
],
|
||||
});
|
||||
|
||||
const meta = await fetchMeta();
|
||||
for (const moderator of moderators) {
|
||||
publishAdminStream(moderator.id, "newAbuseUserReport", {
|
||||
id: report.id,
|
||||
|
@ -91,16 +94,16 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
reporterId: report.reporterId,
|
||||
comment: report.comment,
|
||||
});
|
||||
}
|
||||
|
||||
const meta = await fetchMeta();
|
||||
if (meta.email) {
|
||||
sendEmail(
|
||||
meta.email,
|
||||
"New abuse report",
|
||||
sanitizeHtml(ps.comment),
|
||||
sanitizeHtml(ps.comment),
|
||||
);
|
||||
const profile = await UserProfiles.findOneBy({ userId: moderator.id });
|
||||
if (profile?.email) {
|
||||
sendEmail(
|
||||
profile.email,
|
||||
"New abuse report",
|
||||
sanitizeHtml(toHtml(mfm.parse(ps.comment))!),
|
||||
sanitizeHtml(toHtml(mfm.parse(ps.comment))!),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -74,3 +74,13 @@ export function convertStatus(status: Entity.Status) {
|
|||
|
||||
return status;
|
||||
}
|
||||
|
||||
export function convertConversation(conversation: Entity.Conversation) {
|
||||
conversation.id = convertId(conversation.id, IdType.MastodonId);
|
||||
conversation.accounts = conversation.accounts.map(convertAccount);
|
||||
if (conversation.last_status) {
|
||||
conversation.last_status = convertStatus(conversation.last_status);
|
||||
}
|
||||
|
||||
return conversation;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import Router from "@koa/router";
|
||||
import { getClient } from "../ApiMastodonCompatibleService.js";
|
||||
import { ParsedUrlQuery } from "querystring";
|
||||
import { convertAccount, convertList, convertStatus } from "../converters.js";
|
||||
import {
|
||||
convertAccount,
|
||||
convertConversation,
|
||||
convertList,
|
||||
convertStatus,
|
||||
} from "../converters.js";
|
||||
import { convertId, IdType } from "../../index.js";
|
||||
|
||||
export function limitToInt(q: ParsedUrlQuery) {
|
||||
|
@ -136,7 +141,9 @@ export function apiTimelineMastodon(router: Router): void {
|
|||
const data = await client.getConversationTimeline(
|
||||
convertTimelinesArgsId(limitToInt(ctx.query)),
|
||||
);
|
||||
ctx.body = data.data;
|
||||
ctx.body = data.data.map((conversation) =>
|
||||
convertConversation(conversation),
|
||||
);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
|
|
@ -167,7 +167,6 @@ export default async function (ctx: Koa.Context) {
|
|||
return;
|
||||
}
|
||||
|
||||
ctx.length = file.size;
|
||||
ctx.set("Content-Disposition", contentDisposition("inline", filename));
|
||||
ctx.set("Content-Type", contentType);
|
||||
|
||||
|
@ -192,7 +191,6 @@ export default async function (ctx: Koa.Context) {
|
|||
ctx.set("Accept-Ranges", "bytes");
|
||||
} else {
|
||||
ctx.status = 206;
|
||||
ctx.length = readable.size;
|
||||
readable.on("close", async () => {
|
||||
await fileHandle.close();
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { URL } from "node:url";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
import { createTemp } from "@/misc/create-temp.js";
|
||||
import { downloadUrl } from "@/misc/download-url.js";
|
||||
import { downloadUrl, isPrivateIp } from "@/misc/download-url.js";
|
||||
import type { DriveFolder } from "@/models/entities/drive-folder.js";
|
||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||
import { DriveFiles } from "@/models/index.js";
|
||||
|
@ -35,7 +35,15 @@ export async function uploadFromUrl({
|
|||
requestIp = null,
|
||||
requestHeaders = null,
|
||||
}: Args): Promise<DriveFile> {
|
||||
let name = new URL(url).pathname.split("/").pop() || null;
|
||||
const parsedUrl = new URL(url);
|
||||
if (
|
||||
process.env.NODE_ENV === "production" &&
|
||||
isPrivateIp(parsedUrl.hostname.replaceAll(/(\[)|(\])/g, ""))
|
||||
) {
|
||||
throw new Error("Private IP is not allowed");
|
||||
}
|
||||
|
||||
let name = parsedUrl.pathname.split("/").pop() || null;
|
||||
if (name == null || !DriveFiles.validateFileName(name)) {
|
||||
name = null;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export async function addPinned(
|
|||
if (pinings.length >= 15) {
|
||||
throw new IdentifiableError(
|
||||
"15a018eb-58e5-4da1-93be-330fcc5e4e1a",
|
||||
"You can not pin notes any more.",
|
||||
"You cannot pin notes any more.",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -171,8 +171,7 @@ export default async (
|
|||
) =>
|
||||
// rome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME
|
||||
new Promise<Note>(async (res, rej) => {
|
||||
const dontFederateInitially =
|
||||
data.localOnly || data.visibility?.startsWith("hidden");
|
||||
const dontFederateInitially = data.visibility === "hidden";
|
||||
|
||||
// If you reply outside the channel, match the scope of the target.
|
||||
// TODO (I think it's a process that could be done on the client side, but it's server side for now.)
|
||||
|
@ -806,7 +805,7 @@ async function insertNote(
|
|||
}
|
||||
|
||||
export async function index(note: Note, reindexing: boolean): Promise<void> {
|
||||
if (!note.text) return;
|
||||
if (!note.text || note.visibility !== "public") return;
|
||||
|
||||
if (config.elasticsearch && es) {
|
||||
es.index({
|
||||
|
|
|
@ -11,30 +11,30 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@discordapp/twemoji": "14.1.2",
|
||||
"@eslint-sets/eslint-config-vue3": "^5.7.0",
|
||||
"@eslint-sets/eslint-config-vue3": "^5.8.0",
|
||||
"@eslint-sets/eslint-config-vue3-ts": "^3.3.0",
|
||||
"@phosphor-icons/web": "^2.0.3",
|
||||
"@rollup/plugin-alias": "3.1.9",
|
||||
"@rollup/plugin-json": "4.1.0",
|
||||
"@rollup/pluginutils": "^4.2.1",
|
||||
"@rollup/plugin-alias": "5.0.0",
|
||||
"@rollup/plugin-json": "6.0.0",
|
||||
"@rollup/pluginutils": "^5.0.3",
|
||||
"@syuilo/aiscript": "0.11.1",
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
"@types/glob": "8.1.0",
|
||||
"@types/gulp": "4.0.13",
|
||||
"@types/gulp-rename": "2.0.2",
|
||||
"@types/katex": "0.16.0",
|
||||
"@types/matter-js": "0.18.2",
|
||||
"@types/katex": "0.16.2",
|
||||
"@types/matter-js": "0.19.0",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/seedrandom": "3.0.5",
|
||||
"@types/throttle-debounce": "5.0.0",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@types/uuid": "9.0.2",
|
||||
"@vitejs/plugin-vue": "4.3.1",
|
||||
"@vue/compiler-sfc": "3.3.4",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "6.0.1",
|
||||
"blurhash": "2.0.5",
|
||||
"broadcast-channel": "5.1.0",
|
||||
"broadcast-channel": "5.2.0",
|
||||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer",
|
||||
"chart.js": "4.3.3",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"date-fns": "2.30.0",
|
||||
"emojilib": "github:thatonecalculator/emojilib",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint-config-prettier": "^8.9.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-file-progress": "^1.3.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"fast-blurhash": "^1.1.2",
|
||||
|
@ -61,27 +61,25 @@
|
|||
"insert-text-at-cursor": "0.3.0",
|
||||
"json5": "2.2.3",
|
||||
"katex": "0.16.8",
|
||||
"matter-js": "0.18.0",
|
||||
"matter-js": "0.19.0",
|
||||
"mfm-js": "0.23.3",
|
||||
"paralint": "^1.2.1",
|
||||
"photoswipe": "5.3.8",
|
||||
"prettier": "3.0.1",
|
||||
"prettier": "3.0.2",
|
||||
"prettier-plugin-vue": "1.1.6",
|
||||
"prismjs": "1.29.0",
|
||||
"punycode": "2.3.0",
|
||||
"querystring": "0.2.1",
|
||||
"rndstr": "1.0.0",
|
||||
"rollup": "3.27.2",
|
||||
"rollup": "3.28.0",
|
||||
"s-age": "1.1.2",
|
||||
"sass": "1.64.2",
|
||||
"sass": "1.66.0",
|
||||
"seedrandom": "3.0.5",
|
||||
"start-server-and-test": "1.15.2",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"swiper": "10.1.0",
|
||||
"swiper": "10.2.0",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.146.0",
|
||||
"three": "0.155.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tinyld": "1.3.4",
|
||||
|
@ -91,11 +89,11 @@
|
|||
"typescript": "5.1.6",
|
||||
"unicode-emoji-json": "^0.4.0",
|
||||
"uuid": "9.0.0",
|
||||
"vanilla-tilt": "1.8.0",
|
||||
"vanilla-tilt": "1.8.1",
|
||||
"vite": "4.4.9",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vue": "3.3.4",
|
||||
"vue-draggable-plus": "^0.2.4",
|
||||
"vue-draggable-plus": "^0.2.5",
|
||||
"vue-isyourpasswordsafe": "^2.0.0",
|
||||
"vue-plyr": "^7.0.0",
|
||||
"vue-prism-editor": "2.0.0-alpha.2"
|
||||
|
|
|
@ -65,6 +65,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import MkSwitch from "@/components/form/switch.vue";
|
||||
import MkKeyValue from "@/components/MkKeyValue.vue";
|
||||
|
@ -79,11 +81,11 @@ const emit = defineEmits<{
|
|||
(ev: "resolved", reportId: string): void;
|
||||
}>();
|
||||
|
||||
const forward = $ref(props.report.forwarded);
|
||||
const forward = ref(props.report.forwarded);
|
||||
|
||||
function resolve() {
|
||||
os.apiWithDialog("admin/resolve-abuse-user-report", {
|
||||
forward,
|
||||
forward: forward.value,
|
||||
reportId: props.report.id,
|
||||
}).then(() => {
|
||||
emit("resolved", props.report.id);
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onBeforeUnmount, onMounted } from "vue";
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||
import tinycolor from "tinycolor2";
|
||||
import { globalEvents } from "@/events.js";
|
||||
|
||||
|
@ -167,19 +167,19 @@ const texts = computed(() => {
|
|||
});
|
||||
|
||||
let enabled = true,
|
||||
majorGraduationColor = $ref<string>(),
|
||||
majorGraduationColor = ref<string>(),
|
||||
// let minorGraduationColor = $ref<string>();
|
||||
sHandColor = $ref<string>(),
|
||||
mHandColor = $ref<string>(),
|
||||
hHandColor = $ref<string>(),
|
||||
nowColor = $ref<string>(),
|
||||
h = $ref<number>(0),
|
||||
m = $ref<number>(0),
|
||||
s = $ref<number>(0),
|
||||
hAngle = $ref<number>(0),
|
||||
mAngle = $ref<number>(0),
|
||||
sAngle = $ref<number>(0),
|
||||
disableSAnimate = $ref(false),
|
||||
sHandColor = ref<string>(),
|
||||
mHandColor = ref<string>(),
|
||||
hHandColor = ref<string>(),
|
||||
nowColor = ref<string>(),
|
||||
h = ref<number>(0),
|
||||
m = ref<number>(0),
|
||||
s = ref<number>(0),
|
||||
hAngle = ref<number>(0),
|
||||
mAngle = ref<number>(0),
|
||||
sAngle = ref<number>(0),
|
||||
disableSAnimate = ref(false),
|
||||
sOneRound = false;
|
||||
|
||||
function tick() {
|
||||
|
@ -187,29 +187,31 @@ function tick() {
|
|||
now.setMinutes(
|
||||
now.getMinutes() + (new Date().getTimezoneOffset() + props.offset),
|
||||
);
|
||||
s = now.getSeconds();
|
||||
m = now.getMinutes();
|
||||
h = now.getHours();
|
||||
hAngle =
|
||||
(Math.PI * ((h % (props.twentyfour ? 24 : 12)) + (m + s / 60) / 60)) /
|
||||
s.value = now.getSeconds();
|
||||
m.value = now.getMinutes();
|
||||
h.value = now.getHours();
|
||||
hAngle.value =
|
||||
(Math.PI *
|
||||
((h.value % (props.twentyfour ? 24 : 12)) +
|
||||
(m.value + s.value / 60) / 60)) /
|
||||
(props.twentyfour ? 12 : 6);
|
||||
mAngle = (Math.PI * (m + s / 60)) / 30;
|
||||
mAngle.value = (Math.PI * (m.value + s.value / 60)) / 30;
|
||||
if (sOneRound) {
|
||||
// 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||
sAngle = (Math.PI * 60) / 30;
|
||||
sAngle.value = (Math.PI * 60) / 30;
|
||||
window.setTimeout(() => {
|
||||
disableSAnimate = true;
|
||||
disableSAnimate.value = true;
|
||||
window.setTimeout(() => {
|
||||
sAngle = 0;
|
||||
sAngle.value = 0;
|
||||
window.setTimeout(() => {
|
||||
disableSAnimate = false;
|
||||
disableSAnimate.value = false;
|
||||
}, 100);
|
||||
}, 100);
|
||||
}, 700);
|
||||
} else {
|
||||
sAngle = (Math.PI * s) / 30;
|
||||
sAngle.value = (Math.PI * s.value) / 30;
|
||||
}
|
||||
sOneRound = s === 59;
|
||||
sOneRound = s.value === 59;
|
||||
}
|
||||
|
||||
tick();
|
||||
|
@ -220,16 +222,16 @@ function calcColors() {
|
|||
const accent = tinycolor(
|
||||
computedStyle.getPropertyValue("--accent"),
|
||||
).toHexString();
|
||||
majorGraduationColor = dark
|
||||
majorGraduationColor.value = dark
|
||||
? "rgba(255, 255, 255, 0.3)"
|
||||
: "rgba(0, 0, 0, 0.3)";
|
||||
// minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
sHandColor = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
|
||||
mHandColor = tinycolor(
|
||||
sHandColor.value = dark ? "rgba(255, 255, 255, 0.5)" : "rgba(0, 0, 0, 0.3)";
|
||||
mHandColor.value = tinycolor(
|
||||
computedStyle.getPropertyValue("--fg"),
|
||||
).toHexString();
|
||||
hHandColor = accent;
|
||||
nowColor = accent;
|
||||
hHandColor.value = accent;
|
||||
nowColor.value = accent;
|
||||
}
|
||||
|
||||
calcColors();
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted } from "vue";
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
type?: "button" | "submit" | "reset";
|
||||
|
@ -49,13 +49,13 @@ const emit = defineEmits<{
|
|||
(ev: "click", payload: MouseEvent): void;
|
||||
}>();
|
||||
|
||||
const el = $ref<HTMLElement | null>(null);
|
||||
const ripples = $ref<HTMLElement | null>(null);
|
||||
const el = ref<HTMLElement | null>(null);
|
||||
const ripples = ref<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
nextTick(() => {
|
||||
el!.focus();
|
||||
el.value!.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -81,7 +81,7 @@ function onMousedown(evt: MouseEvent): void {
|
|||
ripple.style.top = (evt.clientY - rect.top - 1).toString() + "px";
|
||||
ripple.style.left = (evt.clientX - rect.left - 1).toString() + "px";
|
||||
|
||||
ripples!.appendChild(ripple);
|
||||
ripples.value!.appendChild(ripple);
|
||||
|
||||
const circleCenterX = evt.clientX - rect.left;
|
||||
const circleCenterY = evt.clientY - rect.top;
|
||||
|
@ -101,7 +101,7 @@ function onMousedown(evt: MouseEvent): void {
|
|||
ripple.style.opacity = "0";
|
||||
}, 1000);
|
||||
window.setTimeout(() => {
|
||||
if (ripples) ripples.removeChild(ripple);
|
||||
if (ripples.value) ripples.value.removeChild(ripple);
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||
import XCheatSheet from "@/pages/mfm-cheat-sheet.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
|
@ -20,7 +22,7 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted } from "vue";
|
||||
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||
import MkMenu from "@/components/MkMenu.vue";
|
||||
import type { MenuItem } from "@/types/menu";
|
||||
import contains from "@/scripts/contains";
|
||||
|
@ -27,16 +27,16 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
const rootEl = $ref<HTMLDivElement>();
|
||||
const rootEl = ref<HTMLDivElement>();
|
||||
|
||||
const zIndex = $ref<number>(os.claimZIndex("high"));
|
||||
const zIndex = ref<number>(os.claimZIndex("high"));
|
||||
|
||||
onMounted(() => {
|
||||
let left = props.ev.pageX + 1, // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||
top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||
|
||||
const width = rootEl.offsetWidth;
|
||||
const height = rootEl.offsetHeight;
|
||||
const width = rootEl.value.offsetWidth;
|
||||
const height = rootEl.value.offsetHeight;
|
||||
|
||||
if (left + width - window.pageXOffset > window.innerWidth) {
|
||||
left = window.innerWidth - width + window.pageXOffset;
|
||||
|
@ -54,8 +54,8 @@ onMounted(() => {
|
|||
left = 0;
|
||||
}
|
||||
|
||||
rootEl.style.top = `${top}px`;
|
||||
rootEl.style.left = `${left}px`;
|
||||
rootEl.value.style.top = `${top}px`;
|
||||
rootEl.value.style.left = `${left}px`;
|
||||
|
||||
document.body.addEventListener("mousedown", onMousedown);
|
||||
});
|
||||
|
@ -65,7 +65,8 @@ onBeforeUnmount(() => {
|
|||
});
|
||||
|
||||
function onMousedown(evt: Event) {
|
||||
if (!contains(rootEl, evt.target) && rootEl !== evt.target) emit("closed");
|
||||
if (!contains(rootEl.value, evt.target) && rootEl.value !== evt.target)
|
||||
emit("closed");
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import type * as misskey from "firefish-js";
|
||||
import Cropper from "cropperjs";
|
||||
import tinycolor from "tinycolor2";
|
||||
|
@ -62,10 +62,10 @@ const props = defineProps<{
|
|||
const imgUrl = `${url}/proxy/image.webp?${query({
|
||||
url: props.file.url,
|
||||
})}`;
|
||||
const dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
||||
const imgEl = $ref<HTMLImageElement>();
|
||||
const dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||
const imgEl = ref<HTMLImageElement>();
|
||||
let cropper: Cropper | null = null,
|
||||
loading = $ref(true);
|
||||
loading = ref(true);
|
||||
|
||||
const ok = async () => {
|
||||
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
||||
|
@ -96,16 +96,16 @@ const ok = async () => {
|
|||
const f = await promise;
|
||||
|
||||
emit("ok", f);
|
||||
dialogEl.close();
|
||||
dialogEl.value.close();
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit("cancel");
|
||||
dialogEl.close();
|
||||
dialogEl.value.close();
|
||||
};
|
||||
|
||||
const onImageLoad = () => {
|
||||
loading = false;
|
||||
loading.value = false;
|
||||
|
||||
if (cropper) {
|
||||
cropper.getCropperImage()!.$center("contain");
|
||||
|
@ -114,7 +114,7 @@ const onImageLoad = () => {
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
cropper = new Cropper(imgEl, {});
|
||||
cropper = new Cropper(imgEl.value, {});
|
||||
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from "vue";
|
||||
import * as Acct from "firefish-js/built/acct";
|
||||
import MkModal from "@/components/MkModal.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
|
@ -281,17 +281,15 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
|
|||
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
||||
const selectedValue = ref(props.select?.default ?? null);
|
||||
|
||||
let disabledReason = $ref<null | "charactersExceeded" | "charactersBelow">(
|
||||
null,
|
||||
);
|
||||
const okButtonDisabled = $computed<boolean>(() => {
|
||||
let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null);
|
||||
const okButtonDisabled = computed<boolean>(() => {
|
||||
if (props.input) {
|
||||
if (props.input.minLength) {
|
||||
if (
|
||||
(inputValue.value || inputValue.value === "") &&
|
||||
(inputValue.value as string).length < props.input.minLength
|
||||
) {
|
||||
disabledReason = "charactersBelow";
|
||||
disabledReason.value = "charactersBelow";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +298,7 @@ const okButtonDisabled = $computed<boolean>(() => {
|
|||
inputValue.value &&
|
||||
(inputValue.value as string).length > props.input.maxLength
|
||||
) {
|
||||
disabledReason = "charactersExceeded";
|
||||
disabledReason.value = "charactersExceeded";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onBeforeUnmount, onMounted } from "vue";
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||
import type * as Misskey from "firefish-js";
|
||||
import * as os from "@/os";
|
||||
import { stream } from "@/stream";
|
||||
|
@ -89,13 +89,13 @@ const props = withDefaults(
|
|||
|
||||
const isBlocking = computed(() => props.user.isBlocking);
|
||||
|
||||
let state = $ref(i18n.ts.processing);
|
||||
let state = ref(i18n.ts.processing);
|
||||
|
||||
let isFollowing = $ref(props.user.isFollowing);
|
||||
let hasPendingFollowRequestFromYou = $ref(
|
||||
let isFollowing = ref(props.user.isFollowing);
|
||||
let hasPendingFollowRequestFromYou = ref(
|
||||
props.user.hasPendingFollowRequestFromYou,
|
||||
);
|
||||
let wait = $ref(false);
|
||||
let wait = ref(false);
|
||||
const connection = stream.useChannel("main");
|
||||
|
||||
const hideFollowButton = props.hideFollowButton ?? false;
|
||||
|
@ -108,13 +108,14 @@ if (props.user.isFollowing == null) {
|
|||
|
||||
function onFollowChange(user: Misskey.entities.UserDetailed) {
|
||||
if (user.id === props.user.id) {
|
||||
isFollowing = user.isFollowing;
|
||||
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
isFollowing.value = user.isFollowing;
|
||||
hasPendingFollowRequestFromYou.value =
|
||||
user.hasPendingFollowRequestFromYou;
|
||||
}
|
||||
}
|
||||
|
||||
async function onClick() {
|
||||
wait = true;
|
||||
wait.value = true;
|
||||
|
||||
try {
|
||||
if (isBlocking.value) {
|
||||
|
@ -133,7 +134,7 @@ async function onClick() {
|
|||
});
|
||||
}
|
||||
emit("refresh");
|
||||
} else if (isFollowing) {
|
||||
} else if (isFollowing.value) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: "warning",
|
||||
text: i18n.t("unfollowConfirm", {
|
||||
|
@ -147,22 +148,22 @@ async function onClick() {
|
|||
userId: props.user.id,
|
||||
});
|
||||
} else {
|
||||
if (hasPendingFollowRequestFromYou) {
|
||||
if (hasPendingFollowRequestFromYou.value) {
|
||||
await os.api("following/requests/cancel", {
|
||||
userId: props.user.id,
|
||||
});
|
||||
hasPendingFollowRequestFromYou = false;
|
||||
hasPendingFollowRequestFromYou.value = false;
|
||||
} else {
|
||||
await os.api("following/create", {
|
||||
userId: props.user.id,
|
||||
});
|
||||
hasPendingFollowRequestFromYou = true;
|
||||
hasPendingFollowRequestFromYou.value = true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
wait = false;
|
||||
wait.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import {} from "vue";
|
||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
|
@ -75,20 +77,20 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
let dialog: InstanceType<typeof XModalWindow> = $ref();
|
||||
let dialog: InstanceType<typeof XModalWindow> = ref();
|
||||
|
||||
let username = $ref("");
|
||||
let email = $ref("");
|
||||
let processing = $ref(false);
|
||||
let username = ref("");
|
||||
let email = ref("");
|
||||
let processing = ref(false);
|
||||
|
||||
async function onSubmit() {
|
||||
processing = true;
|
||||
processing.value = true;
|
||||
await os.apiWithDialog("request-reset-password", {
|
||||
username,
|
||||
email,
|
||||
username: username.value,
|
||||
email: email.value,
|
||||
});
|
||||
emit("done");
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, watch } from "vue";
|
||||
import { onMounted, nextTick, watch, shallowRef, ref } from "vue";
|
||||
import { Chart } from "chart.js";
|
||||
import * as os from "@/os";
|
||||
import { defaultStore } from "@/store";
|
||||
|
@ -23,11 +23,11 @@ const props = defineProps<{
|
|||
src: string;
|
||||
}>();
|
||||
|
||||
const rootEl = $shallowRef<HTMLDivElement>(null);
|
||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
||||
const rootEl = shallowRef<HTMLDivElement>(null);
|
||||
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
||||
const now = new Date();
|
||||
let chartInstance: Chart = null;
|
||||
let fetching = $ref(true);
|
||||
let fetching = ref(true);
|
||||
|
||||
const { handler: externalTooltipHandler } = useChartTooltip({
|
||||
position: "middle",
|
||||
|
@ -43,8 +43,8 @@ async function renderChart() {
|
|||
chartInstance.destroy();
|
||||
}
|
||||
|
||||
const wide = rootEl.offsetWidth > 700;
|
||||
const narrow = rootEl.offsetWidth < 400;
|
||||
const wide = rootEl.value.offsetWidth > 700;
|
||||
const narrow = rootEl.value.offsetWidth < 400;
|
||||
|
||||
const weeks = wide ? 50 : narrow ? 10 : 25;
|
||||
const chartLimit = 7 * weeks;
|
||||
|
@ -113,7 +113,7 @@ async function renderChart() {
|
|||
values = addArrays(raw.diffs.normal, raw.diffs.reply, raw.diffs.renote);
|
||||
}
|
||||
|
||||
fetching = false;
|
||||
fetching.value = false;
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
@ -131,7 +131,7 @@ async function renderChart() {
|
|||
|
||||
const marginEachCell = 4;
|
||||
|
||||
chartInstance = new Chart(chartEl, {
|
||||
chartInstance = new Chart(chartEl.value, {
|
||||
type: "matrix",
|
||||
data: {
|
||||
datasets: [
|
||||
|
@ -247,7 +247,7 @@ async function renderChart() {
|
|||
watch(
|
||||
() => props.src,
|
||||
() => {
|
||||
fetching = true;
|
||||
fetching.value = true;
|
||||
renderChart();
|
||||
},
|
||||
);
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import {} from "vue";
|
||||
import type * as misskey from "firefish-js";
|
||||
import bytes from "@/filters/bytes";
|
||||
|
@ -43,7 +45,7 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
||||
const modal = ref<InstanceType<typeof MkModal>>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { decodeBlurHash } from "fast-blurhash";
|
||||
|
||||
const props = withDefaults(
|
||||
|
@ -48,20 +48,20 @@ const props = withDefaults(
|
|||
},
|
||||
);
|
||||
|
||||
const canvas = $ref<HTMLCanvasElement>();
|
||||
let loaded = $ref(false);
|
||||
const canvas = ref<HTMLCanvasElement>();
|
||||
let loaded = ref(false);
|
||||
|
||||
function draw() {
|
||||
if (props.hash == null || canvas == null) return;
|
||||
if (props.hash == null || canvas.value == null) return;
|
||||
const pixels = decodeBlurHash(props.hash, props.size, props.size);
|
||||
const ctx = canvas.getContext("2d");
|
||||
const ctx = canvas.value.getContext("2d");
|
||||
const imageData = ctx!.createImageData(props.size, props.size);
|
||||
imageData.data.set(pixels);
|
||||
ctx!.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
loaded = true;
|
||||
loaded.value = true;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import * as firefish from "firefish-js";
|
||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||
import * as os from "@/os";
|
||||
|
@ -33,7 +35,7 @@ const props = defineProps<{
|
|||
instance: firefish.entities.Instance;
|
||||
}>();
|
||||
|
||||
let chartValues = $ref<number[] | null>(null);
|
||||
let chartValues = ref<number[] | null>(null);
|
||||
|
||||
os.apiGet("charts/instance", {
|
||||
host: props.instance.host,
|
||||
|
@ -42,7 +44,7 @@ os.apiGet("charts/instance", {
|
|||
}).then((res) => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res.requests.received.splice(0, 1);
|
||||
chartValues = res.requests.received;
|
||||
chartValues.value = res.requests.received;
|
||||
});
|
||||
|
||||
function getInstanceIcon(instance): string {
|
||||
|
|
|
@ -56,6 +56,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import MkInput from "@/components/form/input.vue";
|
||||
import XModalWindow from "@/components/MkModalWindow.vue";
|
||||
import * as os from "@/os";
|
||||
|
@ -68,28 +70,28 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
let hostname = $ref("");
|
||||
let instances: Instance[] = $ref([]);
|
||||
let selected: Instance | null = $ref(null);
|
||||
let dialogEl = $ref<InstanceType<typeof XModalWindow>>();
|
||||
let hostname = ref("");
|
||||
let instances: Instance[] = ref([]);
|
||||
let selected: Instance | null = ref(null);
|
||||
let dialogEl = ref<InstanceType<typeof XModalWindow>>();
|
||||
|
||||
let searchOrderLatch = 0;
|
||||
const search = () => {
|
||||
if (hostname === "") {
|
||||
instances = [];
|
||||
if (hostname.value === "") {
|
||||
instances.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const searchId = ++searchOrderLatch;
|
||||
os.api("federation/instances", {
|
||||
host: hostname,
|
||||
host: hostname.value,
|
||||
limit: 10,
|
||||
blocked: false,
|
||||
suspended: false,
|
||||
sort: "+pubSub",
|
||||
}).then((_instances) => {
|
||||
if (searchId !== searchOrderLatch) return;
|
||||
instances = _instances.map(
|
||||
instances.value = _instances.map(
|
||||
(x) =>
|
||||
({
|
||||
id: x.id,
|
||||
|
@ -101,14 +103,14 @@ const search = () => {
|
|||
};
|
||||
|
||||
const ok = () => {
|
||||
if (selected == null) return;
|
||||
emit("ok", selected);
|
||||
dialogEl?.close();
|
||||
if (selected.value == null) return;
|
||||
emit("ok", selected.value);
|
||||
dialogEl.value?.close();
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit("cancel");
|
||||
dialogEl?.close();
|
||||
dialogEl.value?.close();
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref, shallowRef } from "vue";
|
||||
import { Chart } from "chart.js";
|
||||
import MkSelect from "@/components/form/select.vue";
|
||||
import MkChart from "@/components/MkChart.vue";
|
||||
|
@ -116,11 +116,11 @@ import { initChart } from "@/scripts/init-chart";
|
|||
initChart();
|
||||
|
||||
const chartLimit = 500;
|
||||
let chartSpan = $ref<"hour" | "day">("hour");
|
||||
let chartSrc = $ref("active-users");
|
||||
let heatmapSrc = $ref("active-users");
|
||||
let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
||||
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
||||
let chartSpan = ref<"hour" | "day">("hour");
|
||||
let chartSrc = ref("active-users");
|
||||
let heatmapSrc = ref("active-users");
|
||||
let subDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||
let pubDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||
|
||||
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
||||
position: "middle",
|
||||
|
@ -189,7 +189,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
|||
onMounted(() => {
|
||||
os.apiGet("federation/stats", { limit: 30 }).then((fedStats) => {
|
||||
createDoughnut(
|
||||
subDoughnutEl,
|
||||
subDoughnutEl.value,
|
||||
externalTooltipHandler1,
|
||||
fedStats.topSubInstances
|
||||
.map((x) => ({
|
||||
|
@ -210,7 +210,7 @@ onMounted(() => {
|
|||
);
|
||||
|
||||
createDoughnut(
|
||||
pubDoughnutEl,
|
||||
pubDoughnutEl.value,
|
||||
externalTooltipHandler2,
|
||||
fedStats.topPubInstances
|
||||
.map((x) => ({
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import { instanceName, version } from "@/config";
|
||||
import { instance as Instance } from "@/instance";
|
||||
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
|
||||
|
@ -27,7 +29,7 @@ const props = defineProps<{
|
|||
};
|
||||
}>();
|
||||
|
||||
let ticker = $ref<HTMLElement | null>(null);
|
||||
let ticker = ref<HTMLElement | null>(null);
|
||||
|
||||
// if no instance data is given, this is for the local instance
|
||||
const instance = props.instance ?? {
|
||||
|
@ -46,12 +48,15 @@ const commonNames = new Map<string, string>([
|
|||
["birdsitelive", "BirdsiteLIVE"],
|
||||
["bookwyrm", "BookWyrm"],
|
||||
["bridgy-fed", "Bridgy Fed"],
|
||||
["castopod", "CastoPod"],
|
||||
["foundkey", "FoundKey"],
|
||||
["gnusocial", "GNU social"],
|
||||
["gotosocial", "GoToSocial"],
|
||||
["kbin", "/kbin"],
|
||||
["microblogpub", "microblog.pub"],
|
||||
["nextcloud social", "Nextcloud Social"],
|
||||
["peertube", "PeerTube"],
|
||||
["reel2bits", "reel2bits"],
|
||||
["snac", "snac"],
|
||||
["snac2", "snac2"],
|
||||
["takahe", "Takahē"],
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import MkModal from "@/components/MkModal.vue";
|
||||
import { navbarItemDef } from "@/navbar";
|
||||
import { defaultStore } from "@/store";
|
||||
|
@ -89,7 +91,7 @@ const preferedModalType =
|
|||
? "drawer"
|
||||
: "dialog";
|
||||
|
||||
const modal = $ref<InstanceType<typeof MkModal>>();
|
||||
const modal = ref<InstanceType<typeof MkModal>>();
|
||||
|
||||
const menu = defaultStore.state.menu;
|
||||
|
||||
|
@ -107,7 +109,7 @@ const items = Object.keys(navbarItemDef)
|
|||
}));
|
||||
|
||||
function close() {
|
||||
modal.close();
|
||||
modal.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import { defineAsyncComponent, ref } from "vue";
|
||||
import { url as local } from "@/config";
|
||||
import { useTooltip } from "@/scripts/use-tooltip";
|
||||
import * as os from "@/os";
|
||||
|
@ -35,9 +35,9 @@ const self = props.url.startsWith(local);
|
|||
const attr = self ? "to" : "href";
|
||||
const target = self ? null : "_blank";
|
||||
|
||||
const el = $ref();
|
||||
const el = ref();
|
||||
|
||||
useTooltip($$(el), (showing) => {
|
||||
useTooltip(el, (showing) => {
|
||||
os.popup(
|
||||
defineAsyncComponent(
|
||||
() => import("@/components/MkUrlPreviewPopup.vue"),
|
||||
|
@ -45,7 +45,7 @@ useTooltip($$(el), (showing) => {
|
|||
{
|
||||
showing,
|
||||
url: props.url,
|
||||
source: el,
|
||||
source: el.value,
|
||||
},
|
||||
{},
|
||||
"closed",
|
||||
|
|
|
@ -104,7 +104,7 @@ const props = defineProps<{
|
|||
raw?: boolean;
|
||||
}>();
|
||||
|
||||
let hide = $ref(true);
|
||||
let hide = ref(true);
|
||||
|
||||
const plyr = ref();
|
||||
|
||||
|
@ -145,7 +145,7 @@ function captionPopup() {
|
|||
watch(
|
||||
() => props.media,
|
||||
() => {
|
||||
hide =
|
||||
hide.value =
|
||||
defaultStore.state.nsfw === "force"
|
||||
? true
|
||||
: props.media.isSensitive &&
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import VuePlyr from "vue-plyr";
|
||||
import type * as misskey from "firefish-js";
|
||||
import { ColdDeviceStorage } from "@/store";
|
||||
|
@ -70,15 +70,17 @@ const props = withDefaults(
|
|||
{},
|
||||
);
|
||||
|
||||
const audioEl = $ref<HTMLAudioElement | null>();
|
||||
let hide = $ref(true);
|
||||
const audioEl = ref<HTMLAudioElement | null>();
|
||||
let hide = ref(true);
|
||||
|
||||
function volumechange() {
|
||||
if (audioEl) ColdDeviceStorage.set("mediaVolume", audioEl.volume);
|
||||
if (audioEl.value)
|
||||
ColdDeviceStorage.set("mediaVolume", audioEl.value.volume);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (audioEl) audioEl.volume = ColdDeviceStorage.get("mediaVolume");
|
||||
if (audioEl.value)
|
||||
audioEl.value.volume = ColdDeviceStorage.get("mediaVolume");
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -233,13 +233,13 @@ const emit = defineEmits<{
|
|||
(ev: "close", actioned?: boolean): void;
|
||||
}>();
|
||||
|
||||
let itemsEl = $ref<HTMLDivElement>();
|
||||
let itemsEl = ref<HTMLDivElement>();
|
||||
|
||||
let items2: InnerMenuItem[] = $ref([]);
|
||||
let items2: InnerMenuItem[] = ref([]);
|
||||
|
||||
let child = $ref<InstanceType<typeof XChild>>();
|
||||
let child = ref<InstanceType<typeof XChild>>();
|
||||
|
||||
let childShowingItem = $ref<MenuItem | null>();
|
||||
let childShowingItem = ref<MenuItem | null>();
|
||||
|
||||
watch(
|
||||
() => props.items,
|
||||
|
@ -255,24 +255,24 @@ watch(
|
|||
// if item is Promise
|
||||
items[i] = { type: "pending" };
|
||||
item.then((actualItem) => {
|
||||
items2[i] = actualItem;
|
||||
items2.value[i] = actualItem;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items2 = items as InnerMenuItem[];
|
||||
items2.value = items as InnerMenuItem[];
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
let childMenu = $ref<MenuItem[] | null>();
|
||||
let childTarget = $ref<HTMLElement | null>();
|
||||
let childMenu = ref<MenuItem[] | null>();
|
||||
let childTarget = ref<HTMLElement | null>();
|
||||
|
||||
function closeChild() {
|
||||
childMenu = null;
|
||||
childShowingItem = null;
|
||||
childMenu.value = null;
|
||||
childShowingItem.value = null;
|
||||
}
|
||||
|
||||
function childActioned() {
|
||||
|
@ -282,11 +282,12 @@ function childActioned() {
|
|||
|
||||
function onGlobalMousedown(event: MouseEvent) {
|
||||
if (
|
||||
childTarget &&
|
||||
(event.target === childTarget || childTarget.contains(event.target))
|
||||
childTarget.value &&
|
||||
(event.target === childTarget.value ||
|
||||
childTarget.value.contains(event.target))
|
||||
)
|
||||
return;
|
||||
if (child && child.checkHit(event)) return;
|
||||
if (child.value && child.value.checkHit(event)) return;
|
||||
closeChild();
|
||||
}
|
||||
|
||||
|
@ -305,9 +306,9 @@ async function showChildren(item: MenuItem, ev: MouseEvent) {
|
|||
os.popupMenu(item.children, ev.currentTarget ?? ev.target);
|
||||
close();
|
||||
} else {
|
||||
childTarget = ev.currentTarget ?? ev.target;
|
||||
childMenu = item.children;
|
||||
childShowingItem = item;
|
||||
childTarget.value = ev.currentTarget ?? ev.target;
|
||||
childMenu.value = item.children;
|
||||
childShowingItem.value = item;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from "vue";
|
||||
import { watch, ref } from "vue";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import tinycolor from "tinycolor2";
|
||||
import { useInterval } from "@/scripts/use-interval";
|
||||
|
@ -37,10 +37,10 @@ const props = defineProps<{
|
|||
const viewBoxX = 50;
|
||||
const viewBoxY = 50;
|
||||
const gradientId = uuid();
|
||||
let polylinePoints = $ref("");
|
||||
let polygonPoints = $ref("");
|
||||
let headX = $ref<number | null>(null);
|
||||
let headY = $ref<number | null>(null);
|
||||
let polylinePoints = ref("");
|
||||
let polygonPoints = ref("");
|
||||
let headX = ref<number | null>(null);
|
||||
let headY = ref<number | null>(null);
|
||||
const accent = tinycolor(
|
||||
getComputedStyle(document.documentElement).getPropertyValue("--accent"),
|
||||
);
|
||||
|
@ -55,12 +55,14 @@ function draw(): void {
|
|||
(1 - n / peak) * viewBoxY,
|
||||
]);
|
||||
|
||||
polylinePoints = _polylinePoints.map((xy) => `${xy[0]},${xy[1]}`).join(" ");
|
||||
polylinePoints.value = _polylinePoints
|
||||
.map((xy) => `${xy[0]},${xy[1]}`)
|
||||
.join(" ");
|
||||
|
||||
polygonPoints = `0,${viewBoxY} ${polylinePoints} ${viewBoxX},${viewBoxY}`;
|
||||
polygonPoints.value = `0,${viewBoxY} ${polylinePoints.value} ${viewBoxX},${viewBoxY}`;
|
||||
|
||||
headX = _polylinePoints[_polylinePoints.length - 1][0];
|
||||
headY = _polylinePoints[_polylinePoints.length - 1][1];
|
||||
headX.value = _polylinePoints[_polylinePoints.length - 1][0];
|
||||
headY.value = _polylinePoints[_polylinePoints.length - 1][1];
|
||||
}
|
||||
|
||||
watch(() => props.src, draw, { immediate: true });
|
||||
|
|
|
@ -77,7 +77,16 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, watch, provide, onUnmounted } from "vue";
|
||||
import {
|
||||
nextTick,
|
||||
onMounted,
|
||||
watch,
|
||||
provide,
|
||||
onUnmounted,
|
||||
ref,
|
||||
shallowRef,
|
||||
computed,
|
||||
} from "vue";
|
||||
import * as os from "@/os";
|
||||
import { isTouchUsing } from "@/scripts/touch";
|
||||
import { defaultStore } from "@/store";
|
||||
|
@ -130,14 +139,14 @@ const emit = defineEmits<{
|
|||
|
||||
provide("modal", true);
|
||||
|
||||
let maxHeight = $ref<number>();
|
||||
let fixed = $ref(false);
|
||||
let transformOrigin = $ref("center");
|
||||
let showing = $ref(true);
|
||||
let content = $shallowRef<HTMLElement>();
|
||||
let maxHeight = ref<number>();
|
||||
let fixed = ref(false);
|
||||
let transformOrigin = ref("center");
|
||||
let showing = ref(true);
|
||||
let content = shallowRef<HTMLElement>();
|
||||
const zIndex = os.claimZIndex(props.zPriority);
|
||||
let useSendAnime = $ref(false);
|
||||
const type = $computed<ModalTypes>(() => {
|
||||
let useSendAnime = ref(false);
|
||||
const type = computed<ModalTypes>(() => {
|
||||
if (props.preferType === "auto") {
|
||||
if (
|
||||
!defaultStore.state.disableDrawer &&
|
||||
|
@ -152,28 +161,28 @@ const type = $computed<ModalTypes>(() => {
|
|||
return props.preferType!;
|
||||
}
|
||||
});
|
||||
const isEnableBgTransparent = $computed(
|
||||
() => props.transparentBg && type === "popup",
|
||||
const isEnableBgTransparent = computed(
|
||||
() => props.transparentBg && type.value === "popup",
|
||||
);
|
||||
let transitionName = $computed(() =>
|
||||
let transitionName = computed(() =>
|
||||
defaultStore.state.animation
|
||||
? useSendAnime
|
||||
? useSendAnime.value
|
||||
? "send"
|
||||
: type === "drawer"
|
||||
: type.value === "drawer"
|
||||
? "modal-drawer"
|
||||
: type === "popup"
|
||||
: type.value === "popup"
|
||||
? "modal-popup"
|
||||
: "modal"
|
||||
: "",
|
||||
);
|
||||
let transitionDuration = $computed(() =>
|
||||
transitionName === "send"
|
||||
let transitionDuration = computed(() =>
|
||||
transitionName.value === "send"
|
||||
? 400
|
||||
: transitionName === "modal-popup"
|
||||
: transitionName.value === "modal-popup"
|
||||
? 100
|
||||
: transitionName === "modal"
|
||||
: transitionName.value === "modal"
|
||||
? 200
|
||||
: transitionName === "modal-drawer"
|
||||
: transitionName.value === "modal-drawer"
|
||||
? 200
|
||||
: 0,
|
||||
);
|
||||
|
@ -187,12 +196,12 @@ function close(ev, opts: { useSendAnimation?: boolean } = {}) {
|
|||
// history.forward();
|
||||
// }
|
||||
if (opts.useSendAnimation) {
|
||||
useSendAnime = true;
|
||||
useSendAnime.value = true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
if (props.src) props.src.style.pointerEvents = "auto";
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
emit("close");
|
||||
if (!props.noReturnFocus) {
|
||||
focusedElement.focus();
|
||||
|
@ -204,8 +213,8 @@ function onBgClick() {
|
|||
emit("click");
|
||||
}
|
||||
|
||||
if (type === "drawer") {
|
||||
maxHeight = window.innerHeight / 1.5;
|
||||
if (type.value === "drawer") {
|
||||
maxHeight.value = window.innerHeight / 1.5;
|
||||
}
|
||||
|
||||
const keymap = {
|
||||
|
@ -216,21 +225,21 @@ const MARGIN = 16;
|
|||
|
||||
const align = () => {
|
||||
if (props.src == null) return;
|
||||
if (type === "drawer") return;
|
||||
if (type === "dialog") return;
|
||||
if (type.value === "drawer") return;
|
||||
if (type.value === "dialog") return;
|
||||
|
||||
if (content == null) return;
|
||||
if (content.value == null) return;
|
||||
|
||||
const srcRect = props.src.getBoundingClientRect();
|
||||
|
||||
const width = content!.offsetWidth;
|
||||
const height = content!.offsetHeight;
|
||||
const width = content.value!.offsetWidth;
|
||||
const height = content.value!.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
|
||||
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
|
||||
|
||||
if (props.anchor.x === "center") {
|
||||
left = x + props.src.offsetWidth / 2 - width / 2;
|
||||
|
@ -248,7 +257,7 @@ const align = () => {
|
|||
top = y + props.src.offsetHeight;
|
||||
}
|
||||
|
||||
if (fixed) {
|
||||
if (fixed.value) {
|
||||
// 画面から横にはみ出る場合
|
||||
if (left + width > window.innerWidth) {
|
||||
left = window.innerWidth - width;
|
||||
|
@ -261,16 +270,16 @@ const align = () => {
|
|||
if (top + height > window.innerHeight - MARGIN) {
|
||||
if (props.noOverlap && props.anchor.x === "center") {
|
||||
if (underSpace >= upperSpace / 3) {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight = upperSpace;
|
||||
maxHeight.value = upperSpace;
|
||||
top = upperSpace + MARGIN - height;
|
||||
}
|
||||
} else {
|
||||
top = window.innerHeight - MARGIN - height;
|
||||
}
|
||||
} else {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
}
|
||||
} else {
|
||||
// 画面から横にはみ出る場合
|
||||
|
@ -286,9 +295,9 @@ const align = () => {
|
|||
if (top + height - window.scrollY > window.innerHeight - MARGIN) {
|
||||
if (props.noOverlap && props.anchor.x === "center") {
|
||||
if (underSpace >= upperSpace / 3) {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight = upperSpace;
|
||||
maxHeight.value = upperSpace;
|
||||
top = window.scrollY + (upperSpace + MARGIN - height);
|
||||
}
|
||||
} else {
|
||||
|
@ -300,7 +309,7 @@ const align = () => {
|
|||
1;
|
||||
}
|
||||
} else {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,36 +326,43 @@ const align = () => {
|
|||
|
||||
if (
|
||||
top >=
|
||||
srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)
|
||||
srcRect.top +
|
||||
props.src.offsetHeight +
|
||||
(fixed.value ? 0 : window.pageYOffset)
|
||||
) {
|
||||
transformOriginY = "top";
|
||||
} else if (top + height <= srcRect.top + (fixed ? 0 : window.pageYOffset)) {
|
||||
} else if (
|
||||
top + height <=
|
||||
srcRect.top + (fixed.value ? 0 : window.pageYOffset)
|
||||
) {
|
||||
transformOriginY = "bottom";
|
||||
}
|
||||
|
||||
if (
|
||||
left >=
|
||||
srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)
|
||||
srcRect.left +
|
||||
props.src.offsetWidth +
|
||||
(fixed.value ? 0 : window.pageXOffset)
|
||||
) {
|
||||
transformOriginX = "left";
|
||||
} else if (
|
||||
left + width <=
|
||||
srcRect.left + (fixed ? 0 : window.pageXOffset)
|
||||
srcRect.left + (fixed.value ? 0 : window.pageXOffset)
|
||||
) {
|
||||
transformOriginX = "right";
|
||||
}
|
||||
|
||||
transformOrigin = `${transformOriginX} ${transformOriginY}`;
|
||||
transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
|
||||
|
||||
content.style.left = left + "px";
|
||||
content.style.top = top + "px";
|
||||
content.value.style.left = left + "px";
|
||||
content.value.style.top = top + "px";
|
||||
};
|
||||
|
||||
const onOpened = () => {
|
||||
emit("opened");
|
||||
|
||||
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
||||
const el = content!.children[0];
|
||||
const el = content.value!.children[0];
|
||||
el.addEventListener(
|
||||
"mousedown",
|
||||
(ev) => {
|
||||
|
@ -378,7 +394,8 @@ onMounted(() => {
|
|||
// eslint-disable-next-line vue/no-mutating-props
|
||||
props.src.style.pointerEvents = "none";
|
||||
}
|
||||
fixed = type === "drawer" || getFixedContainer(props.src) != null;
|
||||
fixed.value =
|
||||
type.value === "drawer" || getFixedContainer(props.src) != null;
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
@ -390,7 +407,7 @@ onMounted(() => {
|
|||
nextTick(() => {
|
||||
new ResizeObserver((entries, observer) => {
|
||||
align();
|
||||
}).observe(content!);
|
||||
}).observe(content.value!);
|
||||
});
|
||||
});
|
||||
onUnmounted(() => {
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ComputedRef, provide } from "vue";
|
||||
import { ComputedRef, provide, ref, computed } from "vue";
|
||||
import MkModal from "@/components/MkModal.vue";
|
||||
import { popout as _popout } from "@/scripts/popout";
|
||||
import copyToClipboard from "@/scripts/copy-to-clipboard";
|
||||
|
@ -76,27 +76,27 @@ const router = new Router(routes, props.initialPath);
|
|||
|
||||
router.addListener("push", (ctx) => {});
|
||||
|
||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||
let rootEl = $ref();
|
||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
||||
let path = $ref(props.initialPath);
|
||||
let width = $ref(860);
|
||||
let height = $ref(660);
|
||||
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||
let rootEl = ref();
|
||||
let modal = ref<InstanceType<typeof MkModal>>();
|
||||
let path = ref(props.initialPath);
|
||||
let width = ref(860);
|
||||
let height = ref(660);
|
||||
const history = [];
|
||||
|
||||
provide("router", router);
|
||||
provideMetadataReceiver((info) => {
|
||||
pageMetadata = info;
|
||||
pageMetadata.value = info;
|
||||
});
|
||||
provide("shouldOmitHeaderTitle", true);
|
||||
provide("shouldHeaderThin", true);
|
||||
|
||||
const pageUrl = $computed(() => url + path);
|
||||
const contextmenu = $computed(() => {
|
||||
const pageUrl = computed(() => url + path.value);
|
||||
const contextmenu = computed(() => {
|
||||
return [
|
||||
{
|
||||
type: "label",
|
||||
text: path,
|
||||
text: path.value,
|
||||
},
|
||||
{
|
||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||
|
@ -113,15 +113,15 @@ const contextmenu = $computed(() => {
|
|||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||
text: i18n.ts.openInNewTab,
|
||||
action: () => {
|
||||
window.open(pageUrl, "_blank");
|
||||
modal.close();
|
||||
window.open(pageUrl.value, "_blank");
|
||||
modal.value.close();
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "ph-link-simple ph-bold ph-lg",
|
||||
text: i18n.ts.copyLink,
|
||||
action: () => {
|
||||
copyToClipboard(pageUrl);
|
||||
copyToClipboard(pageUrl.value);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -137,17 +137,17 @@ function back() {
|
|||
}
|
||||
|
||||
function expand() {
|
||||
mainRouter.push(path);
|
||||
modal.close();
|
||||
mainRouter.push(path.value);
|
||||
modal.value.close();
|
||||
}
|
||||
|
||||
function popout() {
|
||||
_popout(path, rootEl);
|
||||
modal.close();
|
||||
_popout(path.value, rootEl.value);
|
||||
modal.value.close();
|
||||
}
|
||||
|
||||
function onContextmenu(ev: MouseEvent) {
|
||||
os.contextMenu(contextmenu, ev);
|
||||
os.contextMenu(contextmenu.value, ev);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from "vue";
|
||||
|
||||
import { FocusTrap } from "focus-trap-vue";
|
||||
import MkModal from "./MkModal.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
|
@ -90,12 +92,12 @@ const emit = defineEmits<{
|
|||
(event: "ok"): void;
|
||||
}>();
|
||||
|
||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||
let rootEl = $shallowRef<HTMLElement>();
|
||||
let headerEl = $shallowRef<HTMLElement>();
|
||||
let modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
let rootEl = shallowRef<HTMLElement>();
|
||||
let headerEl = shallowRef<HTMLElement>();
|
||||
|
||||
const close = (ev) => {
|
||||
modal?.close(ev);
|
||||
modal.value?.close(ev);
|
||||
};
|
||||
|
||||
const onBgClick = () => {
|
||||
|
|
|
@ -306,7 +306,7 @@ const props = defineProps<{
|
|||
|
||||
const inChannel = inject("inChannel", null);
|
||||
|
||||
let note = $ref(deepClone(props.note));
|
||||
let note = ref(deepClone(props.note));
|
||||
|
||||
const softMuteReasonI18nSrc = (what?: string) => {
|
||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||
|
@ -321,19 +321,19 @@ const softMuteReasonI18nSrc = (what?: string) => {
|
|||
// plugin
|
||||
if (noteViewInterruptors.length > 0) {
|
||||
onMounted(async () => {
|
||||
let result = deepClone(note);
|
||||
let result = deepClone(note.value);
|
||||
for (const interruptor of noteViewInterruptors) {
|
||||
result = await interruptor.handler(result);
|
||||
}
|
||||
note = result;
|
||||
note.value = result;
|
||||
});
|
||||
}
|
||||
|
||||
const isRenote =
|
||||
note.renote != null &&
|
||||
note.text == null &&
|
||||
note.fileIds.length === 0 &&
|
||||
note.poll == null;
|
||||
note.value.renote != null &&
|
||||
note.value.text == null &&
|
||||
note.value.fileIds.length === 0 &&
|
||||
note.value.poll == null;
|
||||
|
||||
const el = ref<HTMLElement>();
|
||||
const footerEl = ref<HTMLElement>();
|
||||
|
@ -342,13 +342,15 @@ const starButton = ref<InstanceType<typeof XStarButton>>();
|
|||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||
const renoteTime = ref<HTMLElement>();
|
||||
const reactButton = ref<HTMLElement>();
|
||||
let appearNote = $computed(() =>
|
||||
isRenote ? (note.renote as misskey.entities.Note) : note,
|
||||
let appearNote = computed(() =>
|
||||
isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
|
||||
);
|
||||
const isMyRenote = $i && $i.id === note.userId;
|
||||
const isMyRenote = $i && $i.id === note.value.userId;
|
||||
const showContent = ref(false);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
|
||||
const muted = ref(
|
||||
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
|
||||
);
|
||||
const translation = ref(null);
|
||||
const translating = ref(false);
|
||||
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
||||
|
@ -399,7 +401,7 @@ const keymap = {
|
|||
|
||||
useNoteCapture({
|
||||
rootEl: el,
|
||||
note: $$(appearNote),
|
||||
note: appearNote,
|
||||
isDeletedRef: isDeleted,
|
||||
});
|
||||
|
||||
|
@ -407,7 +409,7 @@ function reply(viaKeyboard = false): void {
|
|||
pleaseLogin();
|
||||
os.post(
|
||||
{
|
||||
reply: appearNote,
|
||||
reply: appearNote.value,
|
||||
animation: !viaKeyboard,
|
||||
},
|
||||
() => {
|
||||
|
@ -423,7 +425,7 @@ function react(viaKeyboard = false): void {
|
|||
reactButton.value,
|
||||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
},
|
||||
|
@ -466,21 +468,24 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
[
|
||||
{
|
||||
type: "label",
|
||||
text: notePage(appearNote),
|
||||
text: notePage(appearNote.value),
|
||||
},
|
||||
{
|
||||
icon: "ph-browser ph-bold ph-lg",
|
||||
text: i18n.ts.openInWindow,
|
||||
action: () => {
|
||||
os.pageWindow(notePage(appearNote));
|
||||
os.pageWindow(notePage(appearNote.value));
|
||||
},
|
||||
},
|
||||
notePage(appearNote) != location.pathname
|
||||
notePage(appearNote.value) != location.pathname
|
||||
? {
|
||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||
text: i18n.ts.showInPage,
|
||||
action: () => {
|
||||
router.push(notePage(appearNote), "forcePage");
|
||||
router.push(
|
||||
notePage(appearNote.value),
|
||||
"forcePage",
|
||||
);
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
|
@ -489,22 +494,25 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
type: "a",
|
||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||
text: i18n.ts.openInNewTab,
|
||||
href: notePage(appearNote),
|
||||
href: notePage(appearNote.value),
|
||||
target: "_blank",
|
||||
},
|
||||
{
|
||||
icon: "ph-link-simple ph-bold ph-lg",
|
||||
text: i18n.ts.copyLink,
|
||||
action: () => {
|
||||
copyToClipboard(`${url}${notePage(appearNote)}`);
|
||||
copyToClipboard(`${url}${notePage(appearNote.value)}`);
|
||||
},
|
||||
},
|
||||
appearNote.user.host != null
|
||||
appearNote.value.user.host != null
|
||||
? {
|
||||
type: "a",
|
||||
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
||||
text: i18n.ts.showOnRemote,
|
||||
href: appearNote.url ?? appearNote.uri ?? "",
|
||||
href:
|
||||
appearNote.value.url ??
|
||||
appearNote.value.uri ??
|
||||
"",
|
||||
target: "_blank",
|
||||
}
|
||||
: undefined,
|
||||
|
@ -517,7 +525,7 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
function menu(viaKeyboard = false): void {
|
||||
os.popupMenu(
|
||||
getNoteMenu({
|
||||
note: note,
|
||||
note: note.value,
|
||||
translating,
|
||||
translation,
|
||||
menuButton,
|
||||
|
@ -541,7 +549,7 @@ function showRenoteMenu(viaKeyboard = false): void {
|
|||
danger: true,
|
||||
action: () => {
|
||||
os.api("notes/delete", {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
|
@ -582,13 +590,13 @@ function noteClick(e) {
|
|||
) {
|
||||
e.stopPropagation();
|
||||
} else {
|
||||
router.push(notePage(appearNote));
|
||||
router.push(notePage(appearNote.value));
|
||||
}
|
||||
}
|
||||
|
||||
function readPromo() {
|
||||
os.api("promo/read", {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
}
|
||||
|
@ -600,28 +608,30 @@ function setPostExpanded(val: boolean) {
|
|||
}
|
||||
|
||||
const accessibleLabel = computed(() => {
|
||||
let label = `${appearNote.user.username}; `;
|
||||
if (appearNote.renote) {
|
||||
label += `${i18n.t("renoted")} ${appearNote.renote.user.username}; `;
|
||||
if (appearNote.renote.cw) {
|
||||
label += `${i18n.t("cw")}: ${appearNote.renote.cw}; `;
|
||||
let label = `${appearNote.value.user.username}; `;
|
||||
if (appearNote.value.renote) {
|
||||
label += `${i18n.t("renoted")} ${
|
||||
appearNote.value.renote.user.username
|
||||
}; `;
|
||||
if (appearNote.value.renote.cw) {
|
||||
label += `${i18n.t("cw")}: ${appearNote.value.renote.cw}; `;
|
||||
if (postIsExpanded.value) {
|
||||
label += `${appearNote.renote.text}; `;
|
||||
label += `${appearNote.value.renote.text}; `;
|
||||
}
|
||||
} else {
|
||||
label += `${appearNote.renote.text}; `;
|
||||
label += `${appearNote.value.renote.text}; `;
|
||||
}
|
||||
} else {
|
||||
if (appearNote.cw) {
|
||||
label += `${i18n.t("cw")}: ${appearNote.cw}; `;
|
||||
if (appearNote.value.cw) {
|
||||
label += `${i18n.t("cw")}: ${appearNote.value.cw}; `;
|
||||
if (postIsExpanded.value) {
|
||||
label += `${appearNote.text}; `;
|
||||
label += `${appearNote.value.text}; `;
|
||||
}
|
||||
} else {
|
||||
label += `${appearNote.text}; `;
|
||||
label += `${appearNote.value.text}; `;
|
||||
}
|
||||
}
|
||||
const date = new Date(appearNote.createdAt);
|
||||
const date = new Date(appearNote.value.createdAt);
|
||||
label += `${date.toLocaleTimeString()}`;
|
||||
return label;
|
||||
});
|
||||
|
|
|
@ -177,9 +177,9 @@ const props = defineProps<{
|
|||
pinned?: boolean;
|
||||
}>();
|
||||
|
||||
let tab = $ref("replies");
|
||||
let tab = ref("replies");
|
||||
|
||||
let note = $ref(deepClone(props.note));
|
||||
let note = ref(deepClone(props.note));
|
||||
|
||||
const softMuteReasonI18nSrc = (what?: string) => {
|
||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||
|
@ -194,30 +194,32 @@ const softMuteReasonI18nSrc = (what?: string) => {
|
|||
// plugin
|
||||
if (noteViewInterruptors.length > 0) {
|
||||
onMounted(async () => {
|
||||
let result = deepClone(note);
|
||||
let result = deepClone(note.value);
|
||||
for (const interruptor of noteViewInterruptors) {
|
||||
result = await interruptor.handler(result);
|
||||
}
|
||||
note = result;
|
||||
note.value = result;
|
||||
});
|
||||
}
|
||||
|
||||
const el = ref<HTMLElement>();
|
||||
const noteEl = $ref();
|
||||
const noteEl = ref();
|
||||
const menuButton = ref<HTMLElement>();
|
||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||
const reactButton = ref<HTMLElement>();
|
||||
const showContent = ref(false);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
|
||||
const muted = ref(
|
||||
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
|
||||
);
|
||||
const translation = ref(null);
|
||||
const translating = ref(false);
|
||||
let conversation = $ref<null | misskey.entities.Note[]>([]);
|
||||
let conversation = ref<null | misskey.entities.Note[]>([]);
|
||||
const replies = ref<misskey.entities.Note[]>([]);
|
||||
let directReplies = $ref<null | misskey.entities.Note[]>([]);
|
||||
let directQuotes = $ref<null | misskey.entities.Note[]>([]);
|
||||
let clips = $ref();
|
||||
let renotes = $ref();
|
||||
let directReplies = ref<null | misskey.entities.Note[]>([]);
|
||||
let directQuotes = ref<null | misskey.entities.Note[]>([]);
|
||||
let clips = ref();
|
||||
let renotes = ref();
|
||||
let isScrolling;
|
||||
|
||||
const reactionsCount = Object.values(props.note.reactions).reduce(
|
||||
|
@ -236,14 +238,14 @@ const keymap = {
|
|||
|
||||
useNoteCapture({
|
||||
rootEl: el,
|
||||
note: $$(note),
|
||||
note: note,
|
||||
isDeletedRef: isDeleted,
|
||||
});
|
||||
|
||||
function reply(viaKeyboard = false): void {
|
||||
pleaseLogin();
|
||||
os.post({
|
||||
reply: note,
|
||||
reply: note.value,
|
||||
animation: !viaKeyboard,
|
||||
}).then(() => {
|
||||
focus();
|
||||
|
@ -257,7 +259,7 @@ function react(viaKeyboard = false): void {
|
|||
reactButton.value,
|
||||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
},
|
||||
|
@ -291,7 +293,7 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
} else {
|
||||
os.contextMenu(
|
||||
getNoteMenu({
|
||||
note: note,
|
||||
note: note.value,
|
||||
translating,
|
||||
translation,
|
||||
menuButton,
|
||||
|
@ -305,7 +307,7 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
function menu(viaKeyboard = false): void {
|
||||
os.popupMenu(
|
||||
getNoteMenu({
|
||||
note: note,
|
||||
note: note.value,
|
||||
translating,
|
||||
translation,
|
||||
menuButton,
|
||||
|
@ -319,48 +321,50 @@ function menu(viaKeyboard = false): void {
|
|||
}
|
||||
|
||||
function focus() {
|
||||
noteEl.focus();
|
||||
noteEl.value.focus();
|
||||
}
|
||||
|
||||
function blur() {
|
||||
noteEl.blur();
|
||||
noteEl.value.blur();
|
||||
}
|
||||
|
||||
directReplies = null;
|
||||
directReplies.value = null;
|
||||
os.api("notes/children", {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
limit: 30,
|
||||
depth: 12,
|
||||
}).then((res) => {
|
||||
res = res.reduce((acc, resNote) => {
|
||||
if (resNote.userId == note.userId) {
|
||||
if (resNote.userId == note.value.userId) {
|
||||
return [...acc, resNote];
|
||||
}
|
||||
return [resNote, ...acc];
|
||||
}, []);
|
||||
replies.value = res;
|
||||
directReplies = res
|
||||
.filter((resNote) => resNote.replyId === note.id)
|
||||
directReplies.value = res
|
||||
.filter((resNote) => resNote.replyId === note.value.id)
|
||||
.reverse();
|
||||
directQuotes = res.filter((resNote) => resNote.renoteId === note.id);
|
||||
directQuotes.value = res.filter(
|
||||
(resNote) => resNote.renoteId === note.value.id,
|
||||
);
|
||||
});
|
||||
|
||||
conversation = null;
|
||||
if (note.replyId) {
|
||||
conversation.value = null;
|
||||
if (note.value.replyId) {
|
||||
os.api("notes/conversation", {
|
||||
noteId: note.replyId,
|
||||
noteId: note.value.replyId,
|
||||
limit: 30,
|
||||
}).then((res) => {
|
||||
conversation = res.reverse();
|
||||
conversation.value = res.reverse();
|
||||
focus();
|
||||
});
|
||||
}
|
||||
|
||||
clips = null;
|
||||
clips.value = null;
|
||||
os.api("notes/clips", {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
}).then((res) => {
|
||||
clips = res;
|
||||
clips.value = res;
|
||||
});
|
||||
|
||||
// const pagination = {
|
||||
|
@ -371,14 +375,14 @@ os.api("notes/clips", {
|
|||
|
||||
// const pagingComponent = $ref<InstanceType<typeof MkPagination>>();
|
||||
|
||||
renotes = null;
|
||||
renotes.value = null;
|
||||
function loadTab() {
|
||||
if (tab === "renotes" && !renotes) {
|
||||
if (tab.value === "renotes" && !renotes.value) {
|
||||
os.api("notes/renotes", {
|
||||
noteId: note.id,
|
||||
noteId: note.value.id,
|
||||
limit: 100,
|
||||
}).then((res) => {
|
||||
renotes = res;
|
||||
renotes.value = res;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +391,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
|
|||
const { type, id, body } = noteData;
|
||||
|
||||
let found = -1;
|
||||
if (id === note.id) {
|
||||
if (id === note.value.id) {
|
||||
found = 0;
|
||||
} else {
|
||||
for (let i = 0; i < replies.value.length; i++) {
|
||||
|
@ -412,7 +416,7 @@ async function onNoteUpdated(noteData: NoteUpdatedEvent): Promise<void> {
|
|||
|
||||
replies.value.splice(found, 0, replyNote);
|
||||
if (found === 0) {
|
||||
directReplies.push(replyNote);
|
||||
directReplies.value.push(replyNote);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -433,12 +437,12 @@ document.addEventListener("wheel", () => {
|
|||
onMounted(() => {
|
||||
stream.on("noteUpdated", onNoteUpdated);
|
||||
isScrolling = false;
|
||||
noteEl.scrollIntoView();
|
||||
noteEl.value.scrollIntoView();
|
||||
});
|
||||
|
||||
onUpdated(() => {
|
||||
if (!isScrolling) {
|
||||
noteEl.scrollIntoView();
|
||||
noteEl.value.scrollIntoView();
|
||||
if (location.hash) {
|
||||
location.replace(location.hash); // Jump to highlighted reply
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import {} from "vue";
|
||||
import type * as misskey from "firefish-js";
|
||||
import { defaultStore } from "@/store";
|
||||
|
@ -61,11 +63,12 @@ const props = defineProps<{
|
|||
pinned?: boolean;
|
||||
}>();
|
||||
|
||||
let note = $ref(props.note);
|
||||
let note = ref(props.note);
|
||||
|
||||
const showTicker =
|
||||
defaultStore.state.instanceTicker === "always" ||
|
||||
(defaultStore.state.instanceTicker === "remote" && note.user.instance);
|
||||
(defaultStore.state.instanceTicker === "remote" &&
|
||||
note.value.user.instance);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, ref } from "vue";
|
||||
import { inject, ref, computed } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import * as misskey from "firefish-js";
|
||||
import * as mfm from "mfm-js";
|
||||
|
@ -233,7 +233,7 @@ const props = withDefaults(
|
|||
},
|
||||
);
|
||||
|
||||
let note = $ref(deepClone(props.note));
|
||||
let note = ref(deepClone(props.note));
|
||||
|
||||
const softMuteReasonI18nSrc = (what?: string) => {
|
||||
if (what === "note") return i18n.ts.userSaysSomethingReason;
|
||||
|
@ -246,10 +246,10 @@ const softMuteReasonI18nSrc = (what?: string) => {
|
|||
};
|
||||
|
||||
const isRenote =
|
||||
note.renote != null &&
|
||||
note.text == null &&
|
||||
note.fileIds.length === 0 &&
|
||||
note.poll == null;
|
||||
note.value.renote != null &&
|
||||
note.value.text == null &&
|
||||
note.value.fileIds.length === 0 &&
|
||||
note.value.poll == null;
|
||||
|
||||
const el = ref<HTMLElement>();
|
||||
const footerEl = ref<HTMLElement>();
|
||||
|
@ -257,11 +257,13 @@ const menuButton = ref<HTMLElement>();
|
|||
const starButton = ref<InstanceType<typeof XStarButton>>();
|
||||
const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
|
||||
const reactButton = ref<HTMLElement>();
|
||||
let appearNote = $computed(() =>
|
||||
isRenote ? (note.renote as misskey.entities.Note) : note,
|
||||
let appearNote = computed(() =>
|
||||
isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
|
||||
);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(getWordSoftMute(note, $i, defaultStore.state.mutedWords));
|
||||
const muted = ref(
|
||||
getWordSoftMute(note.value, $i, defaultStore.state.mutedWords),
|
||||
);
|
||||
const translation = ref(null);
|
||||
const translating = ref(false);
|
||||
const replies: misskey.entities.Note[] =
|
||||
|
@ -309,14 +311,14 @@ const translate = async () => {
|
|||
|
||||
useNoteCapture({
|
||||
rootEl: el,
|
||||
note: $$(appearNote),
|
||||
note: appearNote,
|
||||
isDeletedRef: isDeleted,
|
||||
});
|
||||
|
||||
function reply(viaKeyboard = false): void {
|
||||
pleaseLogin();
|
||||
os.post({
|
||||
reply: appearNote,
|
||||
reply: appearNote.value,
|
||||
animation: !viaKeyboard,
|
||||
}).then(() => {
|
||||
focus();
|
||||
|
@ -330,7 +332,7 @@ function react(viaKeyboard = false): void {
|
|||
reactButton.value,
|
||||
(reaction) => {
|
||||
os.api("notes/reactions/create", {
|
||||
noteId: appearNote.id,
|
||||
noteId: appearNote.value.id,
|
||||
reaction: reaction,
|
||||
});
|
||||
},
|
||||
|
@ -356,7 +358,7 @@ const currentClipPage = inject<Ref<misskey.entities.Clip> | null>(
|
|||
function menu(viaKeyboard = false): void {
|
||||
os.popupMenu(
|
||||
getNoteMenu({
|
||||
note: note,
|
||||
note: note.value,
|
||||
translating,
|
||||
translation,
|
||||
menuButton,
|
||||
|
@ -388,21 +390,24 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
[
|
||||
{
|
||||
type: "label",
|
||||
text: notePage(appearNote),
|
||||
text: notePage(appearNote.value),
|
||||
},
|
||||
{
|
||||
icon: "ph-browser ph-bold ph-lg",
|
||||
text: i18n.ts.openInWindow,
|
||||
action: () => {
|
||||
os.pageWindow(notePage(appearNote));
|
||||
os.pageWindow(notePage(appearNote.value));
|
||||
},
|
||||
},
|
||||
notePage(appearNote) != location.pathname
|
||||
notePage(appearNote.value) != location.pathname
|
||||
? {
|
||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||
text: i18n.ts.showInPage,
|
||||
action: () => {
|
||||
router.push(notePage(appearNote), "forcePage");
|
||||
router.push(
|
||||
notePage(appearNote.value),
|
||||
"forcePage",
|
||||
);
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
|
@ -411,22 +416,22 @@ function onContextmenu(ev: MouseEvent): void {
|
|||
type: "a",
|
||||
icon: "ph-arrow-square-out ph-bold ph-lg",
|
||||
text: i18n.ts.openInNewTab,
|
||||
href: notePage(appearNote),
|
||||
href: notePage(appearNote.value),
|
||||
target: "_blank",
|
||||
},
|
||||
{
|
||||
icon: "ph-link-simple ph-bold ph-lg",
|
||||
text: i18n.ts.copyLink,
|
||||
action: () => {
|
||||
copyToClipboard(`${url}${notePage(appearNote)}`);
|
||||
copyToClipboard(`${url}${notePage(appearNote.value)}`);
|
||||
},
|
||||
},
|
||||
note.user.host != null
|
||||
note.value.user.host != null
|
||||
? {
|
||||
type: "a",
|
||||
icon: "ph-arrow-square-up-right ph-bold ph-lg",
|
||||
text: i18n.ts.showOnRemote,
|
||||
href: note.url ?? note.uri ?? "",
|
||||
href: note.value.url ?? note.value.uri ?? "",
|
||||
target: "_blank",
|
||||
}
|
||||
: undefined,
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
import {} from "vue";
|
||||
import { notificationTypes } from "firefish-js";
|
||||
import MkSwitch from "./form/switch.vue";
|
||||
|
@ -63,43 +65,45 @@ const props = withDefaults(
|
|||
},
|
||||
);
|
||||
|
||||
let includingTypes = $computed(() => props.includingTypes || []);
|
||||
let includingTypes = computed(() => props.includingTypes || []);
|
||||
|
||||
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||
const dialog = ref<InstanceType<typeof XModalWindow>>();
|
||||
|
||||
let typesMap = $ref<Record<(typeof notificationTypes)[number], boolean>>({});
|
||||
let useGlobalSetting = $ref(
|
||||
(includingTypes === null || includingTypes.length === 0) &&
|
||||
let typesMap = ref<Record<(typeof notificationTypes)[number], boolean>>({});
|
||||
let useGlobalSetting = ref(
|
||||
(includingTypes.value === null || includingTypes.value.length === 0) &&
|
||||
props.showGlobalToggle,
|
||||
);
|
||||
|
||||
for (const ntype of notificationTypes) {
|
||||
typesMap[ntype] = includingTypes.includes(ntype);
|
||||
typesMap.value[ntype] = includingTypes.value.includes(ntype);
|
||||
}
|
||||
|
||||
function ok() {
|
||||
if (useGlobalSetting) {
|
||||
if (useGlobalSetting.value) {
|
||||
emit("done", { includingTypes: null });
|
||||
} else {
|
||||
emit("done", {
|
||||
includingTypes: (
|
||||
Object.keys(typesMap) as (typeof notificationTypes)[number][]
|
||||
).filter((type) => typesMap[type]),
|
||||
Object.keys(
|
||||
typesMap.value,
|
||||
) as (typeof notificationTypes)[number][]
|
||||
).filter((type) => typesMap.value[type]),
|
||||
});
|
||||
}
|
||||
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
function disableAll() {
|
||||
for (const type in typesMap) {
|
||||
typesMap[type as (typeof notificationTypes)[number]] = false;
|
||||
for (const type in typesMap.value) {
|
||||
typesMap.value[type as (typeof notificationTypes)[number]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function enableAll() {
|
||||
for (const type in typesMap) {
|
||||
typesMap[type as (typeof notificationTypes)[number]] = true;
|
||||
for (const type in typesMap.value) {
|
||||
typesMap.value[type as (typeof notificationTypes)[number]] = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import XNotification from "@/components/MkNotification.vue";
|
||||
import * as os from "@/os";
|
||||
|
||||
|
@ -28,11 +28,11 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const zIndex = os.claimZIndex("high");
|
||||
let showing = $ref(true);
|
||||
let showing = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
window.setTimeout(() => {
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
}, 6000);
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ComputedRef, provide } from "vue";
|
||||
import { ComputedRef, provide, ref, computed } from "vue";
|
||||
import RouterView from "@/components/global/RouterView.vue";
|
||||
import XWindow from "@/components/MkWindow.vue";
|
||||
import { popout as _popout } from "@/scripts/popout";
|
||||
|
@ -51,18 +51,18 @@ defineEmits<{
|
|||
|
||||
const router = new Router(routes, props.initialPath);
|
||||
|
||||
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
|
||||
let windowEl = $ref<InstanceType<typeof XWindow>>();
|
||||
const history = $ref<{ path: string; key: any }[]>([
|
||||
let pageMetadata = ref<null | ComputedRef<PageMetadata>>();
|
||||
let windowEl = ref<InstanceType<typeof XWindow>>();
|
||||
const history = ref<{ path: string; key: any }[]>([
|
||||
{
|
||||
path: router.getCurrentPath(),
|
||||
key: router.getCurrentKey(),
|
||||
},
|
||||
]);
|
||||
const buttonsLeft = $computed(() => {
|
||||
const buttonsLeft = computed(() => {
|
||||
const buttons = [];
|
||||
|
||||
if (history.length > 1) {
|
||||
if (history.value.length > 1) {
|
||||
buttons.push({
|
||||
icon: "ph-caret-left ph-bold ph-lg",
|
||||
onClick: back,
|
||||
|
@ -71,7 +71,7 @@ const buttonsLeft = $computed(() => {
|
|||
|
||||
return buttons;
|
||||
});
|
||||
const buttonsRight = $computed(() => {
|
||||
const buttonsRight = computed(() => {
|
||||
const buttons = [
|
||||
{
|
||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||
|
@ -84,18 +84,18 @@ const buttonsRight = $computed(() => {
|
|||
});
|
||||
|
||||
router.addListener("push", (ctx) => {
|
||||
history.push({ path: ctx.path, key: ctx.key });
|
||||
history.value.push({ path: ctx.path, key: ctx.key });
|
||||
});
|
||||
|
||||
provide("router", router);
|
||||
provideMetadataReceiver((info) => {
|
||||
pageMetadata = info;
|
||||
pageMetadata.value = info;
|
||||
});
|
||||
provide("shouldOmitHeaderTitle", true);
|
||||
provide("shouldBackButton", false);
|
||||
provide("shouldHeaderThin", true);
|
||||
|
||||
const contextmenu = $computed(() => [
|
||||
const contextmenu = computed(() => [
|
||||
{
|
||||
icon: "ph-arrows-out-simple ph-bold ph-lg",
|
||||
text: i18n.ts.showInPage,
|
||||
|
@ -111,7 +111,7 @@ const contextmenu = $computed(() => [
|
|||
text: i18n.ts.openInNewTab,
|
||||
action: () => {
|
||||
window.open(url + router.getCurrentPath(), "_blank");
|
||||
windowEl.close();
|
||||
windowEl.value.close();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -124,25 +124,25 @@ const contextmenu = $computed(() => [
|
|||
]);
|
||||
|
||||
function back() {
|
||||
history.pop();
|
||||
history.value.pop();
|
||||
router.replace(
|
||||
history[history.length - 1].path,
|
||||
history[history.length - 1].key,
|
||||
history.value[history.value.length - 1].path,
|
||||
history.value[history.value.length - 1].key,
|
||||
);
|
||||
}
|
||||
|
||||
function close() {
|
||||
windowEl.close();
|
||||
windowEl.value.close();
|
||||
}
|
||||
|
||||
function expand() {
|
||||
mainRouter.push(router.getCurrentPath(), "forcePage");
|
||||
windowEl.close();
|
||||
windowEl.value.close();
|
||||
}
|
||||
|
||||
function popout() {
|
||||
_popout(router.getCurrentPath(), windowEl.$el);
|
||||
windowEl.close();
|
||||
_popout(router.getCurrentPath(), windowEl.value.$el);
|
||||
windowEl.value.close();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import MkModal from "./MkModal.vue";
|
||||
import MkMenu from "./MkMenu.vue";
|
||||
import { MenuItem } from "@/types/menu";
|
||||
|
@ -41,7 +43,7 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
let modal = $ref<InstanceType<typeof MkModal>>();
|
||||
let modal = ref<InstanceType<typeof MkModal>>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -245,7 +245,15 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent } from "vue";
|
||||
import {
|
||||
inject,
|
||||
watch,
|
||||
nextTick,
|
||||
onMounted,
|
||||
defineAsyncComponent,
|
||||
ref,
|
||||
computed,
|
||||
} from "vue";
|
||||
import * as mfm from "mfm-js";
|
||||
import * as misskey from "firefish-js";
|
||||
import autosize from "autosize";
|
||||
|
@ -311,45 +319,47 @@ const emit = defineEmits<{
|
|||
(ev: "esc"): void;
|
||||
}>();
|
||||
|
||||
const textareaEl = $ref<HTMLTextAreaElement | null>(null);
|
||||
const cwInputEl = $ref<HTMLInputElement | null>(null);
|
||||
const hashtagsInputEl = $ref<HTMLInputElement | null>(null);
|
||||
const visibilityButton = $ref<HTMLElement | null>(null);
|
||||
const textareaEl = ref<HTMLTextAreaElement | null>(null);
|
||||
const cwInputEl = ref<HTMLInputElement | null>(null);
|
||||
const hashtagsInputEl = ref<HTMLInputElement | null>(null);
|
||||
const visibilityButton = ref<HTMLElement | null>(null);
|
||||
|
||||
const showBigPostButton = defaultStore.state.showBigPostButton;
|
||||
|
||||
let posting = $ref(false);
|
||||
let text = $ref(props.initialText ?? "");
|
||||
let files = $ref(props.initialFiles ?? []);
|
||||
let poll = $ref<{
|
||||
let posting = ref(false);
|
||||
let text = ref(props.initialText ?? "");
|
||||
let files = ref(props.initialFiles ?? []);
|
||||
let poll = ref<{
|
||||
choices: string[];
|
||||
multiple: boolean;
|
||||
expiresAt: string | null;
|
||||
expiredAfter: string | null;
|
||||
} | null>(null);
|
||||
let useCw = $ref(false);
|
||||
let showPreview = $ref(defaultStore.state.showPreviewByDefault);
|
||||
let cw = $ref<string | null>(null);
|
||||
let localOnly = $ref<boolean>(
|
||||
let useCw = ref(false);
|
||||
let showPreview = ref(defaultStore.state.showPreviewByDefault);
|
||||
let cw = ref<string | null>(null);
|
||||
let localOnly = ref<boolean>(
|
||||
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
|
||||
? defaultStore.state.localOnly
|
||||
: defaultStore.state.defaultNoteLocalOnly,
|
||||
);
|
||||
let visibility = $ref(
|
||||
let visibility = ref(
|
||||
props.initialVisibility ??
|
||||
((defaultStore.state.rememberNoteVisibility
|
||||
? defaultStore.state.visibility
|
||||
: defaultStore.state
|
||||
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
|
||||
);
|
||||
let visibleUsers = $ref([]);
|
||||
let visibleUsers = ref([]);
|
||||
if (props.initialVisibleUsers) {
|
||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||
}
|
||||
let quoteId = $ref(null);
|
||||
let hasNotSpecifiedMentions = $ref(false);
|
||||
let recentHashtags = $ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
|
||||
let imeText = $ref("");
|
||||
let autocomplete = ref(null);
|
||||
let draghover = ref(false);
|
||||
let quoteId = ref(null);
|
||||
let hasNotSpecifiedMentions = ref(false);
|
||||
let recentHashtags = ref(JSON.parse(localStorage.getItem("hashtags") || "[]"));
|
||||
let imeText = ref("");
|
||||
|
||||
const typing = throttle(3000, () => {
|
||||
if (props.channel) {
|
||||
|
@ -357,7 +367,7 @@ const typing = throttle(3000, () => {
|
|||
}
|
||||
});
|
||||
|
||||
const draftKey = $computed((): string => {
|
||||
const draftKey = computed((): string => {
|
||||
if (props.editId) {
|
||||
return `edit:${props.editId}`;
|
||||
}
|
||||
|
@ -375,7 +385,7 @@ const draftKey = $computed((): string => {
|
|||
return key;
|
||||
});
|
||||
|
||||
const placeholder = $computed((): string => {
|
||||
const placeholder = computed((): string => {
|
||||
if (props.renote) {
|
||||
return i18n.ts._postForm.quotePlaceholder;
|
||||
} else if (props.reply) {
|
||||
|
@ -395,7 +405,7 @@ const placeholder = $computed((): string => {
|
|||
}
|
||||
});
|
||||
|
||||
const submitText = $computed((): string => {
|
||||
const submitText = computed((): string => {
|
||||
return props.editId
|
||||
? i18n.ts.edit
|
||||
: props.renote
|
||||
|
@ -405,34 +415,37 @@ const submitText = $computed((): string => {
|
|||
: i18n.ts.note;
|
||||
});
|
||||
|
||||
const textLength = $computed((): number => {
|
||||
return length((preprocess(text) + imeText).trim());
|
||||
const textLength = computed((): number => {
|
||||
return length((preprocess(text.value) + imeText.value).trim());
|
||||
});
|
||||
|
||||
const maxTextLength = $computed((): number => {
|
||||
const maxTextLength = computed((): number => {
|
||||
return instance ? instance.maxNoteTextLength : 3000;
|
||||
});
|
||||
|
||||
const canPost = $computed((): boolean => {
|
||||
const canPost = computed((): boolean => {
|
||||
return (
|
||||
!posting &&
|
||||
(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
|
||||
textLength <= maxTextLength &&
|
||||
(!poll || poll.choices.length >= 2)
|
||||
!posting.value &&
|
||||
(1 <= textLength.value ||
|
||||
1 <= files.value.length ||
|
||||
!!poll.value ||
|
||||
!!props.renote) &&
|
||||
textLength.value <= maxTextLength.value &&
|
||||
(!poll.value || poll.value.choices.length >= 2)
|
||||
);
|
||||
});
|
||||
|
||||
const withHashtags = $computed(
|
||||
const withHashtags = computed(
|
||||
defaultStore.makeGetterSetter("postFormWithHashtags"),
|
||||
);
|
||||
const hashtags = $computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
||||
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags"));
|
||||
|
||||
watch($$(text), () => {
|
||||
watch(text, () => {
|
||||
checkMissingMention();
|
||||
});
|
||||
|
||||
watch(
|
||||
$$(visibleUsers),
|
||||
visibleUsers,
|
||||
() => {
|
||||
checkMissingMention();
|
||||
},
|
||||
|
@ -442,10 +455,10 @@ watch(
|
|||
);
|
||||
|
||||
if (props.mention) {
|
||||
text = props.mention.host
|
||||
text.value = props.mention.host
|
||||
? `@${props.mention.username}@${toASCII(props.mention.host)}`
|
||||
: `@${props.mention.username}`;
|
||||
text += " ";
|
||||
text.value += " ";
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -453,7 +466,7 @@ if (
|
|||
(props.reply.user.username !== $i.username ||
|
||||
(props.reply.user.host != null && props.reply.user.host !== host))
|
||||
) {
|
||||
text = `@${props.reply.user.username}${
|
||||
text.value = `@${props.reply.user.username}${
|
||||
props.reply.user.host != null
|
||||
? "@" + toASCII(props.reply.user.host)
|
||||
: ""
|
||||
|
@ -476,15 +489,15 @@ if (props.reply && props.reply.text != null) {
|
|||
continue;
|
||||
|
||||
// 重複は除外
|
||||
if (text.includes(`${mention} `)) continue;
|
||||
if (text.value.includes(`${mention} `)) continue;
|
||||
|
||||
text += `${mention} `;
|
||||
text.value += `${mention} `;
|
||||
}
|
||||
}
|
||||
|
||||
if (props.channel) {
|
||||
visibility = "public";
|
||||
localOnly = true; // TODO: チャンネルが連合するようになった折には消す
|
||||
visibility.value = "public";
|
||||
localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
|
||||
}
|
||||
|
||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||
|
@ -492,17 +505,17 @@ if (
|
|||
props.reply &&
|
||||
["home", "followers", "specified"].includes(props.reply.visibility)
|
||||
) {
|
||||
if (props.reply.visibility === "home" && visibility === "followers") {
|
||||
visibility = "followers";
|
||||
if (props.reply.visibility === "home" && visibility.value === "followers") {
|
||||
visibility.value = "followers";
|
||||
} else if (
|
||||
["home", "followers"].includes(props.reply.visibility) &&
|
||||
visibility === "specified"
|
||||
visibility.value === "specified"
|
||||
) {
|
||||
visibility = "specified";
|
||||
visibility.value = "specified";
|
||||
} else {
|
||||
visibility = props.reply.visibility;
|
||||
visibility.value = props.reply.visibility;
|
||||
}
|
||||
if (visibility === "specified") {
|
||||
if (visibility.value === "specified") {
|
||||
if (props.reply.visibleUserIds) {
|
||||
os.api("users/show", {
|
||||
userIds: props.reply.visibleUserIds.filter(
|
||||
|
@ -524,7 +537,7 @@ if (
|
|||
}
|
||||
|
||||
if (props.specified) {
|
||||
visibility = "specified";
|
||||
visibility.value = "specified";
|
||||
pushVisibleUser(props.specified);
|
||||
}
|
||||
|
||||
|
@ -540,53 +553,53 @@ const addRe = (s: string) => {
|
|||
|
||||
// keep cw when reply
|
||||
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
|
||||
useCw = true;
|
||||
cw =
|
||||
useCw.value = true;
|
||||
cw.value =
|
||||
props.reply.user.username === $i.username
|
||||
? props.reply.cw
|
||||
: addRe(props.reply.cw);
|
||||
}
|
||||
|
||||
function watchForDraft() {
|
||||
watch($$(text), () => saveDraft());
|
||||
watch($$(useCw), () => saveDraft());
|
||||
watch($$(cw), () => saveDraft());
|
||||
watch($$(poll), () => saveDraft());
|
||||
watch($$(files), () => saveDraft(), { deep: true });
|
||||
watch($$(visibility), () => saveDraft());
|
||||
watch($$(localOnly), () => saveDraft());
|
||||
watch(text, () => saveDraft());
|
||||
watch(useCw, () => saveDraft());
|
||||
watch(cw, () => saveDraft());
|
||||
watch(poll, () => saveDraft());
|
||||
watch(files, () => saveDraft(), { deep: true });
|
||||
watch(visibility, () => saveDraft());
|
||||
watch(localOnly, () => saveDraft());
|
||||
}
|
||||
|
||||
function checkMissingMention() {
|
||||
if (visibility === "specified") {
|
||||
const ast = mfm.parse(text);
|
||||
if (visibility.value === "specified") {
|
||||
const ast = mfm.parse(text.value);
|
||||
|
||||
for (const x of extractMentions(ast)) {
|
||||
if (
|
||||
!visibleUsers.some(
|
||||
!visibleUsers.value.some(
|
||||
(u) => u.username === x.username && u.host === x.host,
|
||||
)
|
||||
) {
|
||||
hasNotSpecifiedMentions = true;
|
||||
hasNotSpecifiedMentions.value = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
hasNotSpecifiedMentions = false;
|
||||
hasNotSpecifiedMentions.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function addMissingMention() {
|
||||
const ast = mfm.parse(text);
|
||||
const ast = mfm.parse(text.value);
|
||||
|
||||
for (const x of extractMentions(ast)) {
|
||||
if (
|
||||
!visibleUsers.some(
|
||||
!visibleUsers.value.some(
|
||||
(u) => u.username === x.username && u.host === x.host,
|
||||
)
|
||||
) {
|
||||
os.api("users/show", { username: x.username, host: x.host }).then(
|
||||
(user) => {
|
||||
visibleUsers.push(user);
|
||||
visibleUsers.value.push(user);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -594,10 +607,10 @@ function addMissingMention() {
|
|||
}
|
||||
|
||||
function togglePoll() {
|
||||
if (poll) {
|
||||
poll = null;
|
||||
if (poll.value) {
|
||||
poll.value = null;
|
||||
} else {
|
||||
poll = {
|
||||
poll.value = {
|
||||
choices: ["", ""],
|
||||
multiple: false,
|
||||
expiresAt: null,
|
||||
|
@ -606,16 +619,16 @@ function togglePoll() {
|
|||
}
|
||||
}
|
||||
|
||||
// function addTag(tag: string) {
|
||||
// insertTextAtCursor(textareaEl, ` #${tag} `);
|
||||
// }
|
||||
function addTag(tag: string) {
|
||||
insertTextAtCursor(textareaEl.value, ` #${tag} `);
|
||||
}
|
||||
|
||||
function focus() {
|
||||
if (textareaEl) {
|
||||
textareaEl.focus();
|
||||
textareaEl.setSelectionRange(
|
||||
textareaEl.value.length,
|
||||
textareaEl.value.length,
|
||||
if (textareaEl.value) {
|
||||
textareaEl.value.focus();
|
||||
textareaEl.value.setSelectionRange(
|
||||
textareaEl.value.value.length,
|
||||
textareaEl.value.value.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -624,31 +637,32 @@ function chooseFileFrom(ev) {
|
|||
selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(
|
||||
(files_) => {
|
||||
for (const file of files_) {
|
||||
files.push(file);
|
||||
files.value.push(file);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function detachFile(id) {
|
||||
files = files.filter((x) => x.id !== id);
|
||||
files.value = files.value.filter((x) => x.id !== id);
|
||||
}
|
||||
|
||||
function updateFiles(_files) {
|
||||
files = _files;
|
||||
files.value = _files;
|
||||
}
|
||||
|
||||
function updateFileSensitive(file, sensitive) {
|
||||
files[files.findIndex((x) => x.id === file.id)].isSensitive = sensitive;
|
||||
files.value[files.value.findIndex((x) => x.id === file.id)].isSensitive =
|
||||
sensitive;
|
||||
}
|
||||
|
||||
function updateFileName(file, name) {
|
||||
files[files.findIndex((x) => x.id === file.id)].name = name;
|
||||
files.value[files.value.findIndex((x) => x.id === file.id)].name = name;
|
||||
}
|
||||
|
||||
function upload(file: File, name?: string) {
|
||||
uploadFile(file, defaultStore.state.uploadFolder, name).then((res) => {
|
||||
files.push(res);
|
||||
files.value.push(res);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -663,21 +677,21 @@ function setVisibility() {
|
|||
() => import("@/components/MkVisibilityPicker.vue"),
|
||||
),
|
||||
{
|
||||
currentVisibility: visibility,
|
||||
currentLocalOnly: localOnly,
|
||||
src: visibilityButton,
|
||||
currentVisibility: visibility.value,
|
||||
currentLocalOnly: localOnly.value,
|
||||
src: visibilityButton.value,
|
||||
},
|
||||
{
|
||||
changeVisibility: (v) => {
|
||||
visibility = v;
|
||||
visibility.value = v;
|
||||
if (defaultStore.state.rememberNoteVisibility) {
|
||||
defaultStore.set("visibility", visibility);
|
||||
defaultStore.set("visibility", visibility.value);
|
||||
}
|
||||
},
|
||||
changeLocalOnly: (v) => {
|
||||
localOnly = v;
|
||||
localOnly.value = v;
|
||||
if (defaultStore.state.rememberNoteVisibility) {
|
||||
defaultStore.set("localOnly", localOnly);
|
||||
defaultStore.set("localOnly", localOnly.value);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -687,11 +701,11 @@ function setVisibility() {
|
|||
|
||||
function pushVisibleUser(user) {
|
||||
if (
|
||||
!visibleUsers.some(
|
||||
!visibleUsers.value.some(
|
||||
(u) => u.username === user.username && u.host === user.host,
|
||||
)
|
||||
) {
|
||||
visibleUsers.push(user);
|
||||
visibleUsers.value.push(user);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,21 +716,21 @@ function addVisibleUser() {
|
|||
}
|
||||
|
||||
function removeVisibleUser(user) {
|
||||
visibleUsers = erase(user, visibleUsers);
|
||||
visibleUsers.value = erase(user, visibleUsers.value);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
text = "";
|
||||
files = [];
|
||||
poll = null;
|
||||
quoteId = null;
|
||||
text.value = "";
|
||||
files.value = [];
|
||||
poll.value = null;
|
||||
quoteId.value = null;
|
||||
}
|
||||
|
||||
function onKeydown(ev: KeyboardEvent) {
|
||||
if (
|
||||
(ev.which === 10 || ev.which === 13) &&
|
||||
(ev.ctrlKey || ev.metaKey) &&
|
||||
canPost
|
||||
canPost.value
|
||||
)
|
||||
post();
|
||||
if (ev.which === 27) emit("esc");
|
||||
|
@ -724,12 +738,12 @@ function onKeydown(ev: KeyboardEvent) {
|
|||
}
|
||||
|
||||
function onCompositionUpdate(ev: CompositionEvent) {
|
||||
imeText = ev.data;
|
||||
imeText.value = ev.data;
|
||||
typing();
|
||||
}
|
||||
|
||||
function onCompositionEnd(ev: CompositionEvent) {
|
||||
imeText = "";
|
||||
imeText.value = "";
|
||||
}
|
||||
|
||||
async function onPaste(ev: ClipboardEvent) {
|
||||
|
@ -750,7 +764,7 @@ async function onPaste(ev: ClipboardEvent) {
|
|||
|
||||
const paste = ev.clipboardData.getData("text");
|
||||
|
||||
if (!props.renote && !quoteId && paste.startsWith(url + "/notes/")) {
|
||||
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) {
|
||||
ev.preventDefault();
|
||||
|
||||
os.yesno({
|
||||
|
@ -758,11 +772,13 @@ async function onPaste(ev: ClipboardEvent) {
|
|||
text: i18n.ts.quoteQuestion,
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
insertTextAtCursor(textareaEl, paste);
|
||||
insertTextAtCursor(textareaEl.value, paste);
|
||||
return;
|
||||
}
|
||||
|
||||
quoteId = paste.substr(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
|
||||
quoteId.value = paste
|
||||
.substr(url.length)
|
||||
.match(/^\/notes\/(.+?)\/?$/)[1];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -773,7 +789,7 @@ function onDragover(ev) {
|
|||
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
||||
if (isFile || isDriveFile) {
|
||||
ev.preventDefault();
|
||||
draghover = true;
|
||||
draghover.value = true;
|
||||
switch (ev.dataTransfer.effectAllowed) {
|
||||
case "all":
|
||||
case "uninitialized":
|
||||
|
@ -794,15 +810,15 @@ function onDragover(ev) {
|
|||
}
|
||||
|
||||
function onDragenter(ev) {
|
||||
draghover = true;
|
||||
draghover.value = true;
|
||||
}
|
||||
|
||||
function onDragleave(ev) {
|
||||
draghover = false;
|
||||
draghover.value = false;
|
||||
}
|
||||
|
||||
function onDrop(ev): void {
|
||||
draghover = false;
|
||||
draghover.value = false;
|
||||
|
||||
// ファイルだったら
|
||||
if (ev.dataTransfer.files.length > 0) {
|
||||
|
@ -815,7 +831,7 @@ function onDrop(ev): void {
|
|||
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
||||
if (driveFile != null && driveFile !== "") {
|
||||
const file = JSON.parse(driveFile);
|
||||
files.push(file);
|
||||
files.value.push(file);
|
||||
ev.preventDefault();
|
||||
}
|
||||
//#endregion
|
||||
|
@ -824,16 +840,16 @@ function onDrop(ev): void {
|
|||
function saveDraft() {
|
||||
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
||||
|
||||
draftData[draftKey] = {
|
||||
draftData[draftKey.value] = {
|
||||
updatedAt: new Date(),
|
||||
data: {
|
||||
text: text,
|
||||
useCw: useCw,
|
||||
cw: cw,
|
||||
visibility: visibility,
|
||||
localOnly: localOnly,
|
||||
files: files,
|
||||
poll: poll,
|
||||
text: text.value,
|
||||
useCw: useCw.value,
|
||||
cw: cw.value,
|
||||
visibility: visibility.value,
|
||||
localOnly: localOnly.value,
|
||||
files: files.value,
|
||||
poll: poll.value,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -843,37 +859,38 @@ function saveDraft() {
|
|||
function deleteDraft() {
|
||||
const draftData = JSON.parse(localStorage.getItem("drafts") || "{}");
|
||||
|
||||
delete draftData[draftKey];
|
||||
delete draftData[draftKey.value];
|
||||
|
||||
localStorage.setItem("drafts", JSON.stringify(draftData));
|
||||
}
|
||||
|
||||
async function post() {
|
||||
const processedText = preprocess(text);
|
||||
const processedText = preprocess(text.value);
|
||||
|
||||
let postData = {
|
||||
editId: props.editId ? props.editId : undefined,
|
||||
text: processedText === "" ? undefined : processedText,
|
||||
fileIds: files.length > 0 ? files.map((f) => f.id) : undefined,
|
||||
fileIds:
|
||||
files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
|
||||
replyId: props.reply ? props.reply.id : undefined,
|
||||
renoteId: props.renote
|
||||
? props.renote.id
|
||||
: quoteId
|
||||
? quoteId
|
||||
: quoteId.value
|
||||
? quoteId.value
|
||||
: undefined,
|
||||
channelId: props.channel ? props.channel.id : undefined,
|
||||
poll: poll,
|
||||
cw: useCw ? cw || "" : undefined,
|
||||
localOnly: localOnly,
|
||||
visibility: visibility,
|
||||
poll: poll.value,
|
||||
cw: useCw.value ? cw.value || "" : undefined,
|
||||
localOnly: localOnly.value,
|
||||
visibility: visibility.value,
|
||||
visibleUserIds:
|
||||
visibility === "specified"
|
||||
? visibleUsers.map((u) => u.id)
|
||||
visibility.value === "specified"
|
||||
? visibleUsers.value.map((u) => u.id)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
if (withHashtags && hashtags && hashtags.trim() !== "") {
|
||||
const hashtags_ = hashtags
|
||||
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== "") {
|
||||
const hashtags_ = hashtags.value
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map((x) => (x.startsWith("#") ? x : "#" + x))
|
||||
|
@ -892,12 +909,13 @@ async function post() {
|
|||
|
||||
let token = undefined;
|
||||
|
||||
if (postAccount) {
|
||||
if (postAccount.value) {
|
||||
const storedAccounts = await getAccounts();
|
||||
token = storedAccounts.find((x) => x.id === postAccount.id)?.token;
|
||||
token = storedAccounts.find((x) => x.id === postAccount.value.id)
|
||||
?.token;
|
||||
}
|
||||
|
||||
posting = true;
|
||||
posting.value = true;
|
||||
os.api(postData.editId ? "notes/edit" : "notes/create", postData, token)
|
||||
.then(() => {
|
||||
clear();
|
||||
|
@ -917,12 +935,12 @@ async function post() {
|
|||
JSON.stringify(unique(hashtags_.concat(history))),
|
||||
);
|
||||
}
|
||||
posting = false;
|
||||
postAccount = null;
|
||||
posting.value = false;
|
||||
postAccount.value = null;
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
posting = false;
|
||||
posting.value = false;
|
||||
os.alert({
|
||||
type: "error",
|
||||
text: err.message + "\n" + (err as any).id,
|
||||
|
@ -936,12 +954,12 @@ function cancel() {
|
|||
|
||||
function insertMention() {
|
||||
os.selectUser().then((user) => {
|
||||
insertTextAtCursor(textareaEl, "@" + Acct.toString(user) + " ");
|
||||
insertTextAtCursor(textareaEl.value, "@" + Acct.toString(user) + " ");
|
||||
});
|
||||
}
|
||||
|
||||
async function insertEmoji(ev: MouseEvent) {
|
||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
|
||||
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value);
|
||||
}
|
||||
|
||||
function showActions(ev) {
|
||||
|
@ -951,11 +969,11 @@ function showActions(ev) {
|
|||
action: () => {
|
||||
action.handler(
|
||||
{
|
||||
text: text,
|
||||
text: text.value,
|
||||
},
|
||||
(key, value) => {
|
||||
if (key === "text") {
|
||||
text = value;
|
||||
text.value = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -965,19 +983,19 @@ function showActions(ev) {
|
|||
);
|
||||
}
|
||||
|
||||
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
|
||||
let postAccount = ref<misskey.entities.UserDetailed | null>(null);
|
||||
|
||||
function openAccountMenu(ev: MouseEvent) {
|
||||
openAccountMenu_(
|
||||
{
|
||||
withExtraOperation: false,
|
||||
includeCurrentAccount: true,
|
||||
active: postAccount != null ? postAccount.id : $i.id,
|
||||
active: postAccount.value != null ? postAccount.value.id : $i.id,
|
||||
onChoose: (account) => {
|
||||
if (account.id === $i.id) {
|
||||
postAccount = null;
|
||||
postAccount.value = null;
|
||||
} else {
|
||||
postAccount = account;
|
||||
postAccount.value = account;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -995,30 +1013,30 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
// TODO: detach when unmount
|
||||
new Autocomplete(textareaEl, $$(text));
|
||||
new Autocomplete(cwInputEl, $$(cw));
|
||||
new Autocomplete(hashtagsInputEl, $$(hashtags));
|
||||
new Autocomplete(textareaEl.value, text);
|
||||
new Autocomplete(cwInputEl.value, cw);
|
||||
new Autocomplete(hashtagsInputEl.value, hashtags);
|
||||
|
||||
autosize(textareaEl);
|
||||
autosize(textareaEl.value);
|
||||
|
||||
nextTick(() => {
|
||||
autosize(textareaEl);
|
||||
autosize(textareaEl.value);
|
||||
// 書きかけの投稿を復元
|
||||
if (!props.instant && !props.mention && !props.specified) {
|
||||
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
|
||||
draftKey
|
||||
draftKey.value
|
||||
];
|
||||
if (draft) {
|
||||
text = draft.data.text;
|
||||
useCw = draft.data.useCw;
|
||||
cw = draft.data.cw;
|
||||
visibility = draft.data.visibility;
|
||||
localOnly = draft.data.localOnly;
|
||||
files = (draft.data.files || []).filter(
|
||||
text.value = draft.data.text;
|
||||
useCw.value = draft.data.useCw;
|
||||
cw.value = draft.data.cw;
|
||||
visibility.value = draft.data.visibility;
|
||||
localOnly.value = draft.data.localOnly;
|
||||
files.value = (draft.data.files || []).filter(
|
||||
(draftFile) => draftFile,
|
||||
);
|
||||
if (draft.data.poll) {
|
||||
poll = draft.data.poll;
|
||||
poll.value = draft.data.poll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1026,21 +1044,21 @@ onMounted(() => {
|
|||
// 削除して編集
|
||||
if (props.initialNote) {
|
||||
const init = props.initialNote;
|
||||
text = init.text ? init.text : "";
|
||||
files = init.files;
|
||||
cw = init.cw;
|
||||
useCw = init.cw != null;
|
||||
text.value = init.text ? init.text : "";
|
||||
files.value = init.files;
|
||||
cw.value = init.cw;
|
||||
useCw.value = init.cw != null;
|
||||
if (init.poll) {
|
||||
poll = {
|
||||
poll.value = {
|
||||
choices: init.poll.choices.map((x) => x.text),
|
||||
multiple: init.poll.multiple,
|
||||
expiresAt: init.poll.expiresAt,
|
||||
expiredAfter: init.poll.expiredAfter,
|
||||
};
|
||||
}
|
||||
visibility = init.visibility;
|
||||
localOnly = init.localOnly;
|
||||
quoteId = init.renote ? init.renote.id : null;
|
||||
visibility.value = init.visibility;
|
||||
localOnly.value = init.localOnly;
|
||||
quoteId.value = init.renote ? init.renote.id : null;
|
||||
}
|
||||
|
||||
nextTick(() => watchForDraft());
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from "vue";
|
||||
|
||||
import {} from "vue";
|
||||
import * as misskey from "firefish-js";
|
||||
import MkModal from "@/components/MkModal.vue";
|
||||
|
@ -46,11 +48,11 @@ const emit = defineEmits<{
|
|||
(ev: "closed"): void;
|
||||
}>();
|
||||
|
||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||
let form = $shallowRef<InstanceType<typeof MkPostForm>>();
|
||||
let modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
let form = shallowRef<InstanceType<typeof MkPostForm>>();
|
||||
|
||||
function onPosted() {
|
||||
modal.close({
|
||||
modal.value.close({
|
||||
useSendAnimation: true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
import { $i, getAccounts } from "@/account";
|
||||
import MkButton from "@/components/MkButton.vue";
|
||||
import { instance } from "@/instance";
|
||||
|
@ -74,12 +76,12 @@ defineProps<{
|
|||
}>();
|
||||
|
||||
// ServiceWorker registration
|
||||
let registration = $ref<ServiceWorkerRegistration | undefined>();
|
||||
let registration = ref<ServiceWorkerRegistration | undefined>();
|
||||
// If this browser supports push notification
|
||||
let supported = $ref(false);
|
||||
let supported = ref(false);
|
||||
// If this browser has already subscribed to push notification
|
||||
let pushSubscription = $ref<PushSubscription | null>(null);
|
||||
let pushRegistrationInServer = $ref<
|
||||
let pushSubscription = ref<PushSubscription | null>(null);
|
||||
let pushRegistrationInServer = ref<
|
||||
| {
|
||||
state?: string;
|
||||
key?: string;
|
||||
|
@ -91,11 +93,12 @@ let pushRegistrationInServer = $ref<
|
|||
>();
|
||||
|
||||
function subscribe() {
|
||||
if (!registration || !supported || !instance.swPublickey) return;
|
||||
if (!registration.value || !supported.value || !instance.swPublickey)
|
||||
return;
|
||||
|
||||
// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
|
||||
return promiseDialog(
|
||||
registration.pushManager
|
||||
registration.value.pushManager
|
||||
.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: urlBase64ToUint8Array(
|
||||
|
@ -104,10 +107,10 @@ function subscribe() {
|
|||
})
|
||||
.then(
|
||||
async (subscription) => {
|
||||
pushSubscription = subscription;
|
||||
pushSubscription.value = subscription;
|
||||
|
||||
// Register
|
||||
pushRegistrationInServer = await api("sw/register", {
|
||||
pushRegistrationInServer.value = await api("sw/register", {
|
||||
endpoint: subscription.endpoint,
|
||||
auth: encode(subscription.getKey("auth")),
|
||||
publickey: encode(subscription.getKey("p256dh")),
|
||||
|
@ -136,12 +139,12 @@ function subscribe() {
|
|||
}
|
||||
|
||||
async function unsubscribe() {
|
||||
if (!pushSubscription) return;
|
||||
if (!pushSubscription.value) return;
|
||||
|
||||
const endpoint = pushSubscription.endpoint;
|
||||
const endpoint = pushSubscription.value.endpoint;
|
||||
const accounts = await getAccounts();
|
||||
|
||||
pushRegistrationInServer = undefined;
|
||||
pushRegistrationInServer.value = undefined;
|
||||
|
||||
if ($i && accounts.length >= 2) {
|
||||
apiWithDialog("sw/unregister", {
|
||||
|
@ -149,11 +152,11 @@ async function unsubscribe() {
|
|||
endpoint,
|
||||
});
|
||||
} else {
|
||||
pushSubscription.unsubscribe();
|
||||
pushSubscription.value.unsubscribe();
|
||||
apiWithDialog("sw/unregister", {
|
||||
endpoint,
|
||||
});
|
||||
pushSubscription = null;
|
||||
pushSubscription.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,20 +187,21 @@ if (navigator.serviceWorker == null) {
|
|||
// TODO: よしなに?
|
||||
} else {
|
||||
navigator.serviceWorker.ready.then(async (swr) => {
|
||||
registration = swr;
|
||||
registration.value = swr;
|
||||
|
||||
pushSubscription = await registration.pushManager.getSubscription();
|
||||
pushSubscription.value =
|
||||
await registration.value.pushManager.getSubscription();
|
||||
|
||||
if (instance.swPublickey && "PushManager" in window && $i && $i.token) {
|
||||
supported = true;
|
||||
supported.value = true;
|
||||
|
||||
if (pushSubscription) {
|
||||
if (pushSubscription.value) {
|
||||
const res = await api("sw/show-registration", {
|
||||
endpoint: pushSubscription.endpoint,
|
||||
endpoint: pushSubscription.value.endpoint,
|
||||
});
|
||||
|
||||
if (res) {
|
||||
pushRegistrationInServer = res;
|
||||
pushRegistrationInServer.value = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +209,6 @@ if (navigator.serviceWorker == null) {
|
|||
}
|
||||
|
||||
defineExpose({
|
||||
pushRegistrationInServer: $$(pushRegistrationInServer),
|
||||
pushRegistrationInServer: pushRegistrationInServer,
|
||||
});
|
||||
</script>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue