Merge branch 'develop' into firefish-docs/develop

This commit is contained in:
naskya 2024-03-17 02:32:11 +09:00
commit 05b74e0dc9
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
168 changed files with 1363 additions and 1159 deletions

View file

@ -48,7 +48,7 @@ FROM docker.io/node:20-slim
WORKDIR /firefish
# Install runtime dependencies
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get install -y --no-install-recommends zip unzip tini ffmpeg ca-certificates
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get install -y --no-install-recommends zip unzip tini ffmpeg ca-certificates curl
RUN echo 'deb https://deb.debian.org/debian experimental main' | tee /etc/apt/sources.list
RUN apt-get update && DEBIAN_FRONTEND='noninteractive' apt-get --target-release experimental install -y --no-install-recommends libc6

View file

@ -6,8 +6,10 @@ services:
container_name: firefish_web
restart: unless-stopped
depends_on:
- db
- redis
db:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
networks:
@ -19,6 +21,15 @@ services:
- ./custom:/firefish/custom:ro
- ./files:/firefish/files
- ./.config:/firefish/.config:ro
healthcheck:
test: curl -f http://localhost:3000 || exit 1
interval: 5s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 4096M
redis:
restart: unless-stopped
@ -28,6 +39,16 @@ services:
- calcnet
volumes:
- ./redis:/data
healthcheck:
test: redis-cli ping
interval: 5s
timeout: 5s
retries: 5
# deploy:
# resources:
# limits:
# memory: 200M
db:
restart: unless-stopped
@ -39,6 +60,15 @@ services:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
healthcheck:
test: pg_isready --user="$${POSTGRES_USER}" --dbname="$${POSTGRES_DB}"
interval: 5s
timeout: 5s
retries: 5
# deploy:
# resources:
# limits:
# memory: 200M
networks:
calcnet:

View file

@ -4,6 +4,7 @@ Breaking changes are indicated by the :warning: icon.
## Unreleased
- :warning: `followingCount` and `followersCount` in `users/show` will be `null` (instead of 0) if these values are unavailable.
- :warning: `admin/search/index-all` is removed since posts are now indexed automatically.
- New optional parameters are added to `notes/search` endpoint:
- `sinceDate`

View file

@ -323,8 +323,8 @@ _2fa:
securityKeyInfo: A més de l'autenticació d'empremta digital o PIN, també podeu configurar
l'autenticació mitjançant claus de seguretat de maquinari compatibles amb FIDO2
per protegir encara més el vostre compte.
step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquest
token d'inici de sessió.
step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquesta
clau d'inici de sessió.
registerSecurityKey: Registrar una clau de seguretat o d'accés
step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b})
al dispositiu.
@ -2067,9 +2067,8 @@ _relayStatus:
deleted: Eliminat
editNote: Edita la publicació
edited: 'Editat el {date} {time}'
signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades,
però sempre podeu registrar-vos en un altre servidor. Si teniu un codi d'invitació
per a aquest servidor, introduïu-lo a continuació.
signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades.
Si teniu un codi d'invitació per a aquest servidor, introduïu-lo a continuació.
userSaysSomethingReasonQuote: '{name} ha citat una publicació que conté {reason}'
userSaysSomethingReasonReply: '{name} ha respost a una publicació que conté {reason}'
userSaysSomethingReasonRenote: '{name} ha impulsat una publicació que conté {reason}'
@ -2253,7 +2252,34 @@ searchWordsDescription: "Per cercar publicacions, escriu el terme a buscar. Sepa
o la ID en aquest camp i fes clic al botó 'Trobar'. Fent clic a 'Cercar' trobarà
publicacions que, literalment , continguin la ID/adreça URL."
searchPostsWithFiles: Només publicacions amb fitxers
searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions.
searchCwAndAlt: Inclou avisos de contingut i arxius amb descripcions
searchUsers: Publicat per (opcional)
searchRange: Publicat dintre de (opcional)
publishTimelines: Publica línies de temps per visitants
toPost: Publicà
publishTimelinesDescription: Si està activat, les línies de temps Global i Local es
mostraran a {url} fins i tot sense estar registrat.
noAltTextWarning: Alguns fitxers adjunts no tenen una descripció. T'has s oblidat
d'escriure-les?
showNoAltTextWarning: Mostra un avís si públiques un fitxer sense descripció
toReply: Respon
toQuote: Cita
toEdit: Edita
searchUsersDescription: "Per buscar publicacions concretes d'un usuari/servidor, escriu
la ID (@usuari@exemple.com, o @usuari per un usuari local) o nom del domini (exemple.com).\n
\nSi escrius 'me' (sense cometes), totes les teves publicacions (incloent-hi publicacions
sense llistar, només per a seguidors i secretes) es buscaran.\n\nSi escrius 'local'
(sense cometes), el resultat serà filtrat per mostrar només publicacions d'aquest
servidor."
messagingUnencryptedInfo: Els xats a Firefish no són encriptats d'extrem a extrem.
No comparteixis dades sensibles fent servir Firefish.
searchRangeDescription: "Si vols filtrar per un període de temps, has de fer servir
aquest format: 20220615-20231031\n\nSi no escrius l'any (per exemple 0105-0106 o
20231105-0110), serà interpretat com l'any en curs.\n\nInclús pots morir la data
de començament o de finalització. Per exemple, -0102 filtrarà els resultats per
mostrar només publicacions fetes abans del 2 de gener d'aquest any, i 20231026-
filtrarà els resultats per mostrar publicacions fetes després del 26 d'octubre del
2023."
moderationNote: Nota de moderació
ipFirstAcknowledged: Data en què es va veure la adreça IP per primera vegada
driveCapacityOverride: Capacitat del disc esgotada

View file

@ -2218,3 +2218,4 @@ renotes: Boosts
quotes: Zitate
moreUrlsDescription: "Die Seiten, welche angepinnt werde sollen, im Hilfe-Menü in
der unteren linken Ecke in folgender Notation angeben:\n\"Anzeigename\": https://example.com/"
toQuote: Zitat

View file

@ -462,7 +462,7 @@ securityKeyName: "Key name"
registerSecurityKey: "Register a security key"
lastUsed: "Last used"
unregister: "Unregister"
passwordLessLogin: "Password-less login"
passwordLessLogin: "Password-less sign in"
resetPassword: "Reset password"
newPasswordIs: "The new password is \"{password}\""
reduceUiAnimation: "Reduce UI animations"
@ -527,7 +527,7 @@ disableDrawer: "Don't use drawer-style menus"
youHaveNoGroups: "You have no groups"
joinOrCreateGroup: "Get invited to a group or create your own."
noHistory: "No history available"
signinHistory: "Login history"
signinHistory: "Sign in history"
disableAnimatedMfm: "Disable MFM with animation"
doing: "Processing..."
category: "Category"
@ -717,9 +717,9 @@ useGlobalSetting: "Use global settings"
useGlobalSettingDesc: "If turned on, your account's notification settings will be
used. If turned off, individual configurations can be made."
other: "Other"
regenerateLoginToken: "Regenerate login token"
regenerateLoginTokenDescription: "Regenerates the token used internally during login.
Normally this action is not necessary. If regenerated, all devices will be logged
regenerateLoginToken: "Regenerate sign in token"
regenerateLoginTokenDescription: "Regenerates the token used internally during sign
in. Normally this action is not necessary. If regenerated, all devices will be logged
out."
setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces."
fileIdOrUrl: "File ID or URL"
@ -1001,7 +1001,7 @@ check: "Check"
driveCapOverrideLabel: "Change the drive capacity for this user"
driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0
or lower."
requireAdminForView: "You must log in with an administrator account to view this."
requireAdminForView: "You must sign in with an administrator account to view this."
isSystemAccount: "This account is created and automatically operated by the system.
Please do not moderate, edit, delete, or otherwise tamper with this account, or
it may break your server."
@ -1011,7 +1011,7 @@ document: "Documentation"
numberOfPageCache: "Number of cached pages"
numberOfPageCacheDescription: "Increasing this number will improve convenience for
users but cause more server load as well as more memory to be used."
logoutConfirm: "Really log out?"
logoutConfirm: "Really sign out?"
lastActiveDate: "Last used at"
statusbar: "Status bar"
pleaseSelect: "Select an option"
@ -1112,6 +1112,9 @@ signupsDisabled: "Signups on this server are currently disabled. If you have an
code for this server, please enter it below."
apps: "Apps"
sendModMail: "Send Moderation Notice"
moderationNote: "Moderation Note"
ipFirstAcknowledged: "The date of the first acquisition of the IP address"
driveCapacityOverride: "Drive Capacity Override"
preventAiLearning: "Prevent AI bot scraping"
preventAiLearningDescription: "Request third-party AI language models not to study
content you upload, such as posts and images."
@ -1214,7 +1217,7 @@ searchRangeDescription: "If you want to filter the time period, enter it in this
searchPostsWithFiles: "Only posts with files"
searchCwAndAlt: "Include content warnings and file descriptions"
publishTimelines: "Publish timelines for visitors"
publishTimelinesDescription: "If enabled, the Local and Global timeline will be shown
publishTimelinesDescription: "If enabled, the Local and Global timelines will be shown
on {url} even when signed out."
noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?"
showNoAltTextWarning: "Show a warning if you attempt to post files without a description"
@ -1627,7 +1630,7 @@ _2fa:
step2Url: "You can also enter this URL if you're using a desktop program:"
step3Title: "Enter an authentication code"
step3: "Enter the token provided by your app to finish setup."
step4: "From now on, any future login attempts will ask for such a login token."
step4: "From now on, any future sign in attempts will ask for such a token."
securityKeyNotSupported: "Your browser does not support security keys."
securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup
authentication via hardware security keys that support FIDO2 to further secure
@ -2220,4 +2223,5 @@ _iconSets:
moreUrls: "Pinned pages"
moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower
left corner using this notation:\n\"Display name\": https://example.com/"
messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't share any sensitive infomation over Firefish."
messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't share
any sensitive infomation over Firefish."

View file

@ -666,7 +666,7 @@ useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votr
other: "Autre"
regenerateLoginToken: "Régénérer le jeton de connexion"
regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette
opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton,
opération ne devrait pas être nécessaire; lors de la génération d'un nouveau jeton,
tous les appareils seront déconnectés."
setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant
par des espaces."
@ -2038,7 +2038,7 @@ noInstances: Il n'y a aucun serveur
showLocalPosts: 'Montrer les notes locales dans :'
homeTimeline: Timeline d'Accueil
socialTimeline: Timeline Sociale
requireAdminForView: Vous avez besoin d'un compte d'administration pour voir cela.
requireAdminForView: Vous avez besoin d'un compte d'administration pour voir ceci.
isSystemAccount: Ce compte est créé et géré automatiquement par le système. Veuillez
ne pas modérer, éditer, supprimer ou altérer d'une autre manière ce compte, ou cela
risque de perturber votre serveur.
@ -2091,9 +2091,8 @@ _experiments:
peut entraîner des ralentissements lors du chargement si votre file d'attente
est congestionnée.
userSaysSomethingReasonQuote: '{name} a cité une publication contenant {reason}'
signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés, mais
vous pouvez toujours vous inscrire sur un autre serveur ! Si vous avez un code d'invitation
pour ce serveur, entrez-le ci-dessous s'il vous plait.
signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés. Si
vous avez un code d'invitation pour ce serveur, saisissez-le ci-dessous.
apps: Applications
userSaysSomethingReasonReply: '{noms} a répondu à une publication contenant {raison}'
defaultValueIs: 'défaut: {valeur}'
@ -2300,3 +2299,16 @@ searchRangeDescription: "Si vous voulez filtrer par période de temps, saisissez
résultats de recherche pour afficher seulement les messages effectués avant le 2
janvier de cette année, et 20231026- filtrera les résultats pour afficher seulement
les messages effectués après le 26 octobre 2023."
toReply: Répondre
toPost: Publier
toQuote: Citer
toEdit: Modifier
messagingUnencryptedInfo: Les conversations sur Firefish ne sont pas cryptées. Ne
partagez aucune information sensible sur Firefish.
moderationNote: Note de modération
driveCapacityOverride: Limite de stockage personalisée
ipFirstAcknowledged: La date de la première acquisition de l'adresse IP
noAltTextWarning: Certains fichiers joints n'ont aucune description. Avez-vous oublié
de l'écrire?
showNoAltTextWarning: Afficher un avertissement si vous essayez de publier des fichiers
sans description

View file

@ -415,7 +415,7 @@ securityKeyName: "Nama kunci"
registerSecurityKey: "Daftarkan kunci keamanan"
lastUsed: "Terakhir digunakan"
unregister: "Batalkan pendaftaran"
passwordLessLogin: "Setel login tanpa kata sandi"
passwordLessLogin: "Masuk tanpa kata sandi"
resetPassword: "Atur ulang kata sandi"
newPasswordIs: "Kata sandi baru adalah \"{password}\""
reduceUiAnimation: "Kurangi animasi antarmuka"
@ -655,10 +655,10 @@ useGlobalSetting: "Gunakan setelan global"
useGlobalSettingDesc: "Jika dinyalakan, setelan pemberitahuan akun kamu akan digunakan.
Jika dimatikan, konfigurasi secara individu dapat dibuat."
other: "Lainnya"
regenerateLoginToken: "Perbarui token login"
regenerateLoginToken: "Perbarui token masuk"
regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat
login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan
dilogout."
masuk ke akun. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat
akan dikeluarkan dari akun."
setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya
menggunakan spasi."
fileIdOrUrl: "File-ID atau URL"
@ -1296,8 +1296,8 @@ _2fa:
step2Url: "Di aplikasi desktop, masukkan URL berikut:"
step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan
pemasangan."
step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi
otentikasi kamu."
step4: "Mulai sekarang, upaya pemasukan akun apa pun akan meminta token masuk dari
aplikasi autentikasi kamu."
securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses
login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung
FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu."
@ -1988,9 +1988,8 @@ moveAccountDescription: Proses ini permanen. Pastikan kamu sudah mengatur alias
akun ini ke akun barumu sebelum pindah. Silakan masukkan tag akun dengan format
seperti @orang@server.com
sendModMail: Kirim Pemberitahuan Moderasi
signupsDisabled: Pendaftaran ke server ini nonaktif, tapi kamu dapat selalu mendaftar
ke server lain! Jika kamu memiliki kode undangan server ini, harap masukkan di bawah
ini.
signupsDisabled: Pendaftaran ke server ini nonaktifkam. Jika kamu memiliki kode undangan
server ini, harap masukkan di bawah ini.
enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus
isBot: Akun ini akun otomatis
customMOTD: MOTD khusus (pesan layar percik)
@ -2185,7 +2184,7 @@ emojiModPerm: Perizinan pengelolaan emoji kustom
emojiModPermDescription: "Tambah: Perbolehkan pengguna ini untuk menambahkan emoji
kustom baru dan menetapkan tag/kategori/lisensi untuk semua emoji kustom yang telah
ditambahkan.\nTambah dan Sunting: Perizinan \"Tambah\" + Perbolehkan pengguna ini
untuk menyunting nama/kategori/tag/lisensi emoji kustom yang sudah ada.\nPerbolehkan
untuk menyunting nama/kategori/tag/lisensi emoji kustom yang sudah ada.\n Perbolehkan
Semua: Perizinan \"Tambah dan Sunting\" + Perbolehkan pengguna ini untuk menghapus
semua emoji kustom yang sudah ada."
private: Privat
@ -2241,7 +2240,7 @@ reloading: Memuat ulang
replyMute: Bisukan balasan dalam lini masa
searchRange: Dikirim dalam (opsional)
searchUsersDescription: "Untuk mencari kiriman oleh pengguna/server tertentu, masukkan
ID (@pengguna@contoh.id, atau @pengguna untuk pengguna lokal) atau nama domain (contoh.id).\n
ID (@pengguna@contoh.id, atau @pengguna untuk pengguna lokal) atau nama domain (contoh.id)\n
\nJika kamu memasukkan 'me' ('aku', tanpa tanda kutip), semua kirimanmu (termasuk
kiriman yang tidak terdaftar, khusus pengikut, langsung, dan rahasia) akan dicari.\n
\nJika Anda memasukkan 'local' (tanpa tanda kutip), hasilnya akan disaring untuk
@ -2253,3 +2252,16 @@ searchRangeDescription: "Jika kamu ingin memfilter periode waktu, masukkan dalam
pencarian untuk menampilkan hanya kiriman yang dibuat sebelum tanggal 2 Januari
tahun ini, dan 20231026- akan memfilter hasil pencarian untuk menampilkan hanya
kiriman yang dibuat setelah tanggal 26 Oktober 2023."
toPost: Kirim
toQuote: Kutip
noAltTextWarning: Beberapa berkas yang dilampirkan tidak memiliki deskripsi. Lupa
menulis deskripsinya?
toEdit: Sunting
showNoAltTextWarning: Tampilkan peringatan jika kamu mencoba mengirim berkas tanpa
deskripsi
toReply: Balas
messagingUnencryptedInfo: Percakapan di Firefish tidak terenkripsi secara ujung ke
ujung. Jangan bagikan informasi sensitif apa pun melalui Firefish.
moderationNote: Catatan Moderasi
driveCapacityOverride: Penimpaan Kapasitas Drive
ipFirstAcknowledged: Tanggal akuisisi pertama dari alamat IP

View file

@ -107,7 +107,7 @@ cantRenote: "この投稿はブーストできません。"
cantReRenote: "ブーストをブーストすることはできません。"
quote: "引用"
quotes: "引用"
toQuote: "引用"
toQuote: "引用する"
pinnedNote: "ピン留めされた投稿"
pinned: "ピン留め"
you: "あなた"
@ -416,7 +416,7 @@ securityKeyName: "キーの名前"
registerSecurityKey: "セキュリティキーを登録する"
lastUsed: "最後の使用"
unregister: "登録を解除"
passwordLessLogin: "パスワード無しでログイン"
passwordLessLogin: "パスワード無しでサインイン"
resetPassword: "パスワードをリセット"
newPasswordIs: "新しいパスワードは「{password}」です"
reduceUiAnimation: "UIのアニメーションを減らす"
@ -481,7 +481,7 @@ disableDrawer: "メニューをドロワーで表示しない"
youHaveNoGroups: "グループがありません"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
noHistory: "履歴はありません"
signinHistory: "ログイン履歴"
signinHistory: "サインイン履歴"
disableAnimatedMfm: "動きのあるMFMを無効にする"
doing: "やっています"
category: "カテゴリ"
@ -648,8 +648,8 @@ notificationSettingDesc: "表示する通知の種別を選択してください
useGlobalSetting: "グローバル設定を使う"
useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使用されます。オフにすると、個別に設定できるようになります。"
other: "その他"
regenerateLoginToken: "ログイントークンを再生成"
regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。"
regenerateLoginToken: "サインイントークンを再生成"
regenerateLoginTokenDescription: "サインインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスからサインアウトされます。"
setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
fileIdOrUrl: "ファイルIDまたはURL"
behavior: "動作"
@ -908,14 +908,14 @@ thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
check: "チェック"
driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更"
driveCapOverrideCaption: "0以下を指定すると解除されます。"
requireAdminForView: "閲覧するには管理者アカウントでログインしている必要があります。"
requireAdminForView: "閲覧するには管理者アカウントでサインインしている必要があります。"
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。モデレーション・編集・削除を行うとサーバーの動作が不正になる可能性があるため、操作しないでください。"
typeToConfirm: "この操作を行うには {x} と入力してください"
deleteAccount: "アカウント削除"
document: "ドキュメント"
numberOfPageCache: "ページキャッシュ数"
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
logoutConfirm: "ログアウトしますか?"
logoutConfirm: "サインアウトしますか?"
lastActiveDate: "最終利用日時"
statusbar: "ステータスバー"
pleaseSelect: "選択してください"
@ -2051,3 +2051,7 @@ makePrivate: "秘密にする"
makePrivateConfirm: "リモートサーバーに削除リクエストを送信し、投稿の公開範囲を「秘密」にして他の人から見られないようにします。実行しますか?"
sentFollowRequests: 未承認のフォローリクエスト
noSentFollowRequests: 未承認のフォローリクエストはありません
messagingUnencryptedInfo: FirefishのチャットはE2E暗号化されていません。漏洩してはいけない情報はFirefishで送らないでください。
moderationNote: モデレーション用のメモ
ipFirstAcknowledged: IPアドレスが最初に取得された日
driveCapacityOverride: ドライブ容量の変更

View file

@ -1,7 +1,9 @@
---
_lang_: "Română"
headlineFirefish: "O rețea conectată prin note"
introFirefish: "Bine ai venit! Firefish este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀"
introFirefish: "Bine ai venit! Firefish este un serviciu de microblogging open source
și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine
din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui.
👍\nHai să explorăm o lume nouă! 🚀"
monthAndDay: "{day}/{month}"
search: "Caută"
notifications: "Notificări"
@ -44,7 +46,8 @@ copyContent: "Copiază conținutul"
copyLink: "Copiază link-ul"
delete: "Şterge"
deleteAndEdit: "Șterge și editează"
deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi? Vei pierde reacțiile, re-notele și răspunsurile acesteia."
deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi?
Vei pierde reacțiile, re-notele și răspunsurile acesteia."
addToList: "Adaugă în listă"
sendMessage: "Trimite un mesaj"
copyUsername: "Copiază numele de utilizator"
@ -64,9 +67,11 @@ import: "Importă"
export: "Exportă"
files: "Fișiere"
download: "Descarcă"
driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele atașate fișierului vor fi șterse și ele."
driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele
atașate fișierului vor fi șterse și ele."
unfollowConfirm: "Ești sigur ca vrei să nu mai urmărești pe {name}?"
exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul tău odată completat."
exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul
tău odată completat."
importRequested: "Ai cerut un import. S-ar putea să ia un pic."
lists: "Liste"
noLists: "Nu ai nici o listă"
@ -81,9 +86,12 @@ error: "Eroare"
somethingHappened: "A survenit o eroare"
retry: "Reîncearcă"
pageLoadError: "A apărut o eroare la încărcarea paginii."
pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci din nou."
youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi clientul."
pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul
browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci
din nou."
youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi
clientul."
enterListName: "Introdu un nume pentru listă"
privacy: "Confidenţialitate"
makeFollowManuallyApprove: "Fă cererile de urmărire să necesite aprobare"
@ -137,14 +145,21 @@ emojiUrl: "URL-ul emoji-ului"
addEmoji: "Adaugă un emoji"
settingGuide: "Setări recomandate"
cacheRemoteFiles: "Ține fișierele externe in cache"
cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe
sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului
de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
flagAsBot: "Marchează acest cont ca bot"
flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Firefish pentru a trata acest cont drept un bot."
flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de
un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori
pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează
sistemele interne al Firefish pentru a trata acest cont drept un bot."
flagAsCat: "Marchează acest cont ca pisică"
flagAsCatDescription: "Activează această opțiune dacă acest cont este o pisică."
flagShowTimelineReplies: "Arată răspunsurile în cronologie"
flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie răspunsurile utilizatorilor către alte notele altor utilizatori."
autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care îi urmărești"
flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie
răspunsurile utilizatorilor către alte notele altor utilizatori."
autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care
îi urmărești"
addAccount: "Adaugă un cont"
loginFailed: "Autentificare eșuată"
showOnRemote: "Vezi mai multe pe instanța externă"
@ -156,7 +171,11 @@ searchWith: "Caută: {q}"
youHaveNoLists: "Nu ai nici o listă"
followConfirm: "Ești sigur ca vrei să urmărești pe {name}?"
proxyAccount: "Cont proxy"
proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator, așa că în schimb contul proxy îl va urmări."
proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor
extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva
adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va
fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator,
așa că în schimb contul proxy îl va urmări."
host: "Gazdă"
selectUser: "Selectează un utilizator"
recipient: "Destinatar"
@ -186,11 +205,14 @@ instanceInfo: "Informații despre instanță"
statistics: "Statistici"
clearQueue: "Șterge coada"
clearQueueConfirmTitle: "Ești sigur că vrei să cureți coada?"
clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această operație nu este necesară."
clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această
operație nu este necesară."
clearCachedFiles: "Golește cache-ul"
clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din cache?"
clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din
cache?"
blockedInstances: "Instanțe blocate"
blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să
le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
muteAndBlock: "Amuțiri și Blocări"
mutedUsers: "Utilizatori amuțiți"
blockedUsers: "Utilizatori blocați"
@ -238,7 +260,8 @@ saved: "Salvat"
messaging: "Chat"
upload: "Încarcă"
keepOriginalUploading: "Păstrează imaginea originală"
keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări. Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări.
Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
fromDrive: "Din Drive"
fromUrl: "Din URL"
uploadFromUrl: "Încarcă dintr-un URL"
@ -254,7 +277,8 @@ agreeTo: "Sunt de acord cu {0}"
tos: "Termenii de utilizare"
start: "Să începem"
home: "Acasă"
remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația afișată poate fi incompletă."
remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația
afișată poate fi incompletă."
activity: "Activitate"
images: "Imagini"
birthday: "Zi de naștere"
@ -287,7 +311,8 @@ unableToDelete: "Nu se poate șterge"
inputNewFileName: "Introdu un nou nume de fișier"
inputNewDescription: "Introdu o descriere nouă"
inputNewFolderName: "Introdu un nume de folder nou"
circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe care dorești să îl muți."
circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe
care dorești să îl muți."
hasChildFilesOrFolders: "Acest folder nu este gol, așa că nu poate fi șters."
copyUrl: "Copiază URL"
rename: "Redenumește"
@ -318,7 +343,8 @@ yearX: "{year}"
pages: "Pagini"
enableLocalTimeline: "Activează cronologia locală"
enableGlobalTimeline: "Activeaza cronologia globală"
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate cronologiile, chiar dacă nu sunt activate."
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate
cronologiile, chiar dacă nu sunt activate."
registration: "Inregistrare"
enableRegistration: "Activează înregistrările pentru utilizatori noi"
invite: "Invită"
@ -330,9 +356,11 @@ bannerUrl: "URL-ul imaginii de banner"
backgroundImageUrl: "URL-ul imaginii de fundal"
basicInfo: "Informații de bază"
pinnedUsers: "Utilizatori fixați"
pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor fi fixați pe pagina \"Explorează\"."
pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor
fi fixați pe pagina \"Explorează\"."
pinnedPages: "Pagini fixate"
pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful paginii acestei instanțe, separate de pauze de rând."
pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful
paginii acestei instanțe, separate de pauze de rând."
pinnedClipId: "ID-ul clip-ului pe care să îl fixezi"
pinnedNotes: "Notă fixată"
hcaptcha: "hCaptcha"
@ -343,14 +371,17 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Activează reCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi să rămână activate, apasă Anulare."
avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență
între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi
să rămână activate, apasă Anulare."
antennas: "Antene"
manageAntennas: "Gestionează Antenele"
name: "Nume"
antennaSource: "Sursa antenei"
antennaKeywords: "Cuvinte cheie ascultate"
antennaExcludeKeywords: "Cuvinte cheie excluse"
antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere de rând pentru o condiție SAU."
antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere
de rând pentru o condiție SAU."
notifyAntenna: "Notifică-mă pentru note noi"
withFileAntenna: "Doar note cu fișiere"
enableServiceworker: "Activează ServiceWorker"
@ -437,7 +468,8 @@ strongPassword: "Parolă puternică"
passwordMatched: "Se potrivește!"
passwordNotMatched: "Nu se potrivește"
signinWith: "Autentifică-te cu {x}"
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse sunt incorecte."
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse
sunt incorecte."
tapSecurityKey: "Apasă pe cheia ta de securitate."
or: "Sau"
language: "Limbă"
@ -478,19 +510,26 @@ showFeaturedNotesInTimeline: "Arată notele recomandate în cronologii"
objectStorage: "Object Storage"
useObjectStorage: "Folosește Object Storage"
objectStorageBaseUrl: "URL de bază"
objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com' și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>', etc."
objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul
CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com'
și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>',
etc."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică
endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
objectStorageRegion: "Regiune"
objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău
nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
objectStorageUseSSL: "Folosește SSl"
objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru conexiunile API-ului"
objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru
conexiunile API-ului"
objectStorageUseProxy: "Conectează-te prin Proxy"
objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru conexiunile API-ului"
objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru
conexiunile API-ului"
objectStorageSetPublicRead: "Setează \"public-read\" pentru încărcare"
serverLogs: "Loguri server"
deleteAll: "Șterge tot"
@ -518,7 +557,9 @@ sort: "Sortează"
ascendingOrder: "Crescător"
descendingOrder: "Descrescător"
scratchpad: "Scratchpad"
scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript. Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish în el."
scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript.
Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish
în el."
output: "Ieșire"
script: "Script"
disablePagesScript: "Dezactivează AiScript în Pagini"
@ -526,11 +567,14 @@ updateRemoteUser: "Actualizează informațiile utilizatorului extern"
deleteAllFiles: "Șterge toate fișierele"
deleteAllFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele?"
removeAllFollowing: "Dezurmărește toți utilizatorii urmăriți"
removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te rog execută asta numai dacă instanța, de ex., nu mai există."
removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te
rog execută asta numai dacă instanța, de ex., nu mai există."
userSuspended: "Acest utilizator a fost suspendat."
userSilenced: "Acest utilizator a fost setat silențios."
yourAccountSuspendedTitle: "Acest cont a fost suspendat"
yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării termenilor de serviciu al serverului sau ceva similar. Contactează administratorul dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării
termenilor de serviciu al serverului sau ceva similar. Contactează administratorul
dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
menu: "Meniu"
divider: "Separator"
addItem: "Adaugă element"
@ -569,12 +613,14 @@ permission: "Permisiuni"
enableAll: "Actevează tot"
disableAll: "Dezactivează tot"
tokenRequested: "Acordă acces la cont"
pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile setate aici."
pluginTokenRequestedDescription: "Acest plugin va putea să folosească permisiunile
setate aici."
notificationType: "Tipul notificării"
edit: "Editează"
emailServer: "Server email"
enableEmail: "Activează distribuția de emailuri"
emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți uiți parola"
emailConfigInfo: "Folosit pentru a confirma emailul tău în timpul logări dacă îți
uiți parola"
email: "Email"
emailAddress: "Adresă de email"
smtpConfig: "Configurare Server SMTP"
@ -582,13 +628,15 @@ smtpHost: "Gazdă"
smtpPort: "Port"
smtpUser: "Nume de utilizator"
smtpPass: "Parolă"
emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva verificarea SMTP"
emptyToDisableSmtpAuth: "Lasă username-ul și parola necompletate pentru a dezactiva
verificarea SMTP"
smtpSecure: "Folosește SSL/TLS implicit pentru conecțiunile SMTP"
smtpSecureInfo: "Oprește opțiunea asta dacă STARTTLS este folosit"
testEmail: "Testează livrarea emailurilor"
wordMute: "Cuvinte pe mut"
regexpError: "Eroare de Expresie Regulată"
regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al cuvintelor {tab} setate pe mut:"
regexpErrorDescription: "A apărut o eroare în expresia regulată pe linia {line} al
cuvintelor {tab} setate pe mut:"
instanceMute: "Instanțe pe mut"
userSaysSomething: "{name} a spus ceva"
makeActive: "Activează"
@ -604,10 +652,13 @@ create: "Crează"
notificationSetting: "Setări notificări"
notificationSettingDesc: "Selectează tipurile de notificări care să fie arătate"
useGlobalSetting: "Folosește setările globale"
useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite. Dacă e oprită, configurația va fi individuală."
useGlobalSettingDesc: "Dacă opțiunea e pornită, notificările contului tău vor fi folosite.
Dacă e oprită, configurația va fi individuală."
other: "Altele"
regenerateLoginToken: "Regenerează token de login"
regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări. În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi delogate."
regenerateLoginTokenDescription: "Regenerează token-ul folosit intern în timpul logări.
În mod normal asta nu este necesar. Odată regenerat, toate dispozitivele vor fi
delogate."
setMultipleBySeparatingWithSpace: "Separă mai multe intrări cu spații."
fileIdOrUrl: "Introdu ID sau URL"
behavior: "Comportament"
@ -615,13 +666,15 @@ sample: "exemplu"
abuseReports: "Rapoarte"
reportAbuse: "Raportează"
reportAbuseOf: "Raportează {name}"
fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este despre o notă specifică, te rog introdu URL-ul ei."
fillAbuseReportDescription: "Te rog scrie detaliile legate de acest raport. Dacă este
despre o notă specifică, te rog introdu URL-ul ei."
abuseReported: "Raportul tău a fost trimis. Mulțumim."
reporter: "Raportorul"
reporteeOrigin: "Originea raportatului"
reporterOrigin: "Originea raportorului"
forwardReport: "Redirecționează raportul către instanța externă"
forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de sistem, ca raportor către instanța externă."
forwardReportIsAnonymous: "În locul contului tău, va fi afișat un cont anonim, de
sistem, ca raportor către instanța externă."
send: "Trimite"
abuseMarkAsResolved: "Marchează raportul ca rezolvat"
openInNewTab: "Deschide în tab nou"

View file

@ -873,7 +873,7 @@ recommended: "推荐"
check: "检查"
driveCapOverrideLabel: "修改此用户的网盘容量"
driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。"
requireAdminForView: "需要使用管理员账号登录才能查看。"
requireAdminForView: "需要使用管理员账号登录才能查看。"
isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。"
typeToConfirm: "输入 {x} 以确认操作"
deleteAccount: "删除账号"
@ -1263,7 +1263,7 @@ _2fa:
step2: "然后,扫描屏幕上显示的二维码。"
step2Url: "如果您使用的是桌面程序您也可以输入这个URL"
step3: "输入您的应用提供的令牌以完成设置。"
step4: "从现在开始,任何登录操作都将要求您提供这样一个登录令牌。"
step4: "从现在开始,任何登录操作都将要求您提供这样一个令牌。"
securityKeyInfo: "除了指纹或 PIN 身份验证外,您还可以通过支持 FIDO2 的硬件安全密钥设置身份验证,以进一步保护您的账号。"
token: 2FA 令牌
step3Title: 输入验证码
@ -1934,7 +1934,10 @@ migrationConfirm: "您确实确定要将账号迁移到 {account} 吗?此操
noteId: 帖子 ID
moveFrom: 从旧账号迁移至此账号
defaultReaction: 发出和收到帖子的默认表情符号反应
sendModMail: 发送审核通知
sendModMail: 发送管理通知
moderationNote: "管理笔记"
ipFirstAcknowledged: "该日期是这个 IP 地址首次被获取到的日期"
driveCapacityOverride: "网盘容量变更"
isLocked: 该账号设置了关注请求
_filters:
notesBefore: 帖子早于
@ -2050,3 +2053,5 @@ searchRangeDescription: "如果您要过滤时间段,请按以下格式输入
或 20231105-0110它将被解释为当前年份。\n\n您还可以省略开始日期或结束日期。 例如 -0102 将过滤搜索结果以仅显示今年 1 月 2 日之前发布的帖子,而
20231026- 将过滤结果以仅显示 2023 年 10 月 26 日之后发布的帖子。"
messagingUnencryptedInfo: "Firefish 上的聊天没有经过端到端加密,请不要在聊天中分享您的敏感信息。"
noAltTextWarning: 有些附件没有说明。您是否忘记写说明了?
showNoAltTextWarning: 当您尝试发布没有说明的帖子附件时显示警告

View file

@ -2048,3 +2048,7 @@ searchUsersDescription: "如欲搜尋特定使用者的貼文,請以「@user@e
\n輸入「me」以搜尋自己的所有貼文包含不在主頁顯示、追隨者、指定使用者、祕密貼文。\n\n輸入「local」以搜尋本地伺服器的貼文。"
searchRangeDescription: "如欲搜尋特定期間的貼文請以「20220615-20231031」的格式輸入日期範圍。\n\n今年的日期可省略年份例如0105-0106、20231105-0110。\n\
\n開始日期和結果日期可擇一省略。舉例來說「-0102」表示僅搜尋今年1月2日為止的貼文「20231026-」表示僅搜尋2023年10月26日以後的貼文。"
noAltTextWarning: 有些附件沒有說明,您是否忘記寫了?
moderationNote: 管理員備註
ipFirstAcknowledged: 首次取得此 IP 位址的日期
driveCapacityOverride: 雲端硬碟容量變更

View file

@ -43,14 +43,14 @@
"gulp-terser": "2.1.0"
},
"devDependencies": {
"@biomejs/biome": "1.5.3",
"@biomejs/cli-darwin-arm64": "^1.5.3",
"@biomejs/cli-darwin-x64": "^1.5.3",
"@biomejs/cli-linux-arm64": "^1.5.3",
"@biomejs/cli-linux-x64": "^1.5.3",
"@types/node": "20.11.24",
"@biomejs/biome": "1.6.1",
"@biomejs/cli-darwin-arm64": "^1.6.1",
"@biomejs/cli-darwin-x64": "^1.6.1",
"@biomejs/cli-linux-arm64": "^1.6.1",
"@biomejs/cli-linux-x64": "^1.6.1",
"@types/node": "20.11.28",
"execa": "8.0.1",
"pnpm": "8.15.4",
"typescript": "5.3.3"
"typescript": "5.4.2"
}
}

View file

@ -128,9 +128,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.80"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "arrayvec"
@ -180,16 +180,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "atomic-write-file"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8204db279bf648d64fe845bd8840f78b39c8132ed4d6a4194c3b10d4b4cfb0b"
dependencies = [
"nix",
"rand",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -342,9 +332,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.15.3"
version = "3.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
[[package]]
name = "bytecheck"
@ -388,9 +378,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.89"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
[[package]]
name = "cfg-if"
@ -421,9 +411,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.2"
version = "4.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651"
checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
dependencies = [
"clap_builder",
"clap_derive",
@ -443,11 +433,11 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.0"
version = "4.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
dependencies = [
"heck",
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.52",
@ -868,9 +858,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "h2"
version = "0.3.24"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb"
dependencies = [
"bytes",
"fnv",
@ -922,6 +912,12 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
@ -1321,18 +1317,6 @@ dependencies = [
"libloading",
]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.4.2",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nom"
version = "7.1.3"
@ -1499,7 +1483,7 @@ version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro-error",
"proc-macro2",
"quote",
@ -1672,9 +1656,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.78"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
@ -1799,9 +1783,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.24"
version = "0.11.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2"
dependencies = [
"base64",
"bytes",
@ -2015,7 +1999,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro-error",
"proc-macro2",
"quote",
@ -2056,7 +2040,7 @@ version = "0.12.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec13bfb4c4aef208f68dbea970dd40d13830c868aa8dcb4e106b956e6bb4f2fa"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"sea-bae",
@ -2283,9 +2267,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf"
checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa"
dependencies = [
"sqlx-core",
"sqlx-macros",
@ -2296,9 +2280,9 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
dependencies = [
"ahash 0.8.11",
"atoi",
@ -2308,7 +2292,6 @@ dependencies = [
"chrono",
"crc",
"crossbeam-queue",
"dotenvy",
"either",
"event-listener",
"futures-channel",
@ -2344,9 +2327,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5"
checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127"
dependencies = [
"proc-macro2",
"quote",
@ -2357,14 +2340,13 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841"
checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8"
dependencies = [
"atomic-write-file",
"dotenvy",
"either",
"heck",
"heck 0.4.1",
"hex",
"once_cell",
"proc-macro2",
@ -2384,9 +2366,9 @@ dependencies = [
[[package]]
name = "sqlx-mysql"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4"
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
dependencies = [
"atoi",
"base64",
@ -2431,9 +2413,9 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24"
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
dependencies = [
"atoi",
"base64",
@ -2462,7 +2444,6 @@ dependencies = [
"rust_decimal",
"serde",
"serde_json",
"sha1",
"sha2",
"smallvec",
"sqlx-core",
@ -2476,9 +2457,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490"
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
dependencies = [
"atoi",
"chrono",
@ -2639,18 +2620,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.57"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
@ -2735,9 +2716,9 @@ dependencies = [
[[package]]
name = "tokio-stream"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
dependencies = [
"futures-core",
"pin-project-lite",
@ -3013,9 +2994,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "whoami"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
dependencies = [
"redox_syscall",
"wasite",

View file

@ -12,20 +12,20 @@ napi = ["dep:napi", "dep:napi-derive"]
crate-type = ["cdylib", "lib"]
[dependencies]
async-trait = "0.1.75"
async-trait = "0.1.77"
cfg-if = "1.0.0"
chrono = "0.4.31"
chrono = "0.4.35"
cuid2 = "0.1.2"
jsonschema = "0.17.1"
once_cell = "1.19.0"
parse-display = "0.8.2"
rand = "0.8.5"
schemars = { version = "0.8.16", features = ["chrono"] }
sea-orm = { version = "0.12.10", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
thiserror = "1.0.52"
tokio = { version = "1.35.1", features = ["full"] }
sea-orm = { version = "0.12.14", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
thiserror = "1.0.58"
tokio = { version = "1.36.0", features = ["full"] }
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.16.0", default-features = false, features = ["napi9", "tokio_rt"], optional = true }
@ -36,7 +36,7 @@ basen = "0.1.0"
pretty_assertions = "1.4.0"
[build-dependencies]
napi-build = "2.1.0"
napi-build = "2.1.2"
[profile.release]
lto = true

View file

@ -22,23 +22,23 @@
"@swc/core-android-arm64": "1.3.11"
},
"dependencies": {
"@bull-board/api": "5.14.2",
"@bull-board/koa": "5.14.2",
"@bull-board/ui": "5.14.2",
"@bull-board/api": "5.15.1",
"@bull-board/koa": "5.15.1",
"@bull-board/ui": "5.15.1",
"@discordapp/twemoji": "^15.0.2",
"@koa/cors": "5.0.0",
"@koa/multer": "3.0.2",
"@koa/router": "12.0.1",
"@ladjs/koa-views": "9.0.0",
"@peertube/http-signature": "1.7.0",
"@redocly/openapi-core": "1.10.3",
"@redocly/openapi-core": "1.10.4",
"@sinonjs/fake-timers": "11.2.2",
"@twemoji/parser": "^15.0.0",
"adm-zip": "^0.5.10",
"adm-zip": "^0.5.12",
"ajv": "8.12.0",
"archiver": "7.0.0",
"archiver": "7.0.1",
"argon2": "^0.40.1",
"aws-sdk": "2.1571.0",
"aws-sdk": "2.1578.0",
"axios": "^1.6.7",
"backend-rs": "workspace:*",
"bcryptjs": "2.4.3",
@ -51,7 +51,7 @@
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "3.3.1",
"date-fns": "3.5.0",
"decompress": "^4.2.1",
"deep-email-validator": "0.1.21",
"deepl-node": "1.12.0",
@ -60,9 +60,9 @@
"file-type": "19.0.0",
"fluent-ffmpeg": "2.1.2",
"form-data": "^4.0.0",
"got": "14.2.0",
"got": "14.2.1",
"gunzip-maybe": "^1.4.2",
"happy-dom": "^13.6.2",
"happy-dom": "^13.8.6",
"hpagent": "1.2.0",
"ioredis": "5.3.2",
"ip-cidr": "4.0.0",
@ -71,7 +71,7 @@
"json5": "2.2.3",
"jsonld": "8.3.2",
"jsrsasign": "11.1.0",
"koa": "2.15.0",
"koa": "2.15.1",
"koa-body": "^6.0.1",
"koa-bodyparser": "4.4.1",
"koa-favicon": "2.1.0",
@ -88,7 +88,7 @@
"multer": "1.4.5-lts.1",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"nodemailer": "6.9.11",
"nodemailer": "6.9.12",
"opencc-js": "^1.0.5",
"os-utils": "0.0.14",
"otpauth": "^9.2.2",
@ -100,7 +100,7 @@
"punycode": "2.3.1",
"pureimage": "0.4.13",
"qrcode": "1.5.3",
"qs": "6.11.2",
"qs": "6.12.0",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.20.10",
@ -115,7 +115,7 @@
"stringz": "2.1.0",
"summaly": "2.7.0",
"syslog-pro": "1.0.0",
"systeminformation": "5.22.0",
"systeminformation": "5.22.2",
"tar-stream": "^3.1.7",
"tesseract.js": "^5.0.5",
"tinycolor2": "1.6.0",
@ -129,7 +129,7 @@
},
"devDependencies": {
"@swc/cli": "0.3.10",
"@swc/core": "1.4.4",
"@swc/core": "1.4.8",
"@types/adm-zip": "^0.5.5",
"@types/bcryptjs": "2.4.6",
"@types/color-convert": "^2.0.3",
@ -138,7 +138,7 @@
"@types/fluent-ffmpeg": "2.1.24",
"@types/js-yaml": "4.0.9",
"@types/jsonld": "1.5.13",
"@types/jsrsasign": "10.5.12",
"@types/jsrsasign": "10.5.13",
"@types/koa": "2.15.0",
"@types/koa-bodyparser": "4.3.12",
"@types/koa-cors": "0.0.6",
@ -150,7 +150,7 @@
"@types/koa__multer": "2.0.7",
"@types/koa__router": "12.0.4",
"@types/mocha": "10.0.6",
"@types/node": "20.11.24",
"@types/node": "20.11.28",
"@types/node-fetch": "2.6.11",
"@types/nodemailer": "6.4.14",
"@types/oauth": "0.9.4",
@ -183,7 +183,7 @@
"ts-loader": "9.5.1",
"ts-node": "10.9.2",
"tsconfig-paths": "4.2.0",
"typescript": "5.3.3",
"typescript": "5.4.2",
"webpack": "^5.90.3",
"ws": "8.16.0"
}

View file

@ -71,7 +71,7 @@ function greet() {
136,
0,
)(
" If you like Firefish, please consider starring or contributing to the repo. https://firefish.dev/firefish/firefish",
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
),
);

View file

@ -6,7 +6,7 @@ import { EventEmitter } from "node:events";
import { inspect } from "node:util";
import boot from "./boot/index.js";
Error.stackTraceLimit = Infinity;
Error.stackTraceLimit = Number.POSITIVE_INFINITY;
EventEmitter.defaultMaxListeners = 128;
boot().catch((err) => {

View file

@ -7,7 +7,7 @@ export class RemoveNativeUtilsMigration1705877093218
await queryRunner.query(`DROP TABLE IF EXISTS "reversi_game"`);
await queryRunner.query(`DROP TABLE IF EXISTS "reversi_matching"`);
await queryRunner.query(
`CREATE INDEX IF NOT EXISTS "IDX_note_url" ON "note" ("text")`,
`CREATE INDEX IF NOT EXISTS "IDX_note_url" ON "note" ("url")`,
);
await queryRunner.query(`DROP TABLE IF EXISTS "antenna_note"`);
await queryRunner.query(

View file

@ -1,6 +1,10 @@
import { URL } from "node:url";
import config from "@/config/index.js";
import { toASCII } from "punycode";
import Logger from "@/services/logger.js";
import { inspect } from "node:util";
const logger = new Logger("convert-host");
export function getFullApAccount(username: string, host: string | null) {
return host
@ -13,6 +17,20 @@ export function isSelfHost(host: string) {
return toPuny(config.host) === toPuny(host);
}
export function isSameOrigin(src: unknown): boolean | null {
if (typeof src !== "string") {
logger.debug(`unknown origin: ${inspect(src)}`);
return null;
}
try {
const u = new URL(src);
return u.origin === config.url;
} catch (e) {
logger.debug(inspect(e));
return false;
}
}
export function extractDbHost(uri: string) {
const url = new URL(uri);
return toPuny(url.hostname);

View file

@ -513,8 +513,8 @@ export const UserRepository = db.getRepository(User).extend({
location: profile!.location,
birthday: profile!.birthday,
fields: profile!.fields,
followersCount: followersCount || 0,
followingCount: followingCount || 0,
followersCount: followersCount ?? null,
followingCount: followingCount ?? null,
notesCount: user.notesCount,
pinnedNoteIds: pins.map((pin) => pin.noteId),
pinnedNotes: Notes.packMany(
@ -528,8 +528,11 @@ export const UserRepository = db.getRepository(User).extend({
pinnedPage: profile!.pinnedPageId
? Pages.pack(profile!.pinnedPageId, me)
: null,
publicReactions: profile!.publicReactions,
ffVisibility: profile!.ffVisibility,
// TODO: federate publicReactions
publicReactions:
user.host == null ? profile!.publicReactions : false,
// TODO: federate ffVisibility
ffVisibility: user.host == null ? profile!.ffVisibility : "private",
twoFactorEnabled: profile!.twoFactorEnabled,
usePasswordLessLogin: profile!.usePasswordLessLogin,
securityKeys: UserSecurityKeys.countBy({

View file

@ -1,6 +1,5 @@
import promiseLimit from "promise-limit";
import * as mfm from "mfm-js";
import config from "@/config/index.js";
import Resolver from "../resolver.js";
import post from "@/services/note/create.js";
import { extractMentionedUsers } from "@/services/note/create.js";
@ -14,7 +13,7 @@ import { extractPollFromQuestion } from "./question.js";
import vote from "@/services/note/polls/vote.js";
import { apLogger } from "../logger.js";
import { DriveFile } from "@/models/entities/drive-file.js";
import { extractDbHost, toPuny } from "@/misc/convert-host.js";
import { extractDbHost, isSameOrigin, toPuny } from "@/misc/convert-host.js";
import {
Emojis,
Polls,
@ -234,7 +233,7 @@ export async function createNote(
.catch(async (e) => {
// トークだったらinReplyToのエラーは無視
const uri = getApId(note.inReplyTo);
if (uri.startsWith(`${config.url}/`)) {
if (isSameOrigin(uri)) {
const id = uri.split("/").pop();
const talk = await MessagingMessages.findOneBy({ id });
if (talk) {
@ -439,7 +438,7 @@ export async function resolveNote(
}
//#endregion
if (uri.startsWith(config.url)) {
if (isSameOrigin(uri)) {
throw new StatusError(
"cannot resolve local note",
400,
@ -556,7 +555,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) {
if (!uri) throw new Error("Missing note uri");
// Skip if URI points to this server
if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local");
if (isSameOrigin(uri)) throw new Error("uri points local");
// A new resolver is created if not specified
if (resolver == null) resolver = new Resolver();

View file

@ -19,7 +19,7 @@ import { UserNotePining } from "@/models/entities/user-note-pining.js";
import { genId } from "@/misc/gen-id.js";
import { UserPublickey } from "@/models/entities/user-publickey.js";
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
import { toPuny } from "@/misc/convert-host.js";
import { isSameOrigin, toPuny } from "@/misc/convert-host.js";
import { UserProfile } from "@/models/entities/user-profile.js";
import { toArray } from "@/prelude/array.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
@ -138,7 +138,7 @@ export async function fetchPerson(
if (cached) return cached;
// Fetch from the database if the URI points to this server
if (uri.startsWith(`${config.url}/`)) {
if (isSameOrigin(uri)) {
const id = uri.split("/").pop();
const u = await Users.findOneBy({ id });
if (u) await uriPersonCache.set(uri, u);
@ -166,7 +166,7 @@ export async function createPerson(
): Promise<User> {
if (typeof uri !== "string") throw new Error("uri is not string");
if (uri.startsWith(config.url)) {
if (isSameOrigin(uri)) {
throw new StatusError(
"cannot resolve local user",
400,
@ -419,7 +419,7 @@ export async function updatePerson(
if (typeof uri !== "string") throw new Error("uri is not string");
// Skip if the URI points to this server
if (uri.startsWith(`${config.url}/`)) {
if (isSameOrigin(uri)) {
return;
}

View file

@ -1,10 +1,10 @@
import config from "@/config/index.js";
import Resolver from "../resolver.js";
import type { IObject, IQuestion } from "../type.js";
import { getApId, isQuestion } from "../type.js";
import { apLogger } from "../logger.js";
import { Notes, Polls } from "@/models/index.js";
import type { IPoll } from "@/models/entities/poll.js";
import { isSameOrigin } from "@/misc/convert-host.js";
export async function extractPollFromQuestion(
source: string | IObject,
@ -57,7 +57,7 @@ export async function updateQuestion(
const uri = typeof value === "string" ? value : getApId(value);
// Skip if URI points to this server
if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local");
if (isSameOrigin(uri)) throw new Error("uri points local");
//#region Already registered with this server?
const note = await Notes.findOneBy({ uri });

View file

@ -90,6 +90,11 @@ export default define(meta, paramDef, async (ps, me) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// TODO: federate ffVisibility
if (profile.userHost != null) {
throw new ApiError(meta.errors.forbidden);
}
if (profile.ffVisibility === "private") {
if (me == null || me.id !== user.id) {
throw new ApiError(meta.errors.forbidden);

View file

@ -89,6 +89,11 @@ export default define(meta, paramDef, async (ps, me) => {
const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
// TODO: federate ffVisibility
if (profile.userHost != null) {
throw new ApiError(meta.errors.forbidden);
}
if (profile.ffVisibility === "private") {
if (me == null || me.id !== user.id) {
throw new ApiError(meta.errors.forbidden);

View file

@ -49,6 +49,11 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, me) => {
const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId });
// TODO: federate publicReactions
if (profile.userHost != null) {
throw new ApiError(meta.errors.reactionsNotPublic);
}
if (!profile.publicReactions && (me == null || me.id !== ps.userId)) {
throw new ApiError(meta.errors.reactionsNotPublic);
}

View file

@ -463,7 +463,7 @@ export default abstract class Chart<T extends Schema> {
protected commit(diff: Commit<T>, group: string | null = null): void {
for (const [k, v] of Object.entries(diff)) {
if (v == null || v === 0 || (Array.isArray(v) && v.length === 0))
// rome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
// biome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
delete diff[k];
}
this.buffer.push({

View file

@ -1,6 +1,6 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as assert from "node:assert";
import rndstr from "rndstr";
import { initDb } from "../src/db/postgre.js";
import { initTestDb } from "./utils.js";

View file

@ -1,4 +1,4 @@
import * as assert from "assert";
import * as assert from "node:assert";
import httpSignature from "@peertube/http-signature";
import { genRsaKeyPair } from "../src/misc/gen-key-pair.js";
import {

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
post,

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
post,

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
post,

View file

@ -1,6 +1,6 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as assert from "node:assert";
import { inspect } from "node:util";
import {
signup,

View file

@ -1,4 +1,4 @@
import * as assert from "assert";
import * as assert from "node:assert";
import { parse } from "mfm-js";
import { extractMentions } from "../src/misc/extract-mentions.js";

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import * as openapi from "@redocly/openapi-core";
import {
async,

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
connectStream,

View file

@ -1,4 +1,4 @@
import * as assert from "assert";
import * as assert from "node:assert";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { getFileInfo } from "../src/misc/get-file-info.js";

View file

@ -1,4 +1,4 @@
import * as assert from "assert";
import * as assert from "node:assert";
import * as mfm from "mfm-js";
import { fromHtml } from "../src/mfm/from-html.js";

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
post,

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import { Note } from "../src/models/entities/note.js";
import {
api,

View file

@ -1,4 +1,4 @@
import * as assert from "assert";
import * as assert from "node:assert";
import { just, nothing } from "../../src/prelude/maybe.js";
describe("just", () => {

View file

@ -1,4 +1,4 @@
import * as assert from "assert";
import * as assert from "node:assert";
import { query } from "../../src/prelude/url.js";
describe("url", () => {

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import { Following } from "../src/models/entities/following.js";
import {
api,

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
connectStream,

View file

@ -26,16 +26,9 @@
"paths": {
"@/*": ["../src/*"]
},
"typeRoots": [
"../node_modules/@types",
"../src/@types"
],
"lib": [
"esnext"
]
"typeRoots": ["../node_modules/@types", "../src/@types"],
"lib": ["esnext"]
},
"compileOnSave": false,
"include": [
"./**/*.ts"
]
"include": ["./**/*.ts"]
}

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = "test";
import * as assert from "assert";
import * as childProcess from "child_process";
import * as assert from "node:assert";
import type * as childProcess from "node:child_process";
import {
async,
post,

View file

@ -1,11 +1,11 @@
import * as childProcess from "child_process";
import { SIGKILL } from "constants";
import * as childProcess from "node:child_process";
import * as fs from "node:fs";
import * as http from "node:http";
import * as path from "node:path";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import type { endpoints, Entities } from "firefish-js";
import type { Entities, endpoints } from "firefish-js";
import FormData from "form-data";
import got from "got";
import fetch from "node-fetch";

View file

@ -25,24 +25,13 @@
"rootDir": "./src",
"baseUrl": "./",
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
},
"outDir": "./built",
"types": [
"node"
],
"typeRoots": [
"./node_modules/@types",
"./src/@types"
],
"lib": [
"esnext"
]
"types": ["node"],
"typeRoots": ["./node_modules/@types", "./src/@types"],
"lib": ["esnext"]
},
"compileOnSave": false,
"include": [
"./src/**/*.ts"
]
"include": ["./src/**/*.ts"]
}

View file

@ -1,5 +1,5 @@
declare module "@/themes/*.json5" {
import { Theme } from "@/scripts/theme";
import type { Theme } from "@/scripts/theme";
const theme: Theme;

View file

@ -47,7 +47,7 @@
"city-timezones": "^1.2.1",
"compare-versions": "6.1.0",
"cropperjs": "2.0.0-beta.4",
"date-fns": "3.3.1",
"date-fns": "3.5.0",
"emojilib": "^3.0.11",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-file-progress": "^1.3.0",
@ -69,9 +69,9 @@
"prettier": "3.2.5",
"prismjs": "1.29.0",
"punycode": "2.3.1",
"rollup": "4.12.0",
"rollup": "4.13.0",
"s-age": "1.1.2",
"sass": "1.71.1",
"sass": "1.72.0",
"seedrandom": "3.0.5",
"stringz": "2.1.0",
"swiper": "11.0.7",
@ -81,10 +81,10 @@
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tinyld": "^1.3.4",
"typescript": "5.3.3",
"typescript": "5.4.2",
"unicode-emoji-json": "^0.4.0",
"uuid": "9.0.1",
"vite": "5.1.5",
"vite": "5.1.6",
"vite-plugin-compression": "^0.5.1",
"vue": "3.4.21",
"vue-draggable-plus": "^0.3.5",

View file

@ -2,9 +2,8 @@ import type { entities } from "firefish-js";
import { defineAsyncComponent } from "vue";
import { i18n } from "./i18n";
import { apiUrl } from "@/config";
import { me } from "@/me";
import { alert, api, popup, popupMenu, waiting } from "@/os";
import { $i } from "@/reactiveAccount";
import icon from "@/scripts/icon";
import { del, get, set } from "@/scripts/idb-proxy";
import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
@ -12,11 +11,11 @@ import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
export type Account = entities.MeDetailed;
export async function signout() {
export async function signOut() {
waiting();
localStorage.removeItem("account");
await removeAccount($i.id);
await removeAccount(me.id);
const accounts = await getAccounts();
@ -29,7 +28,7 @@ export async function signout() {
await fetch(`${apiUrl}/sw/unregister`, {
method: "POST",
body: JSON.stringify({
i: $i.token,
i: me.token,
endpoint: push.endpoint,
}),
});
@ -48,7 +47,7 @@ export async function signout() {
document.cookie = "igi=; path=/";
if (accounts.length > 0) login(accounts[0].token);
if (accounts.length > 0) signIn(accounts[0].token);
else unisonReload("/");
}
@ -90,7 +89,7 @@ function fetchAccount(token: string): Promise<Account> {
if (res.error) {
if (res.error.id === "a8c724b3-6e9c-4b46-b1a8-bc3ed6258370") {
showSuspendedDialog();
signout();
signOut();
} else {
alert({
type: "error",
@ -117,22 +116,23 @@ function showSuspendedDialog() {
export function updateAccount(accountData) {
for (const [key, value] of Object.entries(accountData)) {
$i[key] = value;
me[key] = value;
}
localStorage.setItem("account", JSON.stringify($i));
localStorage.setItem("account", JSON.stringify(me));
}
export function refreshAccount() {
return fetchAccount($i.token).then(updateAccount);
export async function refreshAccount() {
const accountData = await fetchAccount(me.token);
return updateAccount(accountData);
}
export async function login(token: Account["token"], redirect?: string) {
export async function signIn(token: Account["token"], redirect?: string) {
waiting();
if (_DEV_) console.log("logging as token ", token);
const me = await fetchAccount(token);
localStorage.setItem("account", JSON.stringify(me));
const newAccount = await fetchAccount(token);
localStorage.setItem("account", JSON.stringify(newAccount));
document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う
await addAccount(me.id, token);
await addAccount(newAccount.id, token);
if (redirect) {
// 他のタブは再読み込みするだけ
@ -190,11 +190,11 @@ export async function openAccountMenu(
}
function switchAccountWithToken(token: string) {
login(token);
signIn(token);
}
const storedAccounts = await getAccounts().then((accounts) =>
accounts.filter((x) => x.id !== $i.id),
accounts.filter((x) => x.id !== me.id),
);
const accountsPromise = api("users/show", {
userIds: storedAccounts.map((x) => x.id),
@ -256,12 +256,12 @@ export async function openAccountMenu(
{
type: "link",
text: i18n.ts.profile,
to: `/@${$i.username}`,
avatar: $i,
to: `/@${me.username}`,
avatar: me,
},
null,
]),
...(opts.includeCurrentAccount ? [createItem($i)] : []),
...(opts.includeCurrentAccount ? [createItem(me)] : []),
...accountItemPromises,
...(isMobile ?? false
? [
@ -269,8 +269,8 @@ export async function openAccountMenu(
{
type: "link",
text: i18n.ts.profile,
to: `/@${$i.username}`,
avatar: $i,
to: `/@${me.username}`,
avatar: me,
},
]
: [
@ -304,7 +304,7 @@ export async function openAccountMenu(
} else {
popupMenu(
[
...(opts.includeCurrentAccount ? [createItem($i)] : []),
...(opts.includeCurrentAccount ? [createItem(me)] : []),
...accountItemPromises,
],
ev.currentTarget ?? ev.target,

View file

@ -4,7 +4,7 @@
:class="{
isMe: isMe(message),
isRead: message.groupId
? message.reads.includes($i?.id)
? message.reads.includes(me?.id)
: message.isRead,
}"
:to="
@ -67,14 +67,14 @@
<script lang="ts" setup>
import { acct } from "firefish-js";
import { i18n } from "@/i18n";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
defineProps<{
message: Record<string, any>;
}>();
function isMe(message): boolean {
return message.userId === $i?.id;
return message.userId === me?.id;
}
</script>

View file

@ -42,7 +42,7 @@ import Cropper from "cropperjs";
import tinycolor from "tinycolor2";
import XModalWindow from "@/components/MkModalWindow.vue";
import * as os from "@/os";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { defaultStore } from "@/store";
import { apiUrl, url } from "@/config";
import { query } from "@/scripts/url";
@ -81,7 +81,7 @@ const ok = async () => {
method: "POST",
body: formData,
headers: {
authorization: `Bearer ${$i.token}`,
authorization: `Bearer ${me.token}`,
},
})
.then((response) => response.json())

View file

@ -10,7 +10,7 @@
</div>
<div :class="$style.text">
{{ i18n.ts._aboutFirefish.pleaseDonateToFirefish }}
<p v-if="$instance.donationLink">
<p v-if="instance.donationLink">
{{
i18n.t("_aboutFirefish.pleaseDonateToHost", {
host: hostname,
@ -27,9 +27,9 @@
>{{ i18n.ts._aboutFirefish.donate }}</MkButton
>
<MkButton
v-if="$instance.donationLink"
v-if="instance.donationLink"
gradate
@click="openExternal($instance.donationLink)"
@click="openExternal(instance.donationLink)"
>{{
i18n.t("_aboutFirefish.donateHost", {
host: hostname,

View file

@ -9,11 +9,11 @@
@dragstart="onDragstart"
@dragend="onDragend"
>
<div v-if="$i?.avatarId == file.id" class="label">
<div v-if="me?.avatarId == file.id" class="label">
<img src="/client-assets/label.svg" />
<p>{{ i18n.ts.avatar }}</p>
</div>
<div v-if="$i?.bannerId == file.id" class="label">
<div v-if="me?.bannerId == file.id" class="label">
<img src="/client-assets/label.svg" />
<p>{{ i18n.ts.banner }}</p>
</div>
@ -45,7 +45,7 @@ import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
import bytes from "@/filters/bytes";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import icon from "@/scripts/icon";
const props = withDefaults(

View file

@ -8,7 +8,7 @@
<i :class="icon('ph-dots-three-outline')"></i>
</button>
<button
v-if="!hideFollowButton && isSignedIn && $i.id != user.id"
v-if="!hideFollowButton && isSignedIn && me.id != user.id"
v-tooltip="full ? null : `${state} ${user.name || user.username}`"
class="kpoogebi _button follow-button"
:class="{
@ -66,7 +66,7 @@ import type { entities } from "firefish-js";
import * as os from "@/os";
import { useStream } from "@/stream";
import { i18n } from "@/i18n";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { getUserMenu } from "@/scripts/get-user-menu";
import { useRouter } from "@/router";
import { vibrate } from "@/scripts/vibrate";

View file

@ -55,6 +55,7 @@ const commonNames = new Map<string, string>([
["gnusocial", "GNU social"],
["gotosocial", "GoToSocial"],
["kbin", "/kbin"],
["kmyblue", "kmyblue"],
["microblogpub", "microblog.pub"],
["nextcloud social", "Nextcloud Social"],
["peertube", "PeerTube"],

View file

@ -36,7 +36,7 @@
<script lang="ts" setup>
import { toUnicode } from "punycode";
import { host as localHost } from "@/config";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { defaultStore } from "@/store";
const props = defineProps<{
@ -53,8 +53,8 @@ const url = `/${canonical}`;
const isMe =
isSignedIn &&
`@${props.username}@${toUnicode(props.host)}` ===
`@${$i.username}@${toUnicode(localHost)}`.toLowerCase();
`@${props.username}@${toUnicode(props.host)}`.toLowerCase() ===
`@${me.username}@${toUnicode(localHost)}`.toLowerCase();
</script>
<style lang="scss" scoped>

View file

@ -127,7 +127,7 @@
<Mfm
:text="translation.text"
:author="appearNote.user"
:i="$i"
:i="me"
:lang="targetLang"
:custom-emojis="appearNote.emojis"
/>
@ -296,7 +296,7 @@ import { userPage } from "@/filters/user";
import * as os from "@/os";
import { defaultStore, noteViewInterruptors } from "@/store";
import { reactionPicker } from "@/scripts/reaction-picker";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n";
import { getNoteMenu } from "@/scripts/get-note-menu";
import { useNoteCapture } from "@/scripts/use-note-capture";
@ -355,13 +355,13 @@ const reactButton = ref<HTMLElement>();
const appearNote = computed(() =>
isRenote ? (note.value.renote as entities.Note) : note.value,
);
const isMyRenote = isSignedIn && $i.id === note.value.userId;
const isMyRenote = isSignedIn && me.id === note.value.userId;
const showContent = ref(false);
const isDeleted = ref(false);
const muted = ref(
getWordSoftMute(
note.value,
$i?.id,
me?.id,
defaultStore.state.mutedWords,
defaultStore.state.mutedLangs,
),
@ -632,9 +632,7 @@ function setPostExpanded(val: boolean) {
const accessibleLabel = computed(() => {
let label = `${appearNote.value.user.username}; `;
if (appearNote.value.renote) {
label += `${i18n.t("renoted")} ${
appearNote.value.renote.user.username
}; `;
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) {

View file

@ -180,7 +180,7 @@ import { userPage } from "@/filters/user";
import * as os from "@/os";
import { defaultStore, noteViewInterruptors } from "@/store";
import { reactionPicker } from "@/scripts/reaction-picker";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { i18n } from "@/i18n";
import { getNoteMenu } from "@/scripts/get-note-menu";
import { useNoteCapture } from "@/scripts/use-note-capture";
@ -235,7 +235,7 @@ const isDeleted = ref(false);
const muted = ref(
getWordSoftMute(
note.value,
$i?.id,
me?.id,
defaultStore.state.mutedWords,
defaultStore.state.mutedLangs,
),

View file

@ -1,17 +1,17 @@
<template>
<div v-size="{ min: [350, 500] }" class="fefdfafb">
<MkAvatar class="avatar" :user="$i" disable-link />
<MkAvatar class="avatar" :user="me" disable-link />
<div class="main">
<div class="header">
<MkUserName :user="$i" />
<MkUserName :user="me" />
</div>
<div class="body">
<div class="content">
<Mfm
:text="preprocess(text).trim()"
:lang="lang"
:author="$i"
:i="$i"
:author="me"
:i="me"
advanced-mfm
/>
</div>

View file

@ -50,7 +50,7 @@
<Mfm
:text="translation.text"
:author="appearNote.user"
:i="$i"
:i="me"
:lang="targetLang"
:custom-emojis="appearNote.emojis"
/>
@ -211,7 +211,7 @@ import { useRouter } from "@/router";
import { userPage } from "@/filters/user";
import * as os from "@/os";
import { reactionPicker } from "@/scripts/reaction-picker";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n";
import { useNoteCapture } from "@/scripts/use-note-capture";
import { defaultStore } from "@/store";
@ -269,7 +269,7 @@ const isDeleted = ref(false);
const muted = ref(
getWordSoftMute(
note.value,
$i?.id,
me?.id,
defaultStore.state.mutedWords,
defaultStore.state.mutedLangs,
),

View file

@ -54,7 +54,7 @@ import XNotification from "@/components/MkNotification.vue";
import XList from "@/components/MkDateSeparatedList.vue";
import XNote from "@/components/MkNote.vue";
import { useStream } from "@/stream";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { i18n } from "@/i18n";
const props = defineProps<{
@ -73,7 +73,7 @@ const pagination: Paging = {
includeTypes: props.includeTypes ?? undefined,
excludeTypes: props.includeTypes
? undefined
: $i.mutingNotificationTypes,
: me.mutingNotificationTypes,
unreadOnly: props.unreadOnly,
})),
};
@ -81,7 +81,7 @@ const pagination: Paging = {
const onNotification = (notification) => {
const isMuted = props.includeTypes
? !props.includeTypes.includes(notification.type)
: $i.mutingNotificationTypes.includes(notification.type);
: me.mutingNotificationTypes.includes(notification.type);
if (isMuted || document.visibilityState === "visible") {
stream.send("readNotification", {
id: notification.id,

View file

@ -20,7 +20,7 @@
class="account _button"
@click="openAccountMenu"
>
<MkAvatar :user="postAccount ?? $i" class="avatar" />
<MkAvatar :user="postAccount ?? me" class="avatar" />
</button>
<div class="right">
<span
@ -322,7 +322,7 @@ import MkInfo from "@/components/MkInfo.vue";
import { i18n } from "@/i18n";
import { instance } from "@/instance";
import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { uploadFile } from "@/scripts/upload";
import { deepClone } from "@/scripts/clone";
import XCheatSheet from "@/components/MkCheatSheetDialog.vue";
@ -517,7 +517,7 @@ if (props.mention) {
if (
props.reply &&
(props.reply.user.username !== $i.username ||
(props.reply.user.username !== me.username ||
(props.reply.user.host != null && props.reply.user.host !== host))
) {
text.value = `@${props.reply.user.username}${
@ -539,7 +539,7 @@ if (props.reply && props.reply.text != null) {
: `@${x.username}@${toASCII(otherHost)}`;
// exclude me
if ($i.username === x.username && (x.host == null || x.host === host))
if (me.username === x.username && (x.host == null || x.host === host))
continue;
// remove duplicates
@ -573,7 +573,7 @@ if (
if (props.reply.visibleUserIds) {
os.api("users/show", {
userIds: props.reply.visibleUserIds.filter(
(uid) => uid !== $i.id && uid !== props.reply.userId,
(uid) => uid !== me.id && uid !== props.reply.userId,
),
}).then((users) => {
users.forEach(pushVisibleUser);
@ -582,7 +582,7 @@ if (
visibility.value = "private";
}
if (props.reply.userId !== $i.id) {
if (props.reply.userId !== me.id) {
os.api("users/show", { userId: props.reply.userId }).then(
(user) => {
pushVisibleUser(user);
@ -611,7 +611,7 @@ const addRe = (s: string) => {
if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
useCw.value = true;
cw.value =
props.reply.user.username === $i.username
props.reply.user.username === me.username
? props.reply.cw
: addRe(props.reply.cw);
}
@ -1194,9 +1194,9 @@ function openAccountMenu(ev: MouseEvent) {
{
withExtraOperation: false,
includeCurrentAccount: true,
active: postAccount.value != null ? postAccount.value.id : $i.id,
active: postAccount.value != null ? postAccount.value.id : me.id,
onChoose: (account) => {
if (account.id === $i.id) {
if (account.id === me.id) {
postAccount.value = null;
} else {
postAccount.value = account;

View file

@ -45,7 +45,6 @@ const isRefreshing = ref(false);
const pullDistance = ref(0);
let disabled = false;
let supportPointerDesktop = false;
let startScreenY: number | null = null;

View file

@ -56,7 +56,7 @@
import { ref } from "vue";
import { getAccounts } from "@/account";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import MkButton from "@/components/MkButton.vue";
import { instance } from "@/instance";
import { api, apiWithDialog, promiseDialog } from "@/os";
@ -149,7 +149,7 @@ async function unsubscribe() {
if (isSignedIn && accounts.length >= 2) {
apiWithDialog("sw/unregister", {
i: $i.token,
i: me.token,
endpoint,
});
} else {
@ -197,7 +197,7 @@ if (navigator.serviceWorker == null) {
instance.swPublickey &&
"PushManager" in window &&
isSignedIn &&
$i.token
me.token
) {
supported.value = true;

View file

@ -14,7 +14,7 @@ import { computed } from "vue";
import type { entities } from "firefish-js";
import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
@ -26,7 +26,7 @@ const props = defineProps<{
const canRenote = computed(
() =>
["public", "home"].includes(props.note.visibility) ||
props.note.userId === $i?.id,
props.note.userId === me?.id,
);
function quote(): void {

View file

@ -28,7 +28,7 @@ import XDetails from "@/components/MkReactionsViewer.details.vue";
import XReactionIcon from "@/components/MkReactionIcon.vue";
import * as os from "@/os";
import { useTooltip } from "@/scripts/use-tooltip";
import { isSignedIn } from "@/reactiveAccount";
import { isSignedIn } from "@/me";
const props = defineProps<{
reaction: string;

View file

@ -19,7 +19,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import type { entities } from "firefish-js";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import XReaction from "@/components/MkReactionsViewer.reaction.vue";
const props = defineProps<{
@ -30,7 +30,7 @@ const reactionsEl = ref<HTMLElement>();
const initialReactions = new Set(Object.keys(props.note.reactions));
const isMe = computed(() => isSignedIn && $i.id === props.note.userId);
const isMe = computed(() => isSignedIn && me.id === props.note.userId);
</script>
<style lang="scss" scoped>

View file

@ -27,7 +27,7 @@ import Ripple from "@/components/MkRipple.vue";
import XDetails from "@/components/MkUsersTooltip.vue";
import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { useTooltip } from "@/scripts/use-tooltip";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
@ -46,7 +46,7 @@ const buttonRef = ref<HTMLElement>();
const canRenote = computed(
() =>
["public", "home"].includes(props.note.visibility) ||
props.note.userId === $i.id,
props.note.userId === me.id,
);
useTooltip(buttonRef, async (showing) => {
@ -77,7 +77,7 @@ const hasRenotedBefore = ref(false);
if (isSignedIn) {
os.api("notes/renotes", {
noteId: props.note.id,
userId: $i.id,
userId: me.id,
limit: 1,
}).then((res) => {
hasRenotedBefore.value = res.length > 0;

View file

@ -52,7 +52,7 @@ export default defineComponent({
flag: true,
radio: "firefish",
mfm: `Hello world! This is an @example mention. BTW, you are @${
this.$i ? this.$i.username : "guest"
this.me ? this.me.username : "guest"
}.\nAlso, here is ${config.url} and [example link](${
config.url
}). for more details, see <https://firefish.dev/firefish/firefish>.\nAs you know #Firefish is open-source software.`,

View file

@ -130,9 +130,7 @@ const searchUsers = ref(
);
const searchRange = ref(
searchParams.has("since") || searchParams.has("until")
? `${searchParams.get("since") ?? ""}-${
searchParams.get("until") ?? ""
}`
? `${searchParams.get("since") ?? ""}-${searchParams.get("until") ?? ""}`
: "",
);
const searchPostsWithFiles = ref(searchParams.get("withFiles") === "1");

View file

@ -142,7 +142,7 @@ import MkInfo from "@/components/MkInfo.vue";
import { host as configHost } from "@/config";
import { byteify, hexify } from "@/scripts/2fa";
import * as os from "@/os";
import { login } from "@/account";
import { signIn } from "@/account";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -195,7 +195,7 @@ function onUsernameChange() {
function onLogin(res) {
if (props.autoSet) {
return login(res.i);
return signIn(res.i);
}
}

View file

@ -281,7 +281,7 @@ import MkSwitch from "./form/switch.vue";
import MkCaptcha from "@/components/MkCaptcha.vue";
import * as config from "@/config";
import * as os from "@/os";
import { login } from "@/account";
import { signIn } from "@/account";
import { instance } from "@/instance";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
@ -470,7 +470,7 @@ function onSubmit(): void {
emit("signup", res);
if (props.autoSet) {
login(res.i);
signIn(res.i);
}
});
}

View file

@ -31,7 +31,7 @@
:text="note.cw"
:author="note.user"
:lang="note.lang"
:i="$i"
:i="me"
:custom-emojis="note.emojis"
/>
</p>
@ -103,7 +103,7 @@
v-if="note.text"
:text="note.text"
:author="note.user"
:i="$i"
:i="me"
:lang="note.lang"
:custom-emojis="note.emojis"
/>

View file

@ -50,7 +50,7 @@ import XNotes from "@/components/MkNotes.vue";
import MkInfo from "@/components/MkInfo.vue";
import { useStream } from "@/stream";
import * as sound from "@/scripts/sound";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
@ -103,7 +103,7 @@ const prepend = (note) => {
emit("note");
if (props.sound) {
sound.play(isSignedIn && note.userId === $i.id ? "noteMy" : "note");
sound.play(isSignedIn && note.userId === me.id ? "noteMy" : "note");
}
};

View file

@ -211,7 +211,7 @@ import MkPushNotificationAllowButton from "@/components/MkPushNotificationAllowB
import FormSwitch from "@/components/form/switch.vue";
import { defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { isModerator } from "@/reactiveAccount";
import { isModerator } from "@/me";
import { instance } from "@/instance";
import icon from "@/scripts/icon";

View file

@ -8,7 +8,7 @@
:class="{ detailed }"
>
<span
v-if="isSignedIn && $i.id !== user.id && user.isFollowed"
v-if="isSignedIn && me.id !== user.id && user.isFollowed"
class="followed"
>{{ i18n.ts.followsYou }}</span
>
@ -34,7 +34,7 @@
class="mfm"
:text="user.description"
:author="user"
:i="$i"
:i="me"
:custom-emojis="user.emojis"
/>
<span v-else style="opacity: 0.7">{{
@ -56,7 +56,7 @@
<Mfm
:text="field.value"
:author="user"
:i="$i"
:i="me"
:custom-emojis="user.emojis"
:colored="false"
/>
@ -80,7 +80,7 @@
<div class="buttons">
<slot>
<MkFollowButton
v-if="isSignedIn && user.id !== $i.id"
v-if="isSignedIn && user.id !== me.id"
:user="user"
/>
</slot>
@ -97,7 +97,7 @@ import XShowMoreButton from "@/components/MkShowMoreButton.vue";
import MkNumber from "@/components/MkNumber.vue";
import { userPage } from "@/filters/user";
import { i18n } from "@/i18n";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
const props = defineProps<{
user: entities.UserDetailed;

View file

@ -20,10 +20,10 @@
<i :class="icon('ph-caret-left')"></i>
</button>
<MkAvatar
v-if="narrow && props.displayMyAvatar && $i"
v-if="narrow && props.displayMyAvatar && me"
v-vibrate="5"
class="avatar button"
:user="$i"
:user="me"
:disable-preview="true"
disable-link
@click.stop="openAccountMenu"
@ -139,7 +139,7 @@ import { popupMenu } from "@/os";
import { scrollToTop } from "@/scripts/scroll";
import { injectPageMetadata } from "@/scripts/page-metadata";
import { openAccountMenu as openAccountMenu_ } from "@/account";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";

View file

@ -27,14 +27,13 @@ export default defineComponent({
if (nextBracketOpen === -1) {
parsed.push(str);
break;
} else {
if (nextBracketOpen > 0) parsed.push(str.substr(0, nextBracketOpen));
parsed.push({
arg: str.substring(nextBracketOpen + 1, nextBracketClose),
});
}
if (nextBracketOpen > 0) parsed.push(str.substring(0, nextBracketOpen));
parsed.push({
arg: str.substring(nextBracketOpen + 1, nextBracketClose),
});
str = str.substr(nextBracketClose + 1);
str = str.substring(nextBracketClose + 1);
}
return h(

View file

@ -73,7 +73,7 @@ export default defineComponent({
method: "POST",
body: formData,
headers: {
authorization: `Bearer ${this.$i.token}`,
authorization: `Bearer ${this.me.token}`,
},
})
.then((response) => response.json())

View file

@ -1,6 +1,6 @@
<template>
<div class="mrdgzndn">
<Mfm :key="text" :text="text" :is-note="false" :i="$i" />
<Mfm :key="text" :text="text" :is-note="false" :i="me" />
<MkUrlPreview v-for="url in urls" :key="url" :url="url" class="url" />
</div>
</template>

View file

@ -21,7 +21,7 @@ import { Parser } from "@syuilo/aiscript";
import XBlock from "./page.block.vue";
import { Hpml } from "@/scripts/hpml/evaluator";
import { url } from "@/config";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { defaultStore } from "@/store";
export default defineComponent({
@ -37,7 +37,7 @@ export default defineComponent({
setup(props, ctx) {
const hpml = new Hpml(props.page, {
randomSeed: Math.random(),
visitor: $i,
visitor: me,
url,
enableAiScript: !defaultStore.state.disablePagesScript,
});

View file

@ -1,13 +1,36 @@
import { markRaw } from "vue";
import { locale } from "@/config";
import { I18n } from "@/scripts/i18n";
export const i18n = markRaw(new I18n(locale));
class I18n<T extends Record<string, any>> {
public ts: T;
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$t: (typeof i18n)["t"];
$ts: (typeof i18n)["locale"];
constructor(locale: T) {
this.ts = locale;
// #region BIND
this.t = this.t.bind(this);
// #endregion
}
// string にしているのは、ドット区切りでのパス指定を許可するため
// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
public t(key: string, args?: Record<string, string | number>): string {
try {
let str = key
.split(".")
.reduce((o, i) => o[i], this.ts) as unknown as string;
if (args) {
for (const [k, v] of Object.entries(args)) {
str = str.replace(`{${k}}`, v.toString());
}
}
return str;
} catch (err) {
console.warn(`missing localization '${key}'`);
return key;
}
}
}
export const i18n = markRaw(new I18n(locale));

View file

@ -32,14 +32,14 @@ import {
} from "vue";
import { set } from "@/scripts/idb-proxy";
import { login, refreshAccount, signout, updateAccount } from "@/account";
import { refreshAccount, signIn, signOut, updateAccount } from "@/account";
import components from "@/components";
import { lang, ui, version } from "@/config";
import directives from "@/directives";
import { i18n } from "@/i18n";
import { fetchInstance, instance } from "@/instance";
import { isSignedIn, me } from "@/me";
import { alert, api, confirm, popup, post, toast } from "@/os";
import { $i, isSignedIn } from "@/reactiveAccount";
import { compareFirefishVersions } from "@/scripts/compare-versions";
import { deviceKind } from "@/scripts/device-kind";
import { getAccountFromId } from "@/scripts/get-account-from-id";
@ -76,8 +76,6 @@ function checkForSplash() {
console.info(`vue ${vueVersion}`);
(window as any).$i = $i;
window.addEventListener("error", (event) => {
console.error(event);
/*
@ -132,10 +130,10 @@ function checkForSplash() {
if (loginId) {
const target = getUrlWithoutLoginId(location.href);
if (!$i || $i.id !== loginId) {
if (!me || me.id !== loginId) {
const account = await getAccountFromId(loginId);
if (account) {
await login(account.token, target);
await signIn(account.token, target);
}
}
@ -145,7 +143,7 @@ function checkForSplash() {
// #endregion
// #region Fetch user
if ($i?.token) {
if (me?.token) {
if (_DEV_) {
console.log("account cache found. refreshing...");
}
@ -166,7 +164,7 @@ function checkForSplash() {
try {
document.body.innerHTML = "<div>Please wait...</div>";
await login(i);
await signIn(i);
} catch (err) {
// Render the error screen
// TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな)
@ -192,7 +190,7 @@ function checkForSplash() {
const app = createApp(
window.location.search === "?zen"
? defineAsyncComponent(() => import("@/ui/zen.vue"))
: !$i
: !me
? defineAsyncComponent(() => import("@/ui/visitor.vue"))
: ui === "deck"
? defineAsyncComponent(() => import("@/ui/deck.vue"))
@ -203,11 +201,6 @@ function checkForSplash() {
app.config.performance = true;
}
app.config.globalProperties = {
$i,
$instance: instance,
};
widgets(app);
directives(app);
components(app);
@ -259,7 +252,7 @@ function checkForSplash() {
defaultStore.state.showUpdates
) {
// ログインしてる場合だけ
if ($i) {
if (me) {
popup(
defineAsyncComponent(() => import("@/components/MkUpdated.vue")),
{},
@ -433,7 +426,7 @@ function checkForSplash() {
// only add post shortcuts if logged in
hotkeys["p|n"] = post;
if ($i.isDeleted) {
if (me.isDeleted) {
alert({
type: "warning",
text: i18n.ts.accountDeletionInProgress,
@ -442,12 +435,12 @@ function checkForSplash() {
const lastUsed = localStorage.getItem("lastUsed");
if (lastUsed) {
const lastUsedDate = parseInt(lastUsed, 10);
const lastUsedDate = Number.parseInt(lastUsed, 10);
// 二時間以上前なら
if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) {
toast(
i18n.t("welcomeBackWithName", {
name: $i.name || $i.username,
name: me.name || me.username,
}),
);
}
@ -460,7 +453,7 @@ function checkForSplash() {
const neverShowDonationInfo = localStorage.getItem("neverShowDonationInfo");
if (
neverShowDonationInfo !== "true" &&
new Date($i.createdAt).getTime() < Date.now() - 1000 * 60 * 60 * 24 * 3 &&
new Date(me.createdAt).getTime() < Date.now() - 1000 * 60 * 60 * 24 * 3 &&
!location.pathname.startsWith("/miauth")
) {
if (
@ -549,7 +542,7 @@ function checkForSplash() {
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on("myTokenRegenerated", () => {
signout();
signOut();
});
}

View file

@ -46,10 +46,3 @@ export const emojiTags = computed(() => {
}
return Array.from(tags);
});
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$instance: typeof instance;
}
}

View file

@ -4,11 +4,11 @@ import type { Account } from "@/account";
const accountData = localStorage.getItem("account");
// TODO: 外部からはreadonlyに
export const $i = accountData
export const me = accountData
? reactive(JSON.parse(accountData) as Account)
: null;
export const isSignedIn = $i != null;
export const isModerator = $i != null && ($i.isModerator || $i.isAdmin);
export const isEmojiMod = isModerator || $i?.emojiModPerm !== "unauthorized";
export const isAdmin = $i?.isAdmin;
export const isSignedIn = me != null;
export const isModerator = me != null && (me.isModerator || me.isAdmin);
export const isEmojiMod = isModerator || me?.emojiModPerm !== "unauthorized";
export const isAdmin = me?.isAdmin;

View file

@ -1,8 +1,8 @@
import { computed, reactive } from "vue";
import { ui } from "@/config";
import { i18n } from "@/i18n";
import { isSignedIn, me } from "@/me";
import * as os from "@/os";
import { $i, isSignedIn } from "@/reactiveAccount";
import icon from "@/scripts/icon";
import { search } from "@/scripts/search";
import { unisonReload } from "@/scripts/unison-reload";
@ -12,14 +12,14 @@ export const navbarItemDef = reactive({
title: "notifications",
icon: `${icon("ph-bell")}`,
show: computed(() => isSignedIn),
indicated: computed(() => $i?.hasUnreadNotification),
indicated: computed(() => me?.hasUnreadNotification),
to: "/my/notifications",
},
messaging: {
title: "messaging",
icon: `${icon("ph-chats-teardrop")}`,
show: computed(() => isSignedIn),
indicated: computed(() => $i?.hasUnreadMessagingMessage),
indicated: computed(() => me?.hasUnreadMessagingMessage),
to: "/my/messaging",
},
drive: {
@ -31,8 +31,8 @@ export const navbarItemDef = reactive({
followRequests: {
title: "followRequests",
icon: `${icon("ph-hand-waving")}`,
show: computed(() => $i?.isLocked || $i?.hasPendingReceivedFollowRequest),
indicated: computed(() => $i?.hasPendingReceivedFollowRequest),
show: computed(() => me?.isLocked || me?.hasPendingReceivedFollowRequest),
indicated: computed(() => me?.hasPendingReceivedFollowRequest),
to: "/my/follow-requests",
},
explore: {
@ -43,7 +43,7 @@ export const navbarItemDef = reactive({
announcements: {
title: "announcements",
icon: `${icon("ph-megaphone-simple")}`,
indicated: computed(() => $i?.hasUnreadAnnouncement),
indicated: computed(() => me?.hasUnreadAnnouncement),
to: "/announcements",
},
search: {

View file

@ -11,7 +11,7 @@ import MkPostFormDialog from "@/components/MkPostFormDialog.vue";
import MkToast from "@/components/MkToast.vue";
import MkWaitingDialog from "@/components/MkWaitingDialog.vue";
import { apiUrl, url } from "@/config";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import type { MenuItem } from "@/types/menu";
export const pendingApiRequestsCount = ref(0);
@ -32,7 +32,7 @@ export const api = ((
pendingApiRequestsCount.value--;
};
const authorizationToken = token ?? $i?.token ?? undefined;
const authorizationToken = token ?? me?.token ?? undefined;
const authorization = authorizationToken
? `Bearer ${authorizationToken}`
: undefined;
@ -77,7 +77,7 @@ export const apiGet = ((
const query = new URLSearchParams(data);
const authorizationToken = token ?? $i?.token ?? undefined;
const authorizationToken = token ?? me?.token ?? undefined;
const authorization = authorizationToken
? `Bearer ${authorizationToken}`
: undefined;

View file

@ -35,7 +35,7 @@
><MkEmoji
class="emoji"
:emoji="emoji.emoji"
:custom-emojis="$instance.emojis"
:custom-emojis="instance.emojis"
:is-reaction="false"
:normal="true"
:no-style="true"
@ -96,6 +96,7 @@ import { defaultReactions, defaultStore } from "@/store";
import * as os from "@/os";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
import { instance } from "@/instance";
let easterEggReady = false;
const easterEggEmojis = ref([]);

View file

@ -54,9 +54,9 @@ import MkInput from "@/components/form/input.vue";
import MkSelect from "@/components/form/select.vue";
import MkFolder from "@/components/MkFolder.vue";
import MkTab from "@/components/MkTab.vue";
import { emojiCategories, emojiTags } from "@/instance";
import { emojiCategories, emojiTags, instance } from "@/instance";
import { i18n } from "@/i18n";
import icon from "@/scripts/icon";
import iconify from "@/scripts/icon";
export default defineComponent({
components: {
@ -72,7 +72,7 @@ export default defineComponent({
return {
q: "",
customEmojiCategories: emojiCategories,
customEmojis: this.$instance.emojis,
customEmojis: instance.emojis,
tags: emojiTags,
selectedTags: new Set(),
searchEmojis: null,
@ -127,6 +127,10 @@ export default defineComponent({
this.selectedTags.add(tag);
}
},
icon(name: string): string {
return iconify(name);
},
},
});
</script>

View file

@ -28,7 +28,7 @@
<div
class="_formBlock fwhjspax"
:style="{
backgroundImage: `url(${$instance.bannerUrl})`,
backgroundImage: `url(${instance.bannerUrl})`,
}"
>
<div class="content">
@ -41,7 +41,7 @@
@click="easterEgg"
/>
<div class="name">
<b>{{ $instance.name || host }}</b>
<b>{{ instance.name || host }}</b>
</div>
</div>
</div>
@ -49,7 +49,7 @@
<MkKeyValue class="_formBlock">
<template #key>{{ i18n.ts.description }}</template>
<template #value
><div v-html="$instance.description"></div
><div v-html="instance.description"></div
></template>
</MkKeyValue>
@ -70,7 +70,7 @@
i18n.ts.administrator
}}</template>
<template #value>{{
$instance.maintainerName
instance.maintainerName
}}</template>
</MkKeyValue>
<MkKeyValue class="_formBlock">
@ -78,20 +78,20 @@
i18n.ts.contact
}}</template>
<template #value>{{
$instance.maintainerEmail
instance.maintainerEmail
}}</template>
</MkKeyValue>
</FormSplit>
<FormLink
v-if="$instance.tosUrl"
:to="$instance.tosUrl"
v-if="instance.tosUrl"
:to="instance.tosUrl"
class="_formBlock"
external
>{{ i18n.ts.tos }}</FormLink
>
<FormLink
v-if="$instance.donationLink"
:to="$instance.donationLink"
v-if="instance.donationLink"
:to="instance.donationLink"
external
>
<template #icon
@ -99,7 +99,7 @@
></template>
{{
i18n.t("_aboutFirefish.donateHost", {
host: $instance.name || host,
host: instance.name || host,
})
}}
<template #suffix>Donate</template>
@ -190,7 +190,7 @@ import number from "@/filters/number";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import { deviceKind } from "@/scripts/device-kind";
import { isModerator } from "@/reactiveAccount";
import { isModerator } from "@/me";
import { instance } from "@/instance";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";

View file

@ -172,7 +172,7 @@ import * as os from "@/os";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import { deviceKind } from "@/scripts/device-kind";
import { isAdmin, isModerator } from "@/reactiveAccount";
import { isAdmin, isModerator } from "@/me";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
import "swiper/scss";

View file

@ -5,7 +5,7 @@
<div class="lxpfedzu">
<div class="banner">
<img
:src="$instance.iconUrl || '/favicon.ico'"
:src="instance.iconUrl || '/favicon.ico'"
alt=""
class="icon"
/>
@ -73,7 +73,7 @@ import MkSuperMenu from "@/components/MkSuperMenu.vue";
import MkInfo from "@/components/MkInfo.vue";
import { instance } from "@/instance";
import { version } from "@/config";
import { $i } from "@/reactiveAccount";
import { isAdmin, me } from "@/me";
import * as os from "@/os";
import { lookupUser } from "@/scripts/lookup-user";
import { lookupFile } from "@/scripts/lookup-file";
@ -214,7 +214,7 @@ const menuDef = computed(() => [
},
],
},
...($i?.isAdmin
...(isAdmin
? [
{
title: i18n.ts.settings,

View file

@ -59,7 +59,7 @@ import * as os from "@/os";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
import { isSignedIn } from "@/reactiveAccount";
import { isSignedIn } from "@/me";
const pagination = {
endpoint: "announcements" as const,

View file

@ -51,8 +51,8 @@ import XForm from "./auth.form.vue";
import MkSignin from "@/components/MkSignin.vue";
import MkKeyValue from "@/components/MkKeyValue.vue";
import * as os from "@/os";
import { login } from "@/account";
import { isSignedIn } from "@/reactiveAccount";
import { signIn } from "@/account";
import { isSignedIn } from "@/me";
import { i18n } from "@/i18n";
const props = defineProps<{
@ -132,6 +132,6 @@ const accepted = () => {
};
const onLogin = (res) => {
login(res.i);
signIn(res.i);
};
</script>

View file

@ -71,13 +71,13 @@
<Mfm
:text="channel.description"
:is-note="false"
:i="$i"
:i="me"
/>
</div>
</div>
<XPostForm
v-if="$i"
v-if="me"
:channel="channel"
class="post-form _panel _gap"
fixed
@ -103,7 +103,7 @@ import XTimeline from "@/components/MkTimeline.vue";
import XChannelFollowButton from "@/components/MkChannelFollowButton.vue";
import * as os from "@/os";
import { useRouter } from "@/router";
import { $i } from "@/reactiveAccount";
import { me } from "@/me";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
@ -132,7 +132,7 @@ function edit() {
}
const headerActions = computed(() => [
...(channel.value && channel.value?.userId === $i?.id
...(channel.value && channel.value?.userId === me?.id
? [
{
icon: `${icon("ph-gear-six")}`,

View file

@ -8,7 +8,7 @@
<Mfm
:text="clip.description"
:is-note="false"
:i="$i"
:i="me"
/>
</div>
<div class="user">
@ -31,7 +31,7 @@
import { computed, provide, ref, watch } from "vue";
import type { entities } from "firefish-js";
import XNotes from "@/components/MkNotes.vue";
import { $i, isSignedIn } from "@/reactiveAccount";
import { isSignedIn, me } from "@/me";
import { i18n } from "@/i18n";
import * as os from "@/os";
import { definePageMetadata } from "@/scripts/page-metadata";
@ -51,7 +51,7 @@ const pagination = {
};
const isOwned: boolean | null = computed<boolean | null>(
() => isSignedIn && clip.value && $i.id === clip.value.userId,
() => isSignedIn && clip.value && me.id === clip.value.userId,
);
watch(

View file

@ -146,7 +146,7 @@ import MkFolder from "@/components/MkFolder.vue";
import MkTab from "@/components/MkTab.vue";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { isSignedIn } from "@/reactiveAccount";
import { isSignedIn } from "@/me";
import icon from "@/scripts/icon";
const props = defineProps<{

Some files were not shown because too many files have changed in this diff Show more