Compare commits
59 Commits
13.14.0-be
...
revert-mod
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b6b300a12a | ||
![]() |
e5fce15ba5 | ||
![]() |
c59a9c6f84 | ||
![]() |
545b5cab96 | ||
![]() |
fe13755be8 | ||
![]() |
9208cfd5f3 | ||
![]() |
81ba841fb8 | ||
![]() |
0404d9c103 | ||
![]() |
c1a19ff900 | ||
![]() |
ac6a8edf0b | ||
![]() |
8851e90316 | ||
![]() |
fca6ae9403 | ||
![]() |
4f4f15a6a4 | ||
![]() |
7097d553e4 | ||
![]() |
b8973a6f19 | ||
![]() |
50de2e76b4 | ||
![]() |
40c5699d93 | ||
![]() |
a87dd5ddec | ||
![]() |
561683f097 | ||
![]() |
c9b9db13c7 | ||
![]() |
a8cd8ed99e | ||
![]() |
b3358f0a8b | ||
![]() |
af2368bd2b | ||
![]() |
2cb8e8a748 | ||
![]() |
c40f52b262 | ||
![]() |
2801946226 | ||
![]() |
0d2e3df061 | ||
![]() |
64c142b1ca | ||
![]() |
df2b61fcc6 | ||
![]() |
509e3f979e | ||
![]() |
b546d4108f | ||
![]() |
46f86a9146 | ||
![]() |
2191479c76 | ||
![]() |
0a06eb27da | ||
![]() |
47b684100d | ||
![]() |
7d6ea74288 | ||
![]() |
8e11a30c36 | ||
![]() |
5dc8f63a07 | ||
![]() |
e6fca72171 | ||
![]() |
d2c942348c | ||
![]() |
21277952ca | ||
![]() |
dc93a418c0 | ||
![]() |
51b02ef23e | ||
![]() |
32430a0dba | ||
![]() |
8e46fade07 | ||
![]() |
871027fa0c | ||
![]() |
2ddf575cdc | ||
![]() |
8a058876a3 | ||
![]() |
1df259a7b2 | ||
![]() |
1e40088d98 | ||
![]() |
5e53b41f2d | ||
![]() |
5f5c4fc44f | ||
![]() |
ad9bb773c5 | ||
![]() |
59934f9767 | ||
![]() |
bf9e74ca05 | ||
![]() |
5280a5e5c6 | ||
![]() |
8c77153c04 | ||
![]() |
c50b952ff9 | ||
![]() |
b0b0ecb857 |
@@ -82,6 +82,8 @@ redis:
|
||||
#pass: example-pass
|
||||
#prefix: example-prefix
|
||||
#db: 1
|
||||
# You can specify more ioredis options...
|
||||
#username: example-username
|
||||
|
||||
#redisForPubsub:
|
||||
# host: localhost
|
||||
@@ -90,6 +92,8 @@ redis:
|
||||
# #pass: example-pass
|
||||
# #prefix: example-prefix
|
||||
# #db: 1
|
||||
# # You can specify more ioredis options...
|
||||
# #username: example-username
|
||||
|
||||
#redisForJobQueue:
|
||||
# host: localhost
|
||||
@@ -98,6 +102,8 @@ redis:
|
||||
# #pass: example-pass
|
||||
# #prefix: example-prefix
|
||||
# #db: 1
|
||||
# # You can specify more ioredis options...
|
||||
# #username: example-username
|
||||
|
||||
# ┌───────────────────────────┐
|
||||
#───┘ MeiliSearch configuration └─────────────────────────────
|
||||
|
32
CHANGELOG.md
32
CHANGELOG.md
@@ -8,24 +8,38 @@
|
||||
-
|
||||
|
||||
### Server
|
||||
-
|
||||
- Fix: 外部サーバーの投稿がタイムラインに表示されないことがある問題を修正
|
||||
|
||||
-->
|
||||
|
||||
## 13.x.x (unreleased)
|
||||
|
||||
### General
|
||||
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
|
||||
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
|
||||
-
|
||||
|
||||
### Client
|
||||
- リストTLで、ユーザーが追加・削除されてもTLを初期化しないように
|
||||
- Fix: モバイル表示のときページ下部がナビゲーションバーに隠れる問題を修正
|
||||
- Fix: Selecting all emojis in Custom emoji is impossible
|
||||
|
||||
### Server
|
||||
- Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正
|
||||
|
||||
## 13.14.1
|
||||
|
||||
### General
|
||||
- 招待機能を改善しました
|
||||
* 過去に発行した招待コードを確認できるようになりました
|
||||
* ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました
|
||||
* 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました
|
||||
- ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるようになりました
|
||||
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
|
||||
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
|
||||
|
||||
### Client
|
||||
- deck UIのカラムのメニューからアンテナとリストの編集画面を開けるように
|
||||
- ドライブファイルのメニューで画像をクロップできるように
|
||||
- 画像を動画と同様に簡単に隠せるように
|
||||
- Enhance: ノートの埋め込みが複数画像と動画を表示されるように
|
||||
- オリジナル画像を保持せずにアップロードする場合webpでアップロードされるように(Safari以外)
|
||||
- 見たことのあるRenoteを省略して表示をオンのときに自分のnoteのrenoteを省略するように
|
||||
- フォルダーやファイルに対しても開発者モード使用時、IDをコピーできるように
|
||||
@@ -41,6 +55,9 @@
|
||||
- ロール設定画面でロールIDを確認できるように
|
||||
- コンテキストメニュー表示時のパフォーマンスを改善
|
||||
- フォロー/フォロワー非公開時の表示を改善
|
||||
- 本文にMFMが含まれている場合に自動でたたまれる機能が、返信先や引用RNにも適用されるように
|
||||
- position は対象外になりました
|
||||
- AiScriptを0.15.0に更新
|
||||
- Fix: サーバーメトリクスが90度傾いている
|
||||
- Fix: 非ログイン時にクレデンシャルが必要なページに行くとエラーが出る問題を修正
|
||||
- Fix: sparkle内にリンクを入れるとクリック不能になる問題の修正
|
||||
@@ -57,14 +74,19 @@
|
||||
- nsfwjs のモデルロードを排他することで、重複ロードによってメモリ使用量が増加しないように
|
||||
- 連合の配送ジョブのパフォーマンスを向上(ロック機構の見直し、Redisキャッシュの活用)
|
||||
- featuredノートのsignedGet回数を減らしました
|
||||
- リモートサーバーからのNSFW映像のキャッシュだけを無効化できるオプションを追加
|
||||
- ActivityPubの署名用鍵長を2048bitに変更しパフォーマンスを向上(新規アカウントのみ)
|
||||
- リモートサーバーのセンシティブなファイルのキャッシュだけを無効化できるオプションを追加
|
||||
- MeilisearchにIndexするノートの範囲を設定できるように
|
||||
- Export notes with file detail
|
||||
- Add unix socket support
|
||||
- 設定ファイルでioredisの全てのオプションを指定可能に
|
||||
- Fix: エクスポートしたカスタム絵文字のzipが大きいと読み込めない問題を修正
|
||||
- Fix: リモートサーバーに無意味なActivityPubの配信を行うことがあるのを修正
|
||||
- Fix: Remove Meilisearch index when notes are deleted
|
||||
- Fix: 非英語環境でのPostgreSQLのエラーハンドリングを修正
|
||||
- Fix: インスタンスのアイコンがbase64の場合の挙動を修正
|
||||
- Fix: ローカルの `Person` を指す `acct` URI を解析するときのバグを修正しました
|
||||
- Fix: 無効化されたアンテナが再度有効化されないことがある問題を修正
|
||||
|
||||
## 13.13.2
|
||||
|
||||
|
@@ -214,30 +214,13 @@ Misskey uses [Storybook](https://storybook.js.org/) for UI development.
|
||||
|
||||
### Setup & Run
|
||||
|
||||
#### Universal
|
||||
|
||||
##### Setup
|
||||
|
||||
```bash
|
||||
pnpm --filter misskey-js build
|
||||
pnpm --filter frontend tsc -p .storybook && (node packages/frontend/.storybook/preload-locale.js & node packages/frontend/.storybook/preload-theme.js)
|
||||
```
|
||||
|
||||
##### Run
|
||||
|
||||
```bash
|
||||
node packages/frontend/.storybook/generate.js && pnpm --filter frontend storybook dev
|
||||
```
|
||||
|
||||
#### macOS & Linux
|
||||
|
||||
##### Setup
|
||||
#### Setup
|
||||
|
||||
```bash
|
||||
pnpm --filter misskey-js build
|
||||
```
|
||||
|
||||
##### Run
|
||||
#### Run
|
||||
|
||||
```bash
|
||||
pnpm --filter frontend storybook-dev
|
||||
|
@@ -54,6 +54,7 @@ describe('After setup instance', () => {
|
||||
cy.get('[data-cy-signup]').click();
|
||||
cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
|
||||
cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
|
||||
cy.get('[data-cy-modal-dialog-ok]').click();
|
||||
cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
|
||||
cy.get('[data-cy-signup-rules-continue]').click();
|
||||
|
||||
@@ -78,6 +79,7 @@ describe('After setup instance', () => {
|
||||
cy.get('[data-cy-signup]').click();
|
||||
cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
|
||||
cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
|
||||
cy.get('[data-cy-modal-dialog-ok]').click();
|
||||
cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
|
||||
cy.get('[data-cy-signup-rules-continue]').click();
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
_lang_: "Čeština"
|
||||
headlineMisskey: "Síť propojená poznámkami"
|
||||
introMisskey: "Vítejte! Misskey je otevřený a decentralizovaný microblogový servis.\n\"Poznámkami\" můžete sdílet co se zrovna děje se všemi ve Vašem okolí. 📡\nPomocí \"reakcí\" můžete sdílet své názory a pocity na ostatní poznámky. 👍\nPojďte objevovat nový svět! 🚀"
|
||||
poweredByMisskeyDescription: "{name} je jeden ze serverů využívající open source platformu <b>Misskey<b> (nazývaná \"Misskey instance\")."
|
||||
monthAndDay: "{day}. {month}."
|
||||
search: "Vyhledávání"
|
||||
notifications: "Oznámení"
|
||||
@@ -48,8 +49,15 @@ delete: "Smazat"
|
||||
deleteAndEdit: "Smazat a upravit"
|
||||
deleteAndEditConfirm: "Jste si jistí že chcete smazat tuto poznámku a editovat ji? Ztratíte tím všechny reakce, sdílení a odpovědi na ni."
|
||||
addToList: "Přidat do seznamu"
|
||||
addToAntenna: "Přidat do antény"
|
||||
sendMessage: "Odeslat zprávu"
|
||||
copyRSS: "Kopírovat RSS"
|
||||
copyUsername: "Kopírovat uživatelské jméno"
|
||||
copyUserId: "Kopírovat ID uživatele"
|
||||
copyNoteId: "Kopírovat ID poznámky"
|
||||
copyFileId: "Kopírovat ID souboru"
|
||||
copyFolderId: "Kopírovat ID složky"
|
||||
copyProfileUrl: "Kopírovat URL profilu"
|
||||
searchUser: "Vyhledat uživatele"
|
||||
reply: "Odpovědět"
|
||||
loadMore: "Zobrazit více"
|
||||
@@ -60,6 +68,7 @@ receiveFollowRequest: "Žádost o sledování přijata"
|
||||
followRequestAccepted: "Žádost o sledování přijata"
|
||||
mention: "Zmínění"
|
||||
mentions: "Zmínění"
|
||||
directNotes: "Přímé poznámky"
|
||||
importAndExport: "Import a export"
|
||||
import: "Importovat"
|
||||
export: "Exportovat"
|
||||
@@ -82,6 +91,7 @@ error: "Chyba"
|
||||
somethingHappened: "Jejda. Něco se nepovedlo."
|
||||
retry: "Opakovat"
|
||||
pageLoadError: "Nepodařilo se načíst stránku"
|
||||
pageLoadErrorDescription: "Tohle je obvykle způsobeno chybou sítě nebo mezipaměti prohlížeče. Zkuste vymazat mezipaměť a po chvíli čekání to zkuste znovu."
|
||||
serverIsDead: "Server neodpovídá. Počkejte chvíli a zkuste to znovu."
|
||||
youShouldUpgradeClient: "Pro zobrazení této stránky obnovte stránku pro aktualizaci klienta."
|
||||
enterListName: "Jméno seznamu"
|
||||
@@ -100,6 +110,8 @@ renoted: "Přeposláno"
|
||||
cantRenote: "Tento příspěvek nelze přeposlat."
|
||||
cantReRenote: "Odpověď nemůže být odstraněna."
|
||||
quote: "Citovat"
|
||||
inChannelRenote: "Přeposlání v kanálu"
|
||||
inChannelQuote: "Citace v kanálu"
|
||||
pinnedNote: "Připnutá poznámka"
|
||||
pinned: "Připnout"
|
||||
you: "Vy"
|
||||
@@ -108,6 +120,7 @@ sensitive: "NSFW"
|
||||
add: "Přidat"
|
||||
reaction: "Reakce"
|
||||
reactions: "Reakce"
|
||||
reactionSetting: "Reakce zobrazené ve výběru reakcí"
|
||||
reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání"
|
||||
rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky"
|
||||
attachCancel: "Odstranit přílohu"
|
||||
@@ -116,6 +129,8 @@ unmarkAsSensitive: "Odznačit jako NSFW"
|
||||
enterFileName: "Zadejte název souboru"
|
||||
mute: "Ztlumit"
|
||||
unmute: "Odmlčet"
|
||||
renoteMute: "Ztlumit poznámky"
|
||||
renoteUnmute: "Zrušit ztlumení poznámek"
|
||||
block: "Zablokovat"
|
||||
unblock: "Odblokovat"
|
||||
suspend: "Zmrazit"
|
||||
@@ -125,7 +140,10 @@ unblockConfirm: "Jste si jistí že chcete odblokovat tento účet?"
|
||||
suspendConfirm: "Jste si jistí že chcete suspendovat tenhle účet?"
|
||||
unsuspendConfirm: "Jste si jistí že chcete obnovit tenhle účet?"
|
||||
selectList: "Vybrat seznam"
|
||||
editList: "Upravit seznam"
|
||||
selectChannel: "Vybrat kanál"
|
||||
selectAntenna: "Vyberte Anténu"
|
||||
editAntenna: "Upravit anténu"
|
||||
selectWidget: "Zvolte widget"
|
||||
editWidgets: "Upravit widget"
|
||||
editWidgetsExit: "Hotovo"
|
||||
@@ -138,6 +156,8 @@ addEmoji: "Přidat emoji"
|
||||
settingGuide: "Doporučené nastavení"
|
||||
cacheRemoteFiles: "Ukládání vzdálených souborů do mezipaměti"
|
||||
cacheRemoteFilesDescription: "Zakázání tohoto nastavení způsobí, že vzdálené soubory budou odkazovány přímo, místo aby byly ukládány do mezipaměti. Tím se ušetří úložiště na serveru, ale zvýší se provoz, protože se negenerují miniatury."
|
||||
cacheRemoteSensitiveFiles: "Uložit do mezipaměti vzdálené citlivé soubory"
|
||||
cacheRemoteSensitiveFilesDescription: "Když je tohle nastavení zrušeno, tak jsou vzdálené citlivé soubory načítány přímo ze vzdálených instancí bez uložení do mezipaměti."
|
||||
flagAsBot: "Tento účet je bot"
|
||||
flagAsBotDescription: "Pokud je tento účet kontrolován programem zaškrtněte tuto možnost. To označí tento účet jako bot pro ostatní vývojáře a zabrání tak nekonečným interakcím s ostatními boty a upraví Misskey systém aby se choval k tomuhle účtu jako bot."
|
||||
flagAsCat: "Tenhle účet je kočka"
|
||||
@@ -146,6 +166,7 @@ flagShowTimelineReplies: "Zobrazovat odpovědi na časové ose"
|
||||
flagShowTimelineRepliesDescription: "Je-li zapnuto, zobrazí odpovědi uživatelů na poznámky jiných uživatelů na vaší časové ose."
|
||||
autoAcceptFollowed: "Automaticky akceptovat následování od účtů které sledujete"
|
||||
addAccount: "Přidat účet"
|
||||
reloadAccountsList: "Obnovit list účtů"
|
||||
loginFailed: "Přihlášení se nezdařilo."
|
||||
showOnRemote: "Více na původním profilu"
|
||||
general: "Obecně"
|
||||
@@ -186,17 +207,26 @@ instanceInfo: "Informace o instanci"
|
||||
statistics: "Statistiky"
|
||||
clearQueue: "Vyčistit frontu"
|
||||
clearQueueConfirmTitle: "Jste si jisti že zrušit všechny úlohy ve frontě?"
|
||||
clearQueueConfirmText: "Jakékoliv nedoručené poznámky ve frontě nebudou sdružovány. Většinou tahle operace není zapotřebí."
|
||||
clearCachedFiles: "Vyprázdnit mezipaměť"
|
||||
clearCachedFilesConfirm: "Jste jistí že chcete smazat všechny vzdálené soubory v mezipaměti?"
|
||||
blockedInstances: "Blokované instance"
|
||||
blockedInstancesDescription: "Vypište názvy hostitelů instancí, které chcete blokovat odděleně řádkovými zlomky. Uvedené instance již nebudou moci s touto instancí komunikovat."
|
||||
muteAndBlock: "Ztlumení a blokování"
|
||||
mutedUsers: "Zltumení uživatelé"
|
||||
blockedUsers: "Blokovaní uživatelé"
|
||||
noUsers: "Žádní uživatelé"
|
||||
editProfile: "Upravit můj profil"
|
||||
noteDeleteConfirm: "Jste si jistí že chcete smazat tuhle poznámku?"
|
||||
pinLimitExceeded: "Nemůžete připnout další poznámky."
|
||||
intro: "Instalace Misskey byla dokončena! Prosím vytvořte admina."
|
||||
done: "Hotovo"
|
||||
processing: "Zpracovávám"
|
||||
preview: "Náhled"
|
||||
default: "Výchozí"
|
||||
defaultValueIs: "Základní hodnota: {value}"
|
||||
noCustomEmojis: "Bez Emoji"
|
||||
federating: "Sdružování"
|
||||
blocked: "Blokováno"
|
||||
suspended: "Suspendováno"
|
||||
all: "Vše"
|
||||
@@ -217,6 +247,7 @@ more: "Více!"
|
||||
featured: "Oblíbené poznámky"
|
||||
usernameOrUserId: "Uživatelské jméno nebo uživatelské id"
|
||||
noSuchUser: "Uživatel nebyl nalezen"
|
||||
lookup: "Vyhledat"
|
||||
announcements: "Oznámení"
|
||||
imageUrl: "URL obrázku"
|
||||
remove: "Smazat"
|
||||
@@ -227,10 +258,13 @@ resetAreYouSure: "Opravdu resetovat?"
|
||||
saved: "Uloženo"
|
||||
messaging: "Zprávy"
|
||||
upload: "Nahrát soubory"
|
||||
keepOriginalUploading: "Ponechat originální obrázek"
|
||||
keepOriginalUploadingDescription: "Uloží původní nahraný obrázek jak je. Pokud je to vypnuté, vygeneruje se zobrazení verze na webu při nahrátí."
|
||||
fromDrive: "Z disku"
|
||||
fromUrl: "Z URL"
|
||||
uploadFromUrl: "Nahrát z URL adresy"
|
||||
uploadFromUrlDescription: "URL adresa souboru, který chcete nahrát"
|
||||
uploadFromUrlRequested: "Upload zažádán"
|
||||
uploadFromUrlMayTakeTime: "Může trvat nějakou dobu, dokud nebude dokončeno nahrávání."
|
||||
explore: "Objevovat"
|
||||
messageRead: "Přečtené"
|
||||
@@ -238,6 +272,10 @@ noMoreHistory: "To je vše"
|
||||
startMessaging: "Zahájit chat"
|
||||
nUsersRead: "přečteno {n} uživateli"
|
||||
agreeTo: "Souhlasím s {0}"
|
||||
agree: "Souhlasím"
|
||||
agreeBelow: "Souhlasím s následným"
|
||||
basicNotesBeforeCreateAccount: "Důležité poznámky"
|
||||
termsOfService: "Podmínky užívání"
|
||||
start: "Začít"
|
||||
home: "Domů"
|
||||
remoteUserCaution: "Tyto informace nemusí být aktuální jelikož uživatel je ze vzdálené instance."
|
||||
@@ -268,17 +306,24 @@ createFolder: "Vytvořit složku"
|
||||
renameFolder: "Přejmenovat složku"
|
||||
deleteFolder: "Odstranit složku"
|
||||
addFile: "Přidat soubor"
|
||||
emptyDrive: "Váš disk je prázdný"
|
||||
emptyFolder: "Tato složka je prázdná"
|
||||
unableToDelete: "Nelze smazat"
|
||||
inputNewFileName: "Zadejte nový název"
|
||||
inputNewDescription: "Zadejte nový popisek"
|
||||
inputNewFolderName: "Zadejte název nové složky"
|
||||
circularReferenceFolder: "Koncová složka je podsložka složky, kterou chcete přesunout."
|
||||
hasChildFilesOrFolders: "Nemůžete odstranit složku, která není prázdná."
|
||||
copyUrl: "Kopírovat URL"
|
||||
rename: "Přejmenovat"
|
||||
avatar: "Avatar"
|
||||
banner: "Baner"
|
||||
displayOfSensitiveMedia: "Zobrazit citlivé média"
|
||||
whenServerDisconnected: "Když ztratíte spojení se serverem"
|
||||
disconnectedFromServer: "Spojení bylo přerušeno"
|
||||
reload: "Aktualizovat"
|
||||
doNothing: "Ignorovat"
|
||||
reloadConfirm: "Chcete obnovit časovou osu?"
|
||||
watch: "Sledovat"
|
||||
unwatch: "Přestat sledovat"
|
||||
accept: "Souhlasím"
|
||||
@@ -301,15 +346,22 @@ connectService: "Připojit"
|
||||
disconnectService: "Odpojit"
|
||||
enableLocalTimeline: "Povolit lokální čas"
|
||||
enableGlobalTimeline: "Povolit globální čas"
|
||||
disablingTimelinesInfo: "Administrátoři a Moderátoři budou mít stálý přístup ke všem časovým osám i přes to že nejsou zapnuté."
|
||||
registration: "Registrace"
|
||||
enableRegistration: "Povolit registraci novým uživatelům"
|
||||
invite: "Pozvat"
|
||||
driveCapacityPerLocalAccount: "Kapacita disku na lokálního uživatele"
|
||||
driveCapacityPerRemoteAccount: "Kapacita disku na vzdáleného uživatele"
|
||||
inMb: "V megabajtech"
|
||||
iconUrl: "Favicon URL"
|
||||
bannerUrl: "Baner URL"
|
||||
backgroundImageUrl: "Adresa URL obrázku pozadí"
|
||||
basicInfo: "Základní informace"
|
||||
pinnedUsers: "Připnutí uživatelé"
|
||||
pinnedUsersDescription: "Seznam uživatelských přezdívek oddělených řádkami bude připnutý v záložce \"Objevit\"."
|
||||
pinnedPages: "Připnutý stránky"
|
||||
pinnedPagesDescription: "Zadejte cesty stránek oddělené řádkami, které si přejete mít přípnutý na vrcholu téhle instance."
|
||||
pinnedClipId: "ID připnutého klipu"
|
||||
pinnedNotes: "Připnutá poznámka"
|
||||
hcaptcha: "hCaptcha"
|
||||
enableHcaptcha: "Aktivovat hCaptchu"
|
||||
@@ -319,30 +371,56 @@ recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Zapnout ReCAPTCHu"
|
||||
recaptchaSiteKey: "Klíč stránky"
|
||||
recaptchaSecretKey: "Tajný Klíč (Secret Key)"
|
||||
turnstile: "Turnstile"
|
||||
enableTurnstile: "Povolit Turnstile"
|
||||
turnstileSiteKey: "Klíč stránky"
|
||||
turnstileSecretKey: "Tajný Klíč (Secret Key)"
|
||||
avoidMultiCaptchaConfirm: "Používání několik Captcha systému může způsobit konflikt mezi nimi. Chtěli byste vypnout ostatní aktivní Captcha systémy? Pokud je chcete nechat zapnuté, stiskněte zrušit."
|
||||
antennas: "Antény"
|
||||
manageAntennas: "Spravovat Antény"
|
||||
name: "Jméno"
|
||||
antennaSource: "Zdroj Antény"
|
||||
antennaKeywords: "Klíčová slova na poslech"
|
||||
antennaExcludeKeywords: "Vyloučená klíčová slova"
|
||||
antennaKeywordsDescription: "Oddělte mezerami pro AND kondice nebo řádkami pro OR kondice."
|
||||
notifyAntenna: "Upozornit na nové poznámky"
|
||||
withFileAntenna: "Poznámky jenom se souborama"
|
||||
enableServiceworker: "Povolit ServiceWorker"
|
||||
antennaUsersDescription: "Vypsat jednoho uživatele na řádek"
|
||||
caseSensitive: "Rozlišuje malá a velká písmena"
|
||||
withReplies: "Zahrnout odpovědi"
|
||||
connectedTo: "Následující účty jsou připojeny"
|
||||
notesAndReplies: "Poznámky a odpovědi"
|
||||
withFiles: "Včetně souborů"
|
||||
silence: "Ztlumení"
|
||||
silenceConfirm: "Jste si jistí že chcete ztlumit tohoto uživatele?"
|
||||
unsilence: "Zrušit ztlumení"
|
||||
unsilenceConfirm: "Jste jistí že chcete vrátit zltumení tohoto uživatele?"
|
||||
popularUsers: "Populární uživatelé"
|
||||
recentlyUpdatedUsers: "Nedávno aktívni uživatelé"
|
||||
recentlyRegisteredUsers: "Nově připojený uživatelé"
|
||||
recentlyDiscoveredUsers: "Nově objevený uživatelé"
|
||||
exploreUsersCount: "Existuje {count} uživatelů"
|
||||
exploreFediverse: "Objevovat Fediverse"
|
||||
popularTags: "Populární tagy"
|
||||
userList: "Seznamy"
|
||||
about: "Informace"
|
||||
aboutMisskey: "O Misskey"
|
||||
administrator: "Administrátor"
|
||||
token: "Token"
|
||||
2fa: "Dvoufázové ověření"
|
||||
totp: "Ověřovací aplikace"
|
||||
totpDescription: "Použít ověřovací aplikaci pro použití jednorázových hesel"
|
||||
moderator: "Moderátor"
|
||||
moderation: "Moderování"
|
||||
nUsersMentioned: "{n} uživatelů zmínilo"
|
||||
securityKeyAndPasskey: "Bezpečnostní klíče a tokeny"
|
||||
securityKey: "Bezpečnostní klíč"
|
||||
lastUsed: "Naposledy použito"
|
||||
lastUsedAt: "Naposledy použito: {t}"
|
||||
unregister: "Odstranit"
|
||||
passwordLessLogin: "Přihlášení bez hesla"
|
||||
passwordLessLoginDescription: "Umožní bez-heslové přihlášení pomocí bezpečnostního klíče či tokenu"
|
||||
resetPassword: "Resetovat heslo"
|
||||
newPasswordIs: "Nové heslo je \"{password}\""
|
||||
reduceUiAnimation: "Snížit UI animace"
|
||||
@@ -391,14 +469,25 @@ or: "Nebo"
|
||||
language: "Jazyk"
|
||||
uiLanguage: "Jazyk uživatelského rozhraní"
|
||||
aboutX: "O {x}"
|
||||
emojiStyle: "Styl emoji"
|
||||
native: "Výchozí"
|
||||
disableDrawer: "Nepoužívat šuplíkové menu"
|
||||
showNoteActionsOnlyHover: "Zobrazit akce poznámky jenom při naběhnutí myši"
|
||||
noHistory: "Žádná historie"
|
||||
signinHistory: "Historie přihlášení"
|
||||
enableAdvancedMfm: "Zapnout pokročilé MFM"
|
||||
enableAnimatedMfm: "Zapnout animované MFM"
|
||||
doing: "Procesuju..."
|
||||
category: "Kategorie"
|
||||
tags: "Štítky"
|
||||
docSource: "Zdroj tohoto dokumentu"
|
||||
createAccount: "Vytvořit účet"
|
||||
existingAccount: "Existující účet"
|
||||
regenerate: "Obnovit"
|
||||
fontSize: "Velikost písma"
|
||||
mediaListWithOneImageAppearance: "Výška seznamu médií s jedním obrázkem"
|
||||
limitTo: "Omezeno na {x}"
|
||||
noFollowRequests: "Nemáte žádné žádosti o sledování"
|
||||
openImageInNewTab: "Otevřít obrázek v novém panelu"
|
||||
dashboard: "Přehled"
|
||||
local: "Lokální"
|
||||
@@ -412,15 +501,35 @@ accountSettings: "Nastavení účtu"
|
||||
promotion: "Propagace"
|
||||
promote: "Propagovat"
|
||||
numberOfDays: "Počet dní"
|
||||
hideThisNote: "Skrýt tuto poznámku"
|
||||
showFeaturedNotesInTimeline: "Zobrazit významné poznámky v časové ose"
|
||||
objectStorage: "Úložiště objektů"
|
||||
useObjectStorage: "Použít úložiště objektů"
|
||||
objectStorageBaseUrl: "Base URL"
|
||||
objectStorageBaseUrlDesc: "URL použitá jako reference. Upřesněte URL vlastní CDN nebo Proxy pokud používáte jeden z nich. Pro S3 použijte 'https://<bucket>.s3.amazonaws.com' a pro GCS nebo ekvivalentní služby použijte 'https://storage.googleapis.com/<bucket>', apd."
|
||||
objectStorageBucket: "Bucket"
|
||||
objectStorageBucketDesc: "Prosím upřesněte název bucketu používaný poskytovatelem."
|
||||
objectStoragePrefix: "Předpona"
|
||||
objectStoragePrefixDesc: "Soubory budou ukládány pod složkama s tímhle prefixem."
|
||||
objectStorageEndpoint: "Endpoint"
|
||||
objectStorageEndpointDesc: "Ponechte tohle prázdné pokud používáte AWS S3, jinak upřesněte endpoint jako \"<host>\" nebo \"<host>:<port>\", podle toho jakou službu používáte."
|
||||
objectStorageRegion: "Región"
|
||||
objectStorageRegionDesc: "Upřesněte region jako například \"xx-east-1\". Pokud vlastní služba nerozlišuje mezi regiony, zadejte \"us-east-1\". Zanechte prázdné pokud používáte AWS konfiguraci či proměnné veličiny."
|
||||
objectStorageUseSSL: "Použít SSL"
|
||||
objectStorageUseSSLDesc: "Vypněte to pokud nebudete používat HTTPS pro API připojení"
|
||||
objectStorageUseProxy: "Připojení skrze Proxy"
|
||||
objectStorageUseProxyDesc: "Vypněte to pokud nebudete používat Proxy pro API připojení."
|
||||
objectStorageSetPublicRead: "Při nahrátí nastavit na \"public-read\""
|
||||
s3ForcePathStyleDesc: "Pokud je povolena funkce s3ForcePathStyle, musí být název Bucketu zahrnut do cesty k adrese URL, nikoli do názvu hostitele adresy URL. Toto nastavení může být nutné povolit při používání služeb, jako je například samostatně hostovaná instance Minio."
|
||||
serverLogs: "Logy serveru"
|
||||
deleteAll: "Smazat vše"
|
||||
showFixedPostForm: "Zobrazit formulář pro nové příspěvky nad časovou osou"
|
||||
showFixedPostFormInChannel: "Zobrazit vkládací formulář na vrcholu časové osy (Kanály)"
|
||||
newNoteRecived: "Jsou k dispozici nové poznámky"
|
||||
sounds: "Zvuky"
|
||||
sound: "Zvuky"
|
||||
listen: "Poslouchat"
|
||||
none: "Žádný"
|
||||
showInPage: "Zobrazit na stránce"
|
||||
popout: "Pop-out"
|
||||
volume: "Hlasitost"
|
||||
@@ -433,29 +542,61 @@ install: "Nainstalovat"
|
||||
uninstall: "Odinstalovat"
|
||||
installedApps: "Autorizované aplikace"
|
||||
nothing: "Nic nebylo nalezeno"
|
||||
installedDate: "Datum autorizace"
|
||||
lastUsedDate: "Poslední použití"
|
||||
state: "Stav"
|
||||
sort: "Seřadit"
|
||||
ascendingOrder: "Vzestupně"
|
||||
descendingOrder: "Sestupně"
|
||||
scratchpad: "Zápisník"
|
||||
scratchpadDescription: "Scratchpad poskytuje rozhraní pro AiScript experimenty. Můžete psát, spustit či zkontrolovat výsledky jeho interakce s Misskey."
|
||||
output: "Výstup"
|
||||
script: "Skript"
|
||||
disablePagesScript: "Vypnout AiScript na stránkách"
|
||||
updateRemoteUser: "Aktualizovat informace o vzdáleném účtu"
|
||||
deleteAllFiles: "Smazat všechny soubory"
|
||||
deleteAllFilesConfirm: "Jste si jistí že chcete smazat všechny soubory?"
|
||||
removeAllFollowing: "Přestat sledovat všechny sledované uživatele"
|
||||
removeAllFollowingDescription: "Spuštěním přestanete sledovat všechny účty z {host}. Prosíme spustěte tohle v případě že instance už neexistuje. "
|
||||
userSuspended: "Tomuto uživateli byl pozastaven účet."
|
||||
userSilenced: "Tenhle uživatel je umlčen."
|
||||
yourAccountSuspendedTitle: "Tenhle účet je zmrazený"
|
||||
yourAccountSuspendedDescription: "Tenhle účet byl zmrazen z důvodu porušení smluvní podmínky serveru. Pro přesnější informace kontaktujte administrátora. Prosíme nezakládejte si nový účet."
|
||||
tokenRevoked: "Nesprávný token"
|
||||
tokenRevokedDescription: "Tenhle token vyprchal. Prosíme přihlašte se znova."
|
||||
accountDeleted: "Účet smazán"
|
||||
accountDeletedDescription: "Tenhle účet byl smazán."
|
||||
menu: "Menu"
|
||||
divider: "Dělící čára"
|
||||
addItem: "Přidat položku"
|
||||
rearrange: "Přeřadit"
|
||||
relays: "Relay"
|
||||
addRelay: "Přidat Relay"
|
||||
inboxUrl: "Inbox URL"
|
||||
addedRelays: "Přidané přenosy"
|
||||
serviceworkerInfo: "Musí být zapnut pro push notifikace."
|
||||
deletedNote: "Odstraněné příspěvky"
|
||||
invisibleNote: "Skryté příspěvky"
|
||||
enableInfiniteScroll: "Automaticky načítat více"
|
||||
visibility: "Viditelnost"
|
||||
poll: "Anketa"
|
||||
useCw: "Schovat obsah"
|
||||
enablePlayer: "Otevřít video přehrávač"
|
||||
disablePlayer: "Zavřít video přehrávač"
|
||||
expandTweet: "Rozbalit tweet"
|
||||
themeEditor: "Editor témat"
|
||||
description: "Popis"
|
||||
describeFile: "Přidat popisek"
|
||||
enterFileDescription: "Vložit popisek"
|
||||
author: "Autor"
|
||||
leaveConfirm: "Máte neuložené změny. Opravdu je chcete zahodit?"
|
||||
manage: "Administrace"
|
||||
plugins: "Pluginy"
|
||||
preferencesBackups: "Zálohy nastavení"
|
||||
deck: "Deck"
|
||||
undeck: "Opustit Deck"
|
||||
useBlurEffectForModal: "Použít efekt rozostření na okna"
|
||||
useFullReactionPicker: "Používat plnou velikost výběru emoji"
|
||||
width: "Šířka"
|
||||
height: "Výška"
|
||||
large: "Velké"
|
||||
@@ -465,10 +606,13 @@ generateAccessToken: "Vygenerovat přístupový token"
|
||||
permission: "Oprávnění"
|
||||
enableAll: "Povolit vše"
|
||||
disableAll: "Vypnout vše"
|
||||
tokenRequested: "Povolit přístup k účtu"
|
||||
pluginTokenRequestedDescription: "Tenhle plugin bude moct používat oprávnění nastavená zde."
|
||||
notificationType: "Typy oznámení"
|
||||
edit: "Upravit"
|
||||
emailServer: "Mailový server"
|
||||
enableEmail: "Zapnout email dystribuci"
|
||||
emailConfigInfo: "Používá se na ověření emailové adresy během registrace nebo při zapomenutí hesla."
|
||||
email: "Email"
|
||||
emailAddress: "Emailová adresa"
|
||||
smtpConfig: "Konfigurace SMTP serveru"
|
||||
@@ -476,8 +620,15 @@ smtpHost: "Hostitel"
|
||||
smtpPort: "Port"
|
||||
smtpUser: "Uživatelské jméno"
|
||||
smtpPass: "Heslo"
|
||||
emptyToDisableSmtpAuth: "Zanechte uživatelské jméno a heslo prázdné pro vypnutí SMTP verifikace."
|
||||
smtpSecure: "Použít implicitní SSL/TLS pro SMTP připojení"
|
||||
smtpSecureInfo: "Toto vypněte pokud používáte STARTTLS"
|
||||
testEmail: "Otestovat doručení emailů"
|
||||
wordMute: "Ztlumené slova"
|
||||
regexpError: "Chyba v regulérním výrazu"
|
||||
regexpErrorDescription: "Došlo k chybě v regulérním výrazu v řádku {line} tabulky {tab} ztlumených slov:"
|
||||
instanceMute: "Ztlumené instance"
|
||||
userSaysSomething: "{name} řekl/a něco"
|
||||
makeActive: "Aktivovat"
|
||||
display: "Zobrazit"
|
||||
copy: "Kopírovat"
|
||||
@@ -489,21 +640,66 @@ database: "Databáze"
|
||||
channel: "Kanály"
|
||||
create: "Vytvořit"
|
||||
notificationSetting: "Nastavení oznámení"
|
||||
notificationSettingDesc: "Vyberte typy oznámení k zobrazení."
|
||||
useGlobalSetting: "Použít globální nastavení"
|
||||
useGlobalSettingDesc: "Pokud je to zapnuté, tak nastavení oznámení účtu bude použito. Pokud je to vypnuté, tak se bude moct použít jednotlivá nastavení."
|
||||
other: "Ostatní"
|
||||
regenerateLoginToken: "Přegenerovat přihlašovací token"
|
||||
regenerateLoginTokenDescription: "Přegeneruje token interně používaný během přihlášení. Běžně tahle akce není nutná. Pokud bude token přegenerovaný, tak se všechna přihlášená zařízení odhlásí."
|
||||
setMultipleBySeparatingWithSpace: "Oddělení více položek mezerami."
|
||||
fileIdOrUrl: "ID nebo URL souboru"
|
||||
behavior: "Chování"
|
||||
sample: "Ukázka"
|
||||
abuseReports: "Nahlášení"
|
||||
reportAbuse: "Nahlášení"
|
||||
reportAbuseOf: "Nahlásit {name}"
|
||||
fillAbuseReportDescription: "Prosíme vyplňte všechny detaily ohledně tohodle nahlášení. Pokud jde o specifickou poznámku, prosíme o přiložení její URL."
|
||||
abuseReported: "Nahlášení bylo odesláno. Děkujeme převelice."
|
||||
reporter: "Nahlásil"
|
||||
reporteeOrigin: "Původ nahlášení"
|
||||
reporterOrigin: "Původ nahlasovače"
|
||||
forwardReport: "Přeposlat nahlášení do vzdálené instance"
|
||||
send: "Odeslat"
|
||||
openInNewTab: "Otevřít v nové kartě"
|
||||
openInSideView: "Otevřít v bočním panelu"
|
||||
defaultNavigationBehaviour: "Výchozí chování navigace"
|
||||
instanceTicker: "Informace instance o poznámkách"
|
||||
waitingFor: "Čeká se na {x}"
|
||||
random: "Náhodně"
|
||||
system: "Systém"
|
||||
switchUi: "Přepnout UI"
|
||||
desktop: "Plocha"
|
||||
clip: "Oříznout"
|
||||
createNew: "Vytvořit nový"
|
||||
optional: "Volitelné"
|
||||
createNewClip: "Vytvořit nový klip"
|
||||
unclip: "Odepnout"
|
||||
confirmToUnclipAlreadyClippedNote: "Tahle poznámku je už součásti \"{name}\" klipu. Chcete ji místo toho odepnout z tohodle klipu?"
|
||||
public: "Veřejný"
|
||||
i18nInfo: "Misskey je překládán do jiných jazyků dobrovolníkama. Můžete pomoci na {link}."
|
||||
manageAccessTokens: "Spravovat přístupové tokeny"
|
||||
accountInfo: "Informace o účtu"
|
||||
notesCount: "Počet poznámek"
|
||||
repliesCount: "Počet odeslaných odpovědí"
|
||||
renotesCount: "Počet přeposlaných poznámek"
|
||||
repliedCount: "Počet přijatých odpovědí"
|
||||
renotedCount: "Počet přijatých přeposlaných poznámek"
|
||||
followingCount: "Počet sledovaných účtů"
|
||||
followersCount: "Počet sledujících"
|
||||
sentReactionsCount: "Počet odeslaných reakcí"
|
||||
receivedReactionsCount: "Počet přijatých reakcí"
|
||||
pollVotesCount: "Počet odeslaných anketových hlasů"
|
||||
pollVotedCount: "Počet přijatých anketových hlasů"
|
||||
yes: "Ano"
|
||||
no: "Ne"
|
||||
driveFilesCount: "Počet souborů na disku"
|
||||
driveUsage: "Využití disku"
|
||||
noCrawle: "Odmítat indexování crawleru"
|
||||
lockedAccountInfo: "Pokud nenastavíte viditelnost poznámek na \"Pouze pro sledující\", budou poznámky viditelné všem i přesto že vyžadujete manuální potvrzení pro sledování."
|
||||
alwaysMarkSensitive: "Výchozně označovat jako citlivý"
|
||||
loadRawImages: "Načítat originální obrázky místo náhledů"
|
||||
disableShowingAnimatedImages: "Nepřehrávat animované obrázky"
|
||||
verificationEmailSent: "Ověřovací email byl zaslán. Ověření dokončíte kliknutím na odkaz v emailu."
|
||||
notSet: "Není nastaveno"
|
||||
emailVerified: "Váš e-mail byl ověřen"
|
||||
contact: "Kontakt"
|
||||
|
@@ -1091,6 +1091,9 @@ usedAt: "Benutzt am"
|
||||
unused: "Unbenutzt"
|
||||
used: "Benutzt"
|
||||
expired: "Abgelaufen"
|
||||
doYouAgree: "Zustimmen?"
|
||||
beSureToReadThisAsItIsImportant: "Lies bitte diese wichtige Informationen."
|
||||
iHaveReadXCarefullyAndAgree: "Ich habe den Text \"{x}\" gelesen und stimme zu."
|
||||
_initialAccountSetting:
|
||||
accountCreated: "Dein Konto wurde erfolgreich erstellt!"
|
||||
letsStartAccountSetup: "Lass uns nun dein Konto einrichten."
|
||||
|
@@ -1091,6 +1091,9 @@ usedAt: "Used at"
|
||||
unused: "Unused"
|
||||
used: "Used"
|
||||
expired: "Expired"
|
||||
doYouAgree: "Agree?"
|
||||
beSureToReadThisAsItIsImportant: "Please read this important information."
|
||||
iHaveReadXCarefullyAndAgree: "I have read the text \"{x}\" and agree."
|
||||
_initialAccountSetting:
|
||||
accountCreated: "Your account was successfully created!"
|
||||
letsStartAccountSetup: "For starters, let's set up your profile."
|
||||
|
@@ -1091,6 +1091,9 @@ usedAt: "Usada el"
|
||||
unused: "Sin usar"
|
||||
used: "Usada"
|
||||
expired: "Caducada"
|
||||
doYouAgree: "¿Está de acuerdo?"
|
||||
beSureToReadThisAsItIsImportant: "Por favor lea esto que es importante"
|
||||
iHaveReadXCarefullyAndAgree: "He leído el texto {x} y estoy de acuerdo"
|
||||
_initialAccountSetting:
|
||||
accountCreated: "¡La cuenta ha sido creada!"
|
||||
letsStartAccountSetup: "Para empezar, creemos tu perfil."
|
||||
|
@@ -49,6 +49,7 @@ delete: "Supprimer"
|
||||
deleteAndEdit: "Supprimer et réécrire"
|
||||
deleteAndEditConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note et la reformuler ? Vous perdrez toutes les réactions, renotes et réponses y afférentes."
|
||||
addToList: "Ajouter à une liste"
|
||||
addToAntenna: "Ajouter à l’antenne"
|
||||
sendMessage: "Envoyer un message"
|
||||
copyRSS: "Copier le RSS"
|
||||
copyUsername: "Copier le nom d’utilisateur·rice"
|
||||
|
1
locales/hu-HU.yml
Normal file
1
locales/hu-HU.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
3
locales/index.d.ts
vendored
3
locales/index.d.ts
vendored
@@ -1094,6 +1094,9 @@ export interface Locale {
|
||||
"unused": string;
|
||||
"used": string;
|
||||
"expired": string;
|
||||
"doYouAgree": string;
|
||||
"beSureToReadThisAsItIsImportant": string;
|
||||
"iHaveReadXCarefullyAndAgree": string;
|
||||
"_initialAccountSetting": {
|
||||
"accountCreated": string;
|
||||
"letsStartAccountSetup": string;
|
||||
|
@@ -1042,7 +1042,7 @@ vertical: "縦"
|
||||
horizontal: "横"
|
||||
position: "位置"
|
||||
serverRules: "サーバールール"
|
||||
pleaseConfirmBelowBeforeSignup: "このサーバーに登録する前に、以下を確認してください。"
|
||||
pleaseConfirmBelowBeforeSignup: "このサーバーに登録するには、以下の内容を確認し同意する必要があります。"
|
||||
pleaseAgreeAllToContinue: "続けるには、全ての「同意する」にチェックが入っている必要があります。"
|
||||
continue: "続ける"
|
||||
preservedUsernames: "予約ユーザー名"
|
||||
@@ -1091,6 +1091,9 @@ usedAt: "使用日時"
|
||||
unused: "未使用"
|
||||
used: "使用済み"
|
||||
expired: "期限切れ"
|
||||
doYouAgree: "同意しますか?"
|
||||
beSureToReadThisAsItIsImportant: "重要ですので必ずお読みください。"
|
||||
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよく読み、同意します。"
|
||||
|
||||
_initialAccountSetting:
|
||||
accountCreated: "アカウントの作成が完了しました!"
|
||||
|
@@ -49,11 +49,15 @@ delete: "ほかす"
|
||||
deleteAndEdit: "ほかして直す"
|
||||
deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのツッコミ、Renote、返信も全部消えるんやけどそれでもええん?"
|
||||
addToList: "リストに入れたる"
|
||||
addToAntenna: "アンテナに追加"
|
||||
sendMessage: "メッセージを送る"
|
||||
copyRSS: "RSSをコピー"
|
||||
copyUsername: "ユーザー名をコピー"
|
||||
copyUserId: "ユーザーIDをコピー"
|
||||
copyNoteId: "ノートIDをコピー"
|
||||
copyFileId: "ファイルIDをコピー"
|
||||
copyFolderId: "フォルダーIDをコピー"
|
||||
copyProfileUrl: "プロフィールURLをコピー"
|
||||
searchUser: "ユーザーを検索"
|
||||
reply: "返事"
|
||||
loadMore: "まだまだあるで!"
|
||||
@@ -152,6 +156,8 @@ addEmoji: "絵文字を追加"
|
||||
settingGuide: "ええ感じの設定"
|
||||
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
||||
cacheRemoteFilesDescription: "この設定を切っとったら、リモートファイルをキャッシュせんと直リンクするようになるで。サーバーの容量は節約できるけど、サムネイルを作らんなるから通信量が増えるで。"
|
||||
cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
|
||||
cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになるで。"
|
||||
flagAsBot: "Botにするで"
|
||||
flagAsBotDescription: "もしこのアカウントをプログラム使うて運用するんやったら、このフラグをオンにしてや。オンにすれば、反応がバーッて連鎖せんように開発者が使うたり、Misskeyのシステム上での扱いがBotに合ったもんになるからな。"
|
||||
flagAsCat: "Catやで"
|
||||
@@ -313,6 +319,7 @@ copyUrl: "URLをコピー"
|
||||
rename: "名前を変えるで"
|
||||
avatar: "アイコン"
|
||||
banner: "バナー"
|
||||
displayOfSensitiveMedia: "センシティブなメディアの表示"
|
||||
whenServerDisconnected: "サーバーとの接続が失くなってしもうたとき"
|
||||
disconnectedFromServer: "サーバーが機嫌悪いねん"
|
||||
reload: "リロード"
|
||||
@@ -1067,8 +1074,26 @@ branding: "あ"
|
||||
enableServerMachineStats: "サーバーのマシン情報見せびらかすで"
|
||||
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
|
||||
turnOffToImprovePerformance: "オフにしたらえらい軽うなるで。"
|
||||
createInviteCode: "招待コードを作成"
|
||||
createWithOptions: "オプションを指定して作成"
|
||||
createCount: "作成数"
|
||||
inviteCodeCreated: "招待コード作ったで"
|
||||
inviteLimitExceeded: "招待コード作りすぎやで。"
|
||||
createLimitRemaining: "作成できる招待コード: 残り {limit} 個やで"
|
||||
inviteLimitResetCycle: "{time}で最大 {limit} 個の招待コードを作成できるで。"
|
||||
expirationDate: "有効期限"
|
||||
noExpirationDate: "有効期限を設けへん"
|
||||
inviteCodeUsedAt: "招待コードが使用された日時"
|
||||
registeredUserUsingInviteCode: "招待コードを使用したユーザー"
|
||||
waitingForMailAuth: "メール認証待ち"
|
||||
inviteCodeCreator: "招待コードを作成したユーザー"
|
||||
usedAt: "使用日時"
|
||||
unused: "つこてへん"
|
||||
used: "もうつこてる"
|
||||
expired: "期限切れ"
|
||||
doYouAgree: "同意するんか?"
|
||||
beSureToReadThisAsItIsImportant: "重要やから絶対読んでや。"
|
||||
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよう読んで、同意するで。"
|
||||
_initialAccountSetting:
|
||||
accountCreated: "アカウント作り終わったで。"
|
||||
letsStartAccountSetup: "アカウントの初期設定をしよか。"
|
||||
@@ -1379,6 +1404,9 @@ _role:
|
||||
ltlAvailable: "ローカルタイムラインの閲覧"
|
||||
canPublicNote: "パブリック投稿の許可"
|
||||
canInvite: "サーバー招待コードの発行"
|
||||
inviteLimit: "招待コードの作成可能数"
|
||||
inviteLimitCycle: "招待コードの発行間隔"
|
||||
inviteExpirationTime: "招待コードの有効期限"
|
||||
canManageCustomEmojis: "カスタム絵文字の管理"
|
||||
driveCapacity: "ドライブ容量"
|
||||
alwaysMarkNsfw: "勝手にファイルにNSFWをくっつける"
|
||||
@@ -1441,6 +1469,7 @@ _ad:
|
||||
back: "戻る"
|
||||
reduceFrequencyOfThisAd: "この広告の表示頻度を下げるで"
|
||||
hide: "表示せん"
|
||||
timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されるで。"
|
||||
_forgotPassword:
|
||||
enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。"
|
||||
ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。"
|
||||
@@ -1970,6 +1999,7 @@ _deck:
|
||||
introduction: "カラムを組み合わせて自分だけのインターフェイスを作りましょ!"
|
||||
introduction2: "画面の右にある + を押して、いつでもカラムを追加できるで。"
|
||||
widgetsIntroduction: "カラムのメニューから、「ウィジェットの編集」を選んでウィジェットを追加してなー"
|
||||
useSimpleUiForNonRootPages: "非ルートページは簡易UIで表示"
|
||||
_columns:
|
||||
main: "メイン"
|
||||
widgets: "ウィジェット"
|
||||
|
@@ -40,7 +40,7 @@ favorites: "즐겨찾기"
|
||||
unfavorite: "즐겨찾기에서 제거"
|
||||
favorited: "즐겨찾기에 등록했습니다"
|
||||
alreadyFavorited: "이미 즐겨찾기에 등록되어 있습니다"
|
||||
cantFavorite: "즐겨찾기에 등록하지 못했습니다."
|
||||
cantFavorite: "즐겨찾기에 등록하지 못했습니다"
|
||||
pin: "프로필에 고정"
|
||||
unpin: "프로필에서 고정 해제"
|
||||
copyContent: "내용 복사"
|
||||
@@ -55,8 +55,8 @@ copyRSS: "RSS 복사"
|
||||
copyUsername: "유저명 복사"
|
||||
copyUserId: "유저 ID 복사"
|
||||
copyNoteId: "노트 ID 복사"
|
||||
copyFileId: "파일명 복사"
|
||||
copyFolderId: "폴더명 복사"
|
||||
copyFileId: "파일 ID 복사"
|
||||
copyFolderId: "폴더 ID 복사"
|
||||
copyProfileUrl: "프로필 URL 복사"
|
||||
searchUser: "사용자 검색"
|
||||
reply: "답글"
|
||||
@@ -108,7 +108,7 @@ renote: "리노트"
|
||||
unrenote: "리노트 취소"
|
||||
renoted: "리노트했습니다"
|
||||
cantRenote: "이 게시물은 리노트 할 수 없습니다."
|
||||
cantReRenote: "리노트를 리노트 할 수 없습니다."
|
||||
cantReRenote: "리노트를 리노트할 수 없습니다."
|
||||
quote: "인용"
|
||||
inChannelRenote: "채널 내 리노트"
|
||||
inChannelQuote: "채널 내 인용"
|
||||
@@ -116,7 +116,7 @@ pinnedNote: "고정해놓은 노트"
|
||||
pinned: "프로필에 고정"
|
||||
you: "당신"
|
||||
clickToShow: "클릭하여 보기"
|
||||
sensitive: "열람주의"
|
||||
sensitive: "열람 주의"
|
||||
add: "추가"
|
||||
reaction: "리액션"
|
||||
reactions: "리액션"
|
||||
@@ -156,12 +156,12 @@ addEmoji: "이모지 추가"
|
||||
settingGuide: "추천 설정"
|
||||
cacheRemoteFiles: "리모트 파일을 캐시"
|
||||
cacheRemoteFilesDescription: "이 설정을 해지하면 리모트 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다."
|
||||
cacheRemoteSensitiveFiles: "민감한 원격 파일을 캐시"
|
||||
cacheRemoteSensitiveFilesDescription: "이 설정을 비활성화하면 중요한 원격 파일은 캐싱 없이 원격 인스턴스에서 직접 로드됩니다."
|
||||
cacheRemoteSensitiveFiles: "리모트의 민감한 파일을 캐시"
|
||||
cacheRemoteSensitiveFilesDescription: "이 설정을 비활성화하면 리모트의 민감한 파일은 캐시하지 않고 리모트에서 직접 가져오도록 합니다."
|
||||
flagAsBot: "나는 봇입니다"
|
||||
flagAsBotDescription: "이 계정을 자동화된 수단으로 운용할 경우에 활성화해 주세요. 이 플래그를 활성화하면, 다른 봇이 이를 참고하여 봇 끼리의 무한 연쇄 반응을 회피하거나, 이 계정의 시스템 상에서의 취급이 Bot 운영에 최적화되는 등의 변화가 생깁니다."
|
||||
flagAsCat: "나는 고양이다냥"
|
||||
flagAsCatDescription: "이 계정이 고양이라면 활성화 해주세요."
|
||||
flagAsCatDescription: "이 계정이 고양이라면 활성화해 주세요."
|
||||
flagShowTimelineReplies: "타임라인에 노트의 답글을 표시하기"
|
||||
flagShowTimelineRepliesDescription: "이 설정을 활성화하면 타임라인에 다른 유저 간의 답글을 표시합니다."
|
||||
autoAcceptFollowed: "팔로우 중인 유저로부터의 팔로우 요청을 자동 수락"
|
||||
@@ -207,7 +207,7 @@ instanceInfo: "서버 정보"
|
||||
statistics: "통계"
|
||||
clearQueue: "대기열 비우기"
|
||||
clearQueueConfirmTitle: "대기열을 비우시겠습니까?"
|
||||
clearQueueConfirmText: "대기열에 남아 있는 노트는 더이상 연합되지 않습니다. 보통의 경우 이 작업은 필요하지 않습니다."
|
||||
clearQueueConfirmText: "대기열에 남아 있는 노트는 더 이상 연합되지 않습니다. 보통의 경우 이 작업은 필요하지 않습니다."
|
||||
clearCachedFiles: "캐시 비우기"
|
||||
clearCachedFilesConfirm: "캐시된 리모트 파일을 모두 삭제하시겠습니까?"
|
||||
blockedInstances: "차단된 서버"
|
||||
@@ -1075,7 +1075,7 @@ enableServerMachineStats: "서버의 머신 사양을 공개하기"
|
||||
enableIdenticonGeneration: "유저마다의 Identicon 생성 유효화"
|
||||
turnOffToImprovePerformance: "이 기능을 끄면 성능이 향상될 수 있습니다."
|
||||
createInviteCode: "초대 코드 생성"
|
||||
createWithOptions: "설정으로 생성"
|
||||
createWithOptions: "옵션을 지정하여 생성"
|
||||
createCount: "초대 수"
|
||||
inviteCodeCreated: "초대 코드 생성됨"
|
||||
inviteLimitExceeded: "초대 코드 생성 한도를 초과했습니다."
|
||||
@@ -1087,7 +1087,7 @@ inviteCodeUsedAt: "다음에 사용된 초대 코드"
|
||||
registeredUserUsingInviteCode: "초대 코드 사용 대상"
|
||||
waitingForMailAuth: "이메일 인증 보류 중"
|
||||
inviteCodeCreator: "초대 코드 생성자"
|
||||
usedAt: "사용 기간"
|
||||
usedAt: "사용 시각"
|
||||
unused: "사용되지 않음"
|
||||
used: "사용됨"
|
||||
expired: "만료됨"
|
||||
@@ -1402,7 +1402,7 @@ _role:
|
||||
canPublicNote: "공개 노트 허용"
|
||||
canInvite: "서버 초대 코드 발행"
|
||||
inviteLimit: "초대 한도"
|
||||
inviteLimitCycle: "초대 발급 재사용 대기 시간"
|
||||
inviteLimitCycle: "초대 발급 간격"
|
||||
inviteExpirationTime: "초대 만료 기간"
|
||||
canManageCustomEmojis: "커스텀 이모지 관리"
|
||||
driveCapacity: "드라이브 용량"
|
||||
@@ -1996,7 +1996,7 @@ _deck:
|
||||
introduction: "칼럼을 조합해서 나만의 인터페이스를 구성해 보아요!"
|
||||
introduction2: "나중에라도 화면 우측의 + 버튼을 눌러 새 칼럼을 추가할 수 있습니다."
|
||||
widgetsIntroduction: "칼럼 메뉴의 \"위젯 편집\"에서 위젯을 추가해 주세요"
|
||||
useSimpleUiForNonRootPages: "탐색 페이지에 간단한 UI 사용하기"
|
||||
useSimpleUiForNonRootPages: "루트 이외의 페이지로 접속한 경우 UI 간략화하기"
|
||||
_columns:
|
||||
main: "메인"
|
||||
widgets: "위젯"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,7 @@ delete: "Удалить"
|
||||
deleteAndEdit: "Удалить и отредактировать"
|
||||
deleteAndEditConfirm: "Удалить эту заметку и создать отредактированную? Все реакции, ссылки и ответы на существующую будут будут потеряны."
|
||||
addToList: "Добавить в список"
|
||||
addToAntenna: "Добавить к антенне"
|
||||
sendMessage: "Отправить сообщение"
|
||||
copyRSS: "Скопировать RSS"
|
||||
copyUsername: "Скопировать имя пользователя"
|
||||
@@ -155,6 +156,8 @@ addEmoji: "Добавить эмодзи"
|
||||
settingGuide: "Рекомендуемые настройки"
|
||||
cacheRemoteFiles: "Кешировать внешние файлы"
|
||||
cacheRemoteFilesDescription: "Когда эта настройка отключена, файлы с других сайтов будут загружаться прямо оттуда. Это сэкономит место на сервере, но увеличит трафик, так как не будут создаваться эскизы."
|
||||
cacheRemoteSensitiveFiles: "Кешировать внешние файлы"
|
||||
cacheRemoteSensitiveFilesDescription: "Описание удаленных внешних файлов в кэше"
|
||||
flagAsBot: "Аккаунт бота"
|
||||
flagAsBotDescription: "Включите, если этот аккаунт управляется программой. Это позволит системе Misskey учитывать это, а также поможет разработчикам других ботов предотвратить бесконечные циклы взаимодействия."
|
||||
flagAsCat: "Аккаунт кота"
|
||||
@@ -316,6 +319,7 @@ copyUrl: "Копировать ссылку"
|
||||
rename: "Переименовать"
|
||||
avatar: "Аватар"
|
||||
banner: "Шапка"
|
||||
displayOfSensitiveMedia: "Определение деликатного контента"
|
||||
whenServerDisconnected: "Когда соединение с сервером потеряно"
|
||||
disconnectedFromServer: "Разорвано соединение с сервером"
|
||||
reload: "Перезагрузить"
|
||||
@@ -840,6 +844,8 @@ breakFollow: "Отписка"
|
||||
breakFollowConfirm: "Удалить из подписок пользователя ?"
|
||||
itsOn: "Включено"
|
||||
itsOff: "Выключено"
|
||||
on: "Вкл"
|
||||
off: "Выкл"
|
||||
emailRequiredForSignup: "Для регистрации учётной записи нужен адрес электронной почты"
|
||||
unread: "Непрочитанное"
|
||||
filter: "Фильтры"
|
||||
@@ -994,6 +1000,7 @@ cannotBeChangedLater: "Это нельзя изменить позже"
|
||||
reactionAcceptance: "Принятие реакций"
|
||||
likeOnly: "Только лайки"
|
||||
likeOnlyForRemote: "Только лайки с удалённых серверов"
|
||||
nonSensitiveOnly: "Безопасный серфинг"
|
||||
rolesAssignedToMe: "Мои роли"
|
||||
resetPasswordConfirm: "Сбросить пароль?"
|
||||
sensitiveWords: "Чувствительные слова"
|
||||
@@ -1014,7 +1021,15 @@ noteIdOrUrl: "ID или ссылка на заметку"
|
||||
video: "Видео"
|
||||
videos: "Видео"
|
||||
dataSaver: "Экономия трафика"
|
||||
accountMigration: "Перенести учётную запись"
|
||||
accountMoved: "Учетная запись перенесена"
|
||||
operationForbidden: "Эта операция невозможна."
|
||||
addMemo: "Добавить заметку"
|
||||
editMemo: "Редактировать заметку"
|
||||
reactionsList: "Реакции"
|
||||
renotesList: "Репосты"
|
||||
notificationDisplay: "Отображение уведомления"
|
||||
leftTop: "Верхний левый угол"
|
||||
horizontal: "Сбоку"
|
||||
youFollowing: "Подписки"
|
||||
options: "Настройки ролей"
|
||||
|
@@ -389,10 +389,13 @@ help: "Hjälp"
|
||||
close: "Stäng"
|
||||
invites: "Inbjudan"
|
||||
members: "Medlemmar"
|
||||
transfer: "Överför"
|
||||
text: "Text"
|
||||
enable: "Aktivera"
|
||||
next: "Nästa"
|
||||
invitations: "Inbjudan"
|
||||
invitationCode: "Inbjudningskod"
|
||||
available: "Tillgängligt"
|
||||
weakPassword: "Svagt Lösenord"
|
||||
normalPassword: "Medel Lösenord"
|
||||
strongPassword: "Starkt Lösenord"
|
||||
@@ -481,6 +484,7 @@ windowMinimize: "Minimera"
|
||||
windowRestore: "Återställ"
|
||||
pleaseDonate: "Misskey är en gratis programvara som används på {host}. Donera gärna för att göra utvecklingen ständigt, tack!"
|
||||
resetPasswordConfirm: "Återställ verkligen ditt lösenord?"
|
||||
dataSaver: "Databesparing"
|
||||
_achievements:
|
||||
_types:
|
||||
_open3windows:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
_lang_: "ภาษาไทย"
|
||||
headlineMisskey: "เชื่อมต่อเครือข่ายโดยโน้ต"
|
||||
introMisskey: "ยินดีต้อนรับจ้าาา! Misskey เป็นบริการไมโครบล็อกโอเพ่นซอร์ส แบบการกระจายอำนาจ\nสร้าง \"โน้ต\" เพื่อแบ่งปันความคิดของคุณกับทุกคนรอบตัวคุณกันเถอะ 📡\nด้วยการ \"รีแอคชั่นผู้คน\" คุณยังสามารถแสดงความรู้สึกของคุณเกี่ยวกับบันทึกของทุกคนได้อย่างรวดเร็ว 👍\n\nแล้วมาท่องสำรวจโลกใบใหม่กันเถอะ! 🚀"
|
||||
headlineMisskey: "เชื่อมต่อระบบ Network ด้วย Note"
|
||||
introMisskey: "ยินดีต้อนรับทุกคนจ้า! Misskey คือ บริการไมโครบล็อกกิ้ง (MicroBlogging) แบบกระจายศูนย์อำนาจ (Decentralized) \n\nเขียน \"โน้ต (Note)\" เพื่อส่งต่อเรื่องราวของคุณให้ทั้งโลกได้รับรู้📡\nและอย่าลืมที่จะ \"React\" กับเรื่องราวของคนอื่น ๆ ด้วย! 👍\n\nมุ่งสู่โลกใบใหม่กันเถอะ🚀"
|
||||
poweredByMisskeyDescription: "{name} เป็นส่วนหนึ่งในบริการที่ถูกขับเคลื่อนโดยแพลตฟอร์มโอเพ่นซอร์ส <b>Misskey</b> (เรียกว่า \"อินสแตนซ์ Misskey\")"
|
||||
monthAndDay: "{month}/{day}"
|
||||
search: "ค้นหา"
|
||||
@@ -98,7 +98,7 @@ enterListName: "ใส่ชื่อสำหรับรายการลิ
|
||||
privacy: "ความเป็นส่วนตัว"
|
||||
makeFollowManuallyApprove: "ติดตามคำขอที่ต้องได้รับการอนุมัติ"
|
||||
defaultNoteVisibility: "การมองเห็นที่เป็นค่าเริ่มต้น"
|
||||
follow: "กำลังติดตาม"
|
||||
follow: "ติดตาม"
|
||||
followRequest: "ส่งคำขอติดตาม"
|
||||
followRequests: "ส่งคำขอติดตาม"
|
||||
unfollow: "เลิกติดตาม"
|
||||
@@ -183,7 +183,7 @@ selectUser: "เลือกผู้ใช้งาน"
|
||||
recipient: "ผู้รับ"
|
||||
annotation: "ความคิดเห็น"
|
||||
federation: "เฟดิเวิร์ส"
|
||||
instances: "ตัวอย่าง"
|
||||
instances: "Server"
|
||||
registeredAt: "จดทะเบียนที่"
|
||||
latestRequestReceivedAt: "ได้รับคำขอล่าสุดไปแล้ว"
|
||||
latestStatus: "สถานะล่าสุด"
|
||||
@@ -339,7 +339,7 @@ thisYear: "ปีนี้"
|
||||
thisMonth: "เดือนนี้"
|
||||
today: "วันนี้"
|
||||
dayX: "{day}"
|
||||
monthX: "{เดือน}"
|
||||
monthX: "เดือน {month}"
|
||||
yearX: "{year}"
|
||||
pages: "หน้า"
|
||||
integration: "รวบรวม"
|
||||
@@ -1091,6 +1091,9 @@ usedAt: "ใช้แล้วที่"
|
||||
unused: "ไม่ใช้แล้ว"
|
||||
used: "ใช้แล้ว"
|
||||
expired: "หมดอายุแล้ว"
|
||||
doYouAgree: "ยอมรับมั้ย?"
|
||||
beSureToReadThisAsItIsImportant: "กรุณาอ่านข้อมูลที่สำคัญอันนี้"
|
||||
iHaveReadXCarefullyAndAgree: "ฉันได้อ่านข้อความ \"{x}\" และยินยอม"
|
||||
_initialAccountSetting:
|
||||
accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!"
|
||||
letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ"
|
||||
@@ -1996,6 +1999,7 @@ _deck:
|
||||
introduction: "สร้างอินเทอร์เฟซที่สมบูรณ์แบบสำหรับคุณโดยจัดเรียงคอลัมน์ได้อย่างอิสระ!"
|
||||
introduction2: "คลิกที่เครื่องหมาย + ทางขวาของหน้าจอเพื่อเพิ่มคอลัมน์ใหม่ทุกครั้งที่คุณต้องการ"
|
||||
widgetsIntroduction: "กรุณาเลือก \"แก้ไขวิดเจ็ต\" ในเมนูคอลัมน์และเพิ่มวิดเจ็ต"
|
||||
useSimpleUiForNonRootPages: "แสดง UI ของ Root Page อย่างง่าย "
|
||||
_columns:
|
||||
main: "หลัก"
|
||||
widgets: "วิดเจ็ต"
|
||||
|
634
locales/uz-UZ.yml
Normal file
634
locales/uz-UZ.yml
Normal file
@@ -0,0 +1,634 @@
|
||||
---
|
||||
_lang_: "O'zbek tili "
|
||||
headlineMisskey: "Qaydlar bilan bog'langan tarmoq"
|
||||
introMisskey: "Xush kelibsiz! Misskey ochiq kodli, markazlashmagan mikroblogging xizmati.\nO'zingizni fikrlaringizni atrofingizdagilar bilan ulashish uchun \"Qaydlar\" yarating. 📡\nUstiga-ustak, \"Reaktsiyalar\" yordamida siz boshqalarning xatlari haqidagi o'zingizni xissiyotlaringizni tez ravishda bildiring. 👍\nQani, yangi dunyoni kashf qilaylik! 🚀"
|
||||
poweredByMisskeyDescription: "{name} ochiq manbali <b>Misskey</b>(\"Misskey instance\" deb ataladi) platformasi tomonidan qurilgan servislardan biri. "
|
||||
monthAndDay: "{day}/{month}"
|
||||
search: "Izlash"
|
||||
notifications: "Xabarnomalar"
|
||||
username: "Foydalanuvchi nomi"
|
||||
password: "Parol"
|
||||
forgotPassword: "Parolni unutib qo'ydim"
|
||||
fetchingAsApObject: "Fediversedan olib kelinmoqda..."
|
||||
ok: "Ho'p"
|
||||
gotIt: "Tushunarli!"
|
||||
cancel: "Bekor qilish"
|
||||
noThankYou: "Hozir emas"
|
||||
enterUsername: "Foydalanuvchini nomini kiriting"
|
||||
renotedBy: "{user} tomonidan qayta qayd etildi"
|
||||
noNotes: "Qaydlar mavjud emas"
|
||||
noNotifications: "Xabarnomalar mavjud emas"
|
||||
instance: "Server"
|
||||
settings: "Sozalamalar"
|
||||
notificationSettings: "Xabarnomalar sozlamalari"
|
||||
basicSettings: "Asosiy sozlamalar"
|
||||
otherSettings: "Qo‘shimcha sozlamalar"
|
||||
openInWindow: "Yangi oynada ochish"
|
||||
profile: "Profil"
|
||||
timeline: "Xronologiya"
|
||||
noAccountDescription: "Ushbu foydalanuvchi hali o'zi haqida ma'lumot yozmagan."
|
||||
login: "Kirish"
|
||||
loggingIn: "Kirilmoqda"
|
||||
logout: "Chiqish"
|
||||
signup: "Ro'yxatdan o'tish"
|
||||
uploading: "Yuklanmoqda..."
|
||||
save: "Saqlash"
|
||||
users: "Foydalanuvchilar"
|
||||
addUser: "Foydalanuvchi qo'shish"
|
||||
favorite: "Sevimli"
|
||||
favorites: "Sevimli"
|
||||
unfavorite: "Sevimlidan chiqarish"
|
||||
favorited: "sevimli"
|
||||
alreadyFavorited: "allaqachon sevimlilar orasida"
|
||||
cantFavorite: "sevimlilarga qo'shib bo'lmadi"
|
||||
pin: "Profilga qadab qo'yish"
|
||||
unpin: "Profildan olib tashlash"
|
||||
copyContent: "Tarkibini nusxalash"
|
||||
copyLink: "Havolani nuxalash"
|
||||
delete: "O'chirib tashlash"
|
||||
deleteAndEdit: "O'chirish va tahrirlash"
|
||||
deleteAndEditConfirm: "O'chirib, tahrirlamoqchiligingizga ishonchingiz komilmi? Siz bu qaydga tegishli barcha reaktsiyalar, qayta qaydlar va javoblarni yo'qotasiz."
|
||||
addToList: "Ro‘yxatga qo‘shish"
|
||||
addToAntenna: "Antennaga qo'shish"
|
||||
sendMessage: "Xabar yuborish"
|
||||
copyRSS: "RSS'ni nusxalash"
|
||||
copyUsername: "Foydalanuvchi nomini nusxalash"
|
||||
copyUserId: "Foydalanuvchi IDsini nusxalash"
|
||||
copyNoteId: "Qayd IDsini ko'chirish"
|
||||
copyFileId: "Fayl ID raqamini nusxalash"
|
||||
copyFolderId: "Jild ID raqamini nusxalash"
|
||||
copyProfileUrl: "Profil manzilini nusxalash"
|
||||
searchUser: "Foydalanuvchini izlash"
|
||||
reply: "Javob berish"
|
||||
loadMore: "Ko‘proq ko‘rish"
|
||||
showMore: "Ko‘proq ko‘rish"
|
||||
showLess: "Yopish"
|
||||
youGotNewFollower: "sizga obuna bo'ldi"
|
||||
receiveFollowRequest: "Obuna bo'lishga ruxsat qabul qilindi"
|
||||
followRequestAccepted: "Obuna bo'lishga ruxsat berildi"
|
||||
mention: "Murojat"
|
||||
mentions: "Eslatib o'tish"
|
||||
directNotes: "Bevosita qaydlar"
|
||||
importAndExport: "Import/eksport"
|
||||
import: "Import"
|
||||
export: "Eksport"
|
||||
files: "Fayllar"
|
||||
download: "Yuklab olish"
|
||||
driveFileDeleteConfirm: "\"{name}\" o'chirib tashlamoqchimisiz? Buni ishlatadihan kontentni hammasidan o'chadi"
|
||||
unfollowConfirm: "{name}ga obunani bekor qilmoqchimisiz?"
|
||||
exportRequested: "Eksport so'raldi. Bu ozgina vaqt olishi mumkin. Tugatilgandan so'ng sizning Diskingizga qo'shiladi"
|
||||
importRequested: "Import so'raldi. Bu ozgina vaqt olishi mumkin."
|
||||
lists: "Ro'yxatlar"
|
||||
noLists: "Hech qanday ro'yxatlar mavjud emas"
|
||||
note: "Qayd"
|
||||
notes: "Qaydlar"
|
||||
following: "Obuna bo‘lish"
|
||||
followers: "Obunachilar"
|
||||
followsYou: "Sizning obunachingiz."
|
||||
createList: "Ro'yxat yaratish"
|
||||
manageLists: "Ro'yxatlarni boshqarish."
|
||||
error: "Xato"
|
||||
somethingHappened: "Xatolik yuz berdi"
|
||||
retry: "Qayta urinib ko'rish"
|
||||
pageLoadError: "Sahifani yuklayotganda xatolik yuz berdi"
|
||||
pageLoadErrorDescription: "Buni odatda tarmoq muammolari yoki browser keshi keltirib chiqaradi. Keshni tozalab, keyinroq urinib ko'ring"
|
||||
serverIsDead: "Server javob bermayabdi. Iltimos kuting va keyinroq urinib ko'ring"
|
||||
youShouldUpgradeClient: "Iltimos, ushbu sahifani ko'rish uchun sahifani yangilang."
|
||||
enterListName: "Ro'yxatga nom kiriting"
|
||||
privacy: "Maxfiylik"
|
||||
makeFollowManuallyApprove: "Yopiq akkaunt"
|
||||
follow: "Obuna bo‘lish"
|
||||
followRequest: "Obuna bo'lish uchun ruxsat olish"
|
||||
followRequests: "Obuna bo'lmoqchilar"
|
||||
unfollow: "obunani bker qilish"
|
||||
followRequestPending: "obuna bo'lishga ruxsat kutilmoqda"
|
||||
enterEmoji: "Emojini kiriting"
|
||||
renote: "Qayta qaydetish"
|
||||
unrenote: "Qayta qayd etishni bekor qilish"
|
||||
renoted: "Qayta qaydetildi"
|
||||
cantRenote: "Qyta qayd etish mumkin emas"
|
||||
quote: "Iqtibos keltirish"
|
||||
inChannelRenote: "Faqat kanalga qayta qayd etish"
|
||||
inChannelQuote: "Kanaldagi eslatmalar"
|
||||
pinnedNote: "Qadanlgan qayd"
|
||||
pinned: "Profilga qadab qo'yish"
|
||||
you: "Siz"
|
||||
clickToShow: "Ko'rsatish uchun bosing"
|
||||
sensitive: "Sezuvchan"
|
||||
add: "Qo'shish"
|
||||
reaction: "Reaktsiyalar"
|
||||
reactions: "Reaktsiyalar"
|
||||
reactionSetting: "Reaksiyalar ro'yxatingiz "
|
||||
rememberNoteVisibility: "Qaydning ko'rinish sozlamarini eslab qolish"
|
||||
attachCancel: "Qo'shimchani olib tashlash"
|
||||
enterFileName: "Fayl nomini kiriting"
|
||||
mute: "Ovozni o‘chirish"
|
||||
unmute: "Ovozni yoqish"
|
||||
renoteMute: "Qayta qaydlarni ovozini o'chirish"
|
||||
renoteUnmute: "Qayta qaydlarni ovozini yoqish"
|
||||
block: "Bloklash"
|
||||
unblock: "Blokdan chiqarish"
|
||||
suspend: "To'xtatish"
|
||||
unsuspend: "Blokdan chiqarish"
|
||||
blockConfirm: "Haqiqatdan ham quyidagi hisobni bloklashni xohlaysizmi? "
|
||||
unblockConfirm: "Haqiqatdan ham quyidagi hisobni blokdan chiqarishni xohlaysizmi? "
|
||||
suspendConfirm: "Bu hisobni to‘xtatib qo‘ymoqchi ekanligingizga ishonchingiz komilmi?"
|
||||
unsuspendConfirm: "Tasdiqlashni to'xtatib turish"
|
||||
selectList: "Ro'yxat tanlash"
|
||||
editList: "Roʻyxatni tahrirlash"
|
||||
selectChannel: "Kanalni tanlang"
|
||||
selectAntenna: "Antennani tanlang"
|
||||
editAntenna: "Antennani tahrirlang"
|
||||
selectWidget: "Vidjet tanlash"
|
||||
editWidgets: "Vidjetni tahrirlash"
|
||||
editWidgetsExit: "Tugadi"
|
||||
customEmojis: "Maxsus emoji"
|
||||
emoji: "Emoji"
|
||||
emojis: "Emoji"
|
||||
emojiName: "Emoji nomi"
|
||||
emojiUrl: "Emoji URL'i"
|
||||
addEmoji: "Emoji qo'shish"
|
||||
settingGuide: "Tavsiya qilingan sozlamalar"
|
||||
cacheRemoteFiles: "Tashqi fayllarni keshlash"
|
||||
cacheRemoteFilesDescription: "Ushbu sozlama o'chirilgan bo'lsa tashqi fayllar bevosita tashqi serverdan yuklanadi. Buni o'chirish ombor ishlatilishini kamaytiradi, lekin traffikni ko'paytiradi, chunki eskizlar generatsiya qilinmaydi."
|
||||
cacheRemoteSensitiveFiles: "Tashqi fayllarni keshlash"
|
||||
flagAsBot: "Ushbu akkauntni bot sifatida belgilash"
|
||||
flagAsBotDescription: "Agar bu akkaunt bot tomonidan boshqaralayotgan bo'ls, bu sozlamani yoqing. Sozlama yoqilganda, boshqa foydalanuvchilar uchun belgi sifatida ishlaydi, va Misskey ichki tizimlari bu akkauntni bot ekanini biladi."
|
||||
flagAsCat: "Bu akkauntni mushuk sifatida belgilash"
|
||||
flagAsCatDescription: "Ushbu akkauntni mushuk sifatida belgilash uchun ushbu sozlamani yoqing."
|
||||
flagShowTimelineReplies: "Javbolarni xronogoliya bo'yicha ko'rsatish"
|
||||
addAccount: "Akkaunt qo'shish"
|
||||
reloadAccountsList: "Hisoblar ro'yxatini yangilash"
|
||||
loginFailed: "Tizimga kirishda xatolik yuz berdi"
|
||||
showOnRemote: "Masofaviy boshqaruvni ko'rish"
|
||||
general: "Asosiy"
|
||||
wallpaper: "Fon rasmi"
|
||||
setWallpaper: "Fon rasmini o'rnatish"
|
||||
removeWallpaper: "Fon rasmini olib tashlash"
|
||||
searchWith: "Izlash: {q}"
|
||||
youHaveNoLists: "Sizda hech qanday ro'yxatlar mavjud emas"
|
||||
followConfirm: "{name} ga obuna bo'lmoqchimisiz?"
|
||||
proxyAccount: "Proksi hisob"
|
||||
host: "Host"
|
||||
selectUser: "Foydalanuvchini tanlang"
|
||||
recipient: "Qabul qiluvchi"
|
||||
annotation: "Izohlar"
|
||||
federation: "Federatsiya"
|
||||
instances: "Serverlar"
|
||||
registeredAt: "Ro'yhatdan o'tgan"
|
||||
latestStatus: "So'nggi holat"
|
||||
storageUsage: "Ishlatilgan xotira"
|
||||
charts: "Diagrammalar"
|
||||
perHour: "Soatbay"
|
||||
perDay: "Kunbay"
|
||||
stopActivityDelivery: "Faollikni jo'natishi to'xtatish"
|
||||
blockThisInstance: "Ko;rsatilgan serverni bloklash"
|
||||
operations: "Amallar"
|
||||
software: "Dastur"
|
||||
version: "Versiya"
|
||||
metadata: "Meta ma'lumot"
|
||||
withNFiles: "{n} ta fayl(lar)"
|
||||
monitor: "Kuzatish"
|
||||
jobQueue: "Vazifalar navbati"
|
||||
cpuAndMemory: "CPU va Xotira"
|
||||
network: "Tarmoq"
|
||||
disk: "Disk"
|
||||
instanceInfo: "Instans haqida ma'lumot"
|
||||
statistics: "Statistika"
|
||||
clearQueue: "Navbatni tozalash"
|
||||
clearQueueConfirmTitle: "Navbatni tozalamoqchimisiz?"
|
||||
clearCachedFiles: "Keshni tozalash"
|
||||
blockedInstances: "Bloklangan serverlar"
|
||||
muteAndBlock: "Ovozsiz va Bloklangan"
|
||||
mutedUsers: "Ovozsiz foydalanuvchilar"
|
||||
blockedUsers: "Bloklangan foydalanuvchilar"
|
||||
noUsers: "Foydalanuvchilar yo‘q"
|
||||
editProfile: "Profilni o'zgartirish"
|
||||
noteDeleteConfirm: "Haqiqatan ham bu qaydni oʻchirib tashlamoqchimisiz?"
|
||||
pinLimitExceeded: "Siz boshqa qaydlarni mahkamlay olmaysiz"
|
||||
intro: "Misskeyni o'rnatish tugallandi! Iltimos, administrator foydalanuvchi yarating."
|
||||
done: "Bajarildi"
|
||||
processing: "Amaliyotda"
|
||||
preview: "Ko'rish"
|
||||
default: "Odatiy"
|
||||
defaultValueIs: "Sukut bo'yicha: {value}"
|
||||
noCustomEmojis: "Emojilar mavjud emas"
|
||||
noJobs: "Vazifalar yo'q"
|
||||
blocked: "Bloklangan"
|
||||
suspended: "To'xtatilgan"
|
||||
all: "Barcha"
|
||||
subscribing: "Obuna bo'lish"
|
||||
publishing: "Yuborilmoqda"
|
||||
notResponding: "Javob bermayapti"
|
||||
changePassword: "Parolni o‘zgartirish"
|
||||
security: "Xavfsizlik"
|
||||
retypedNotMatch: "Maydonlar mos kelmayapti"
|
||||
currentPassword: "Joriy parol"
|
||||
newPassword: "Yangi parol"
|
||||
newPasswordRetype: "Yangi parolni boshqatdan tering"
|
||||
attachFile: "Fayl biriktirish"
|
||||
more: "Ko'proq!"
|
||||
noSuchUser: "Foydalanuvchi topilmadi"
|
||||
lookup: "So'rov"
|
||||
announcements: "Bildirishnomalar"
|
||||
imageUrl: "Rasm URL"
|
||||
remove: "O'chirib tashlash"
|
||||
removed: "Muvaffaqiyatli o'chirildi"
|
||||
removeAreYouSure: "“{x}”ni olib tashlamoqchi ekanligingizga ishonchingiz komilmi?"
|
||||
deleteAreYouSure: "“{x}”ni chindan ham yo'q qilmoqchimisiz?"
|
||||
resetAreYouSure: "Haqiqatan ham qayta tiklansinmi?"
|
||||
saved: "Saqlandi"
|
||||
messaging: "Suhbat"
|
||||
upload: "Yuklash"
|
||||
keepOriginalUploading: "Asl rasmni saqlang"
|
||||
uploadFromUrlDescription: "Yuklamoqchi bo'lgan faylingizga havola"
|
||||
explore: "Ko'rib chiqish"
|
||||
messageRead: "O‘qildi"
|
||||
startMessaging: "Yangi suhbatni boshlash"
|
||||
nUsersRead: "{n} tomonidan o'qildi"
|
||||
agreeTo: "Men {0} ga roziman"
|
||||
agree: "Rozi bo'lish"
|
||||
agreeBelow: "Men quyidagilarga roziman"
|
||||
basicNotesBeforeCreateAccount: "Muhim qaydlar"
|
||||
termsOfService: "Foydalanish shartlari"
|
||||
start: "Boshlash"
|
||||
home: "Bosh sahifa"
|
||||
activity: "Faollik"
|
||||
images: "Rasmlar"
|
||||
image: "Rasm"
|
||||
birthday: "Tug'ilgan kun"
|
||||
yearsOld: "{age} yashar"
|
||||
registeredDate: "Ro'yxatdan o'tgan sanasi"
|
||||
location: "Manzil"
|
||||
theme: "Rang sxemasi"
|
||||
themeForLightMode: "Yorug' rejim uchun rang sxemasi"
|
||||
themeForDarkMode: "Qorong'i rejim uchun rang sxemasi"
|
||||
light: "Yorug'"
|
||||
dark: "Qorongʻi"
|
||||
lightThemes: "Yorug‘ rang sxemasi"
|
||||
darkThemes: "Qorong'i rang sxemasi"
|
||||
drive: "Disk"
|
||||
fileName: "Fayl nomi"
|
||||
selectFile: "Faylni tanlang"
|
||||
selectFiles: "Fayllarni tanlang"
|
||||
selectFolder: "Jildni tanlang"
|
||||
selectFolders: "Jildlarni tanlang"
|
||||
renameFile: "Faylni nomini tahrirlash"
|
||||
folderName: "Jild nomi"
|
||||
createFolder: "Papka qo'shish"
|
||||
renameFolder: "Papka nomini o‘zgartirish"
|
||||
deleteFolder: "Papkani o‘chirish"
|
||||
addFile: "Fayl qo‘shish"
|
||||
emptyDrive: "Diskingiz bo'sh"
|
||||
emptyFolder: "Ushbu papka bo'sh"
|
||||
unableToDelete: "O'chirilmadi"
|
||||
inputNewFileName: "Yangi fayl nomini kiriting"
|
||||
inputNewFolderName: "Yangi papka nomini kiriting"
|
||||
copyUrl: "Bog'lamadan nusxa olish"
|
||||
rename: "Qayta nomlash"
|
||||
avatar: "Avatar"
|
||||
banner: "Banner"
|
||||
disconnectedFromServer: "Server bilan ulanish uzulib qoldi"
|
||||
reload: "Yangilash"
|
||||
doNothing: "E'tiborsiz qoldirish"
|
||||
reloadConfirm: "Timeline'ni yangilashni xohlaysizmi?"
|
||||
watch: "Kuzatmoq"
|
||||
unwatch: "Kuzatishni to'xtatish"
|
||||
accept: "Ruxsat"
|
||||
reject: "Rad etish"
|
||||
normal: "Yaxshi"
|
||||
instanceName: "Server nomi"
|
||||
instanceDescription: "Server tavsifi"
|
||||
maintainerName: "Qo'llab-quvvatlovchi"
|
||||
maintainerEmail: "Administratorning elektron pochtasi"
|
||||
tosUrl: "Foydalanish shartlariga havola"
|
||||
thisYear: "Joriy yil"
|
||||
thisMonth: "Shu oy"
|
||||
today: "Bugun"
|
||||
dayX: "{day}"
|
||||
monthX: "{month}"
|
||||
yearX: "{year}"
|
||||
pages: "Sahifalar"
|
||||
integration: "Integratsiya"
|
||||
connectService: "Ulash"
|
||||
disconnectService: "Uzish"
|
||||
enableLocalTimeline: "Mahalliy vaqt mintaqasini yoqing"
|
||||
enableGlobalTimeline: "Global vaqt mintaqasini yoqing"
|
||||
registration: "Ro'yxatdan o'tish"
|
||||
enableRegistration: "Ro'yxatdan o'tishni yoqing"
|
||||
invite: "Taklif qilish"
|
||||
inMb: "Megabaytlarda"
|
||||
basicInfo: "Asosiy ma'lumot"
|
||||
pinnedUsers: "Qadalgan foydalanuvchilar"
|
||||
pinnedPages: "Qadalgan Sahifalar"
|
||||
pinnedNotes: "Qadanlgan qayd"
|
||||
hcaptcha: "hCaptcha"
|
||||
enableHcaptcha: "hCaptchani yoqish"
|
||||
hcaptchaSiteKey: "Sayt kaliti"
|
||||
hcaptchaSecretKey: "Mahfiy kalit"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "reCAPTCHA ni yoqish"
|
||||
recaptchaSiteKey: "Sayt kaliti"
|
||||
recaptchaSecretKey: "Maxfiy kalit"
|
||||
turnstileSiteKey: "Sayt kaliti"
|
||||
turnstileSecretKey: "Maxfiy kalit"
|
||||
antennas: "Antennalar"
|
||||
manageAntennas: "Antennalarni boshqarish"
|
||||
name: "Ism"
|
||||
antennaSource: "Antenna manbai"
|
||||
silence: "Sukunat"
|
||||
exploreUsersCount: "{count} ta foydalanuvchi bor"
|
||||
popularTags: "Ommabop teglar"
|
||||
userList: "Ro'yxatlar"
|
||||
about: "Haqida"
|
||||
aboutMisskey: "Misskey haqida"
|
||||
administrator: "Administrator"
|
||||
token: "Tasdiqlash"
|
||||
2fa: "Ikki faktorli autentifikatsiya"
|
||||
totp: "Autentifikatsiya ilovasi"
|
||||
totpDescription: "Bir martalik parollarni kiritish uchun autentifikatsiya ilovasidan foydalaning"
|
||||
moderator: "Moderator"
|
||||
resetPassword: "Parolni tiklash"
|
||||
share: "Yuborish"
|
||||
notFound: "Topilmadi"
|
||||
uploadFolder: "Jildni yuklash"
|
||||
cacheClear: "Keshni tozalash"
|
||||
help: "Yordam"
|
||||
inputMessageHere: "Xabar kiriting"
|
||||
close: "Yopish"
|
||||
invites: "Taklif qilish"
|
||||
members: "A'zolar"
|
||||
title: "Sarlavha"
|
||||
text: "Matn"
|
||||
enable: "Yoqish"
|
||||
invitations: "Taklif qilish"
|
||||
dashboard: "Boshqaruv paneli"
|
||||
local: "Mahalliy"
|
||||
total: "Jami"
|
||||
weekOverWeekChanges: "Oxirgi haftadagi o'zgarishlar"
|
||||
dayOverDayChanges: "Kecha bo'lgan o'zgarishlar"
|
||||
appearance: "Tasgqi ko'rinish"
|
||||
clientSettings: "Klient sozlamalari"
|
||||
accountSettings: "Profil sozlamalari"
|
||||
sounds: "Tovushlar"
|
||||
sound: "ovoz"
|
||||
none: "Hechnima"
|
||||
volume: "Ovoz balandligi"
|
||||
details: "Batafsil"
|
||||
output: "Chiqish"
|
||||
menu: "Menyu"
|
||||
divider: "Ajratrmoq"
|
||||
addItem: "Element qo'shish"
|
||||
themeEditor: "Rang sxemasi muharriri"
|
||||
enableAll: "Yoqish"
|
||||
edit: "Tahrirlash"
|
||||
email: "Email"
|
||||
smtpHost: "Host"
|
||||
smtpUser: "Foydalanuvchi nomi"
|
||||
smtpPass: "Parol"
|
||||
copy: "Nusxa olish"
|
||||
notificationSetting: "Bildirishnoma sozlamalari"
|
||||
other: "Qo‘shimcha"
|
||||
behavior: "Hatti-harakatlar"
|
||||
sample: "Namuna"
|
||||
public: "Ommaviy"
|
||||
clearCache: "Keshni tozalash"
|
||||
onlineUsersCount: "Faol userlar"
|
||||
myTheme: "Mening rang sxemam"
|
||||
backgroundColor: "Fon"
|
||||
accentColor: "Urg'u"
|
||||
textColor: "Matn"
|
||||
info: "Haqida"
|
||||
user: "Foydalanuvchilar"
|
||||
global: "Global"
|
||||
squareAvatars: "Kvadrat avatarkalar"
|
||||
searchByGoogle: "Izlash"
|
||||
indefinitely: "Hech qachon"
|
||||
file: "Fayllar"
|
||||
label: "Yorliq"
|
||||
color: "Rang"
|
||||
_achievements:
|
||||
_types:
|
||||
_viewInstanceChart:
|
||||
title: "Tahlilchi"
|
||||
_ad:
|
||||
hide: "Boshqa ko'rsatilmasin"
|
||||
_email:
|
||||
_follow:
|
||||
title: "sizga obuna bo'ldi"
|
||||
_registry:
|
||||
key: "Kalit"
|
||||
keys: "Kalit"
|
||||
_instanceTicker:
|
||||
none: "Boshqa ko'rsatilmasin"
|
||||
always: "Doimo ko'rsatilsin"
|
||||
_theme:
|
||||
install: "Rang sxemasini o'rnatish"
|
||||
manage: "Rang sxemalarini boshqarish"
|
||||
code: "Rang sxemasining kodi"
|
||||
description: "Tavsif"
|
||||
installed: "{name} o'rnatildi"
|
||||
installedThemes: "O'rnatilgan rang sxemalari"
|
||||
alreadyInstalled: "Ushbu rang sxemasi allaqachon o'rnatilgan"
|
||||
invalid: "Ushbu rang sxemasining formati yaroqsiz"
|
||||
make: "Rang sxemasini yasash"
|
||||
base: "Asos"
|
||||
addConstant: "O'zgarmas qo'shish"
|
||||
constant: "O'zgarmas"
|
||||
color: "Rang"
|
||||
key: "Kalit"
|
||||
func: "Funksiyalar"
|
||||
funcKind: "Funksiya turi"
|
||||
argument: "Argument"
|
||||
darken: "Qoraytirish"
|
||||
lighten: "Yoritish"
|
||||
inputConstantName: "Ushbu o'zgarmas uchun nom kiriting"
|
||||
deleteConstantConfirm: "Siz rostdan ham {const} o'zgarmasni o'chirmoqchimisiz?"
|
||||
keys:
|
||||
accent: "Urg'u"
|
||||
bg: "Fon"
|
||||
fg: "Matn"
|
||||
focus: "Fokus"
|
||||
panel: "Panel"
|
||||
shadow: "Soya"
|
||||
header: "Sarlavha"
|
||||
navBg: "Yon panel foni"
|
||||
navFg: "Yon panel matni"
|
||||
mention: "Murojat"
|
||||
renote: "Qayta qaydetish"
|
||||
divider: "Ajratrmoq"
|
||||
accentDarken: "Urg'u (Qoraytirilgan)"
|
||||
accentLighten: "Urg'u (Yoritilgan)"
|
||||
fgHighlighted: "Belgilangan matn"
|
||||
_sfx:
|
||||
note: "Qaydlar"
|
||||
notification: "Xabarnomalar"
|
||||
chat: "Suhbat"
|
||||
_2fa:
|
||||
renewTOTPCancel: "Hozir emas"
|
||||
_permissions:
|
||||
"read:blocks": "Bloklangan foydalanuvchilar roʻyxatini koʻring"
|
||||
"write:blocks": "Bloklangan foydalanuvchilar roʻyxatini tahrirlang"
|
||||
_weekday:
|
||||
saturday: "Shanba"
|
||||
_widgets:
|
||||
profile: "Profil"
|
||||
instanceInfo: "Instans haqida ma'lumot"
|
||||
notifications: "Xabarnomalar"
|
||||
timeline: "Xronologiya"
|
||||
clock: "Soat"
|
||||
activity: "Faollik"
|
||||
photos: "Rasmlar"
|
||||
digitalClock: "Raqamli soat"
|
||||
unixClock: "UNIX soat"
|
||||
federation: "Federatsiya"
|
||||
button: "Tugma"
|
||||
jobQueue: "Vazifalar navbati"
|
||||
_userList:
|
||||
chooseList: "Ro'yxat tanlash"
|
||||
_cw:
|
||||
show: "Ko‘proq ko‘rish"
|
||||
chars: "{count} ta belgi(lar)"
|
||||
files: "{count} ta fayl(lar)"
|
||||
_poll:
|
||||
noOnlyOneChoice: "Kamida ikkita tanvol kerak"
|
||||
infinite: "Hech qachon"
|
||||
at: "...da tugatish"
|
||||
after: "...dan keyin tugatish"
|
||||
deadlineTime: "Vaqt"
|
||||
duration: "Davomiylik"
|
||||
votesCount: "{n} ovozlar"
|
||||
totalVotes: "Umuman {n} ovozlar"
|
||||
vote: "Ovoz berish"
|
||||
showResult: "Natijalarni ko'rish"
|
||||
voted: "Ovoz berildi"
|
||||
closed: "Yakunladi"
|
||||
remainingDays: "{d} kun {h} soat qoldi"
|
||||
remainingHours: "{h} soat {m} daqiqa qoldi"
|
||||
remainingMinutes: "{m} daqiqa {s} sekund qoldi"
|
||||
remainingSeconds: "{s} sekund qoldi"
|
||||
_visibility:
|
||||
public: "Ommaviy"
|
||||
publicDescription: "Sizning ovozingiz barcha foydalanuvchilarga ko'rinadi"
|
||||
home: "Bosh sahifa"
|
||||
followers: "Obunachilar"
|
||||
specified: "Bevosita"
|
||||
_profile:
|
||||
name: "Ism"
|
||||
username: "Foydalanuvchi nomi"
|
||||
description: "Biografiya"
|
||||
metadata: "Qo'shimcha ma'lumot"
|
||||
metadataLabel: "Yorliq"
|
||||
metadataContent: "Tarkib"
|
||||
changeBanner: "Bannerni o'zgartirish"
|
||||
_exportOrImport:
|
||||
allNotes: "Barcha qaydlar"
|
||||
followingList: "Obuna bo‘lish"
|
||||
muteList: "Ovozni o‘chirish"
|
||||
blockingList: "Bloklangan foydalanuvchilar"
|
||||
userLists: "Ro'yxatlar"
|
||||
_charts:
|
||||
federation: "Federatsiya"
|
||||
apRequest: "So'rovlar"
|
||||
usersTotal: "Foydalanuvchilarning umumiy soni"
|
||||
activeUsers: "Faol foydalanuvchilar"
|
||||
notesTotal: "Qaydlarning umumiy soni"
|
||||
filesTotal: "Fayllarning umumiy soni"
|
||||
_instanceCharts:
|
||||
requests: "So'rovlar"
|
||||
notes: "Qaydlar sonidagi farq"
|
||||
cacheSize: "Kesh hajmidagi farq"
|
||||
files: "Fayllar sonidagi farq"
|
||||
_timelines:
|
||||
home: "Bosh sahifa"
|
||||
local: "Mahalliy"
|
||||
social: "Ijtimoiy"
|
||||
global: "Global"
|
||||
_play:
|
||||
featured: "Mashhur"
|
||||
title: "Sarlavha"
|
||||
script: "Skript"
|
||||
summary: "Tavsif"
|
||||
_pages:
|
||||
newPage: "Yangi Sahifa yaratish"
|
||||
editPage: "Ushbu Sahifani tahrirlash"
|
||||
created: "Sahifa muvaffaqiyatli yaratildi"
|
||||
updated: "Sahifa muvaffaqiyatli tahrirlandi"
|
||||
deleted: "Sahifa muvaffaqiyatli o'chirildi"
|
||||
pageSetting: "Sahifa sozlamalari"
|
||||
nameAlreadyExists: "Ko'rsatilgan Sahifa URL'i allaqachon mavjud"
|
||||
invalidNameTitle: "Ko'rsatilgan Sahifa URL'i yaroqsiz"
|
||||
editThisPage: "Ushbu Sahifani tahrirlash"
|
||||
viewPage: "Sizning Sahifalaringizni ko'rish"
|
||||
my: "Mening Sahifalarim"
|
||||
featured: "Mashhur"
|
||||
contents: "Tarkib"
|
||||
title: "Sarlavha"
|
||||
url: "Sahifa URL'i"
|
||||
summary: "Sahifa bayoni"
|
||||
font: "Shrift"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
selectType: "Turni tanlang"
|
||||
contentBlocks: "Tarkib"
|
||||
blocks:
|
||||
text: "Matn"
|
||||
textarea: "Matn maydoni"
|
||||
section: "Bo'lim"
|
||||
image: "Rasmlar"
|
||||
button: "Tugma"
|
||||
note: "Biriktirilgan qayd"
|
||||
_note:
|
||||
id: "Qayd ID"
|
||||
detailed: "Batafsil ko'rinishi"
|
||||
_relayStatus:
|
||||
requesting: "Kutilmoqda"
|
||||
accepted: "Tasdiqlandi"
|
||||
rejected: "Rad etildi"
|
||||
_notification:
|
||||
fileUploaded: "Fayl muvaffaqiyatli yuklandi"
|
||||
youGotMention: "{name} sizni eslab o'tdi"
|
||||
youGotReply: "{name} sizga javob berdi"
|
||||
youGotQuote: "{name} sizdan iqtibos keltirdi"
|
||||
youRenoted: "{name} dan qayta qayd qilish"
|
||||
youWereFollowed: "sizga obuna bo'ldi"
|
||||
unreadAntennaNote: "Antenna {name}"
|
||||
_types:
|
||||
all: "Barchasi"
|
||||
follow: "Obuna bo‘lish"
|
||||
mention: "Murojat"
|
||||
renote: "Qayta qaydetish"
|
||||
quote: "Iqtibos keltirish"
|
||||
reaction: "Reaktsiyalar"
|
||||
receiveFollowRequest: "Qabul qilingan kuzatuv so'rovlari"
|
||||
_actions:
|
||||
reply: "Javob berish"
|
||||
renote: "Qayta qayd qilish"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "Har doim asosiy ustunni ko'rsatish"
|
||||
columnAlign: "Ustunlarni tekislash"
|
||||
addColumn: "Ustun qo'shish"
|
||||
configureColumn: "Ustun sozlamalari"
|
||||
swapLeft: "Chapdagi ustun bilan joyni almashtirish"
|
||||
swapRight: "O'ngdagi ustun bilan joyni almashtirish"
|
||||
swapUp: "Yuqoridagi ustun bilan joyni almashtirish"
|
||||
swapDown: "Quyidagi ustun bilan joyni almashtirish"
|
||||
profile: "Profil"
|
||||
newProfile: "Yangi profil"
|
||||
deleteProfile: "Profilni o‘chirib tashlash"
|
||||
_columns:
|
||||
main: "Asosiy"
|
||||
notifications: "Xabarnomalar"
|
||||
tl: "Xronologiya"
|
||||
antenna: "Antennalar"
|
||||
list: "Ro‘yxat"
|
||||
channel: "Kanal"
|
||||
mentions: "Eslatib o'tish"
|
||||
direct: "Bevosita qaydlar"
|
||||
roleTimeline: "Rol xronologiyasi"
|
||||
_webhookSettings:
|
||||
name: "Ism"
|
||||
active: "Yoqilgan"
|
||||
_events:
|
||||
renote: "Qayta qayd qilinganda"
|
||||
mention: "Eslanganda"
|
@@ -1091,6 +1091,9 @@ usedAt: "使用时间"
|
||||
unused: "未使用"
|
||||
used: "已使用"
|
||||
expired: "已过期"
|
||||
doYouAgree: "你同意吗?"
|
||||
beSureToReadThisAsItIsImportant: "请好好阅读,这真的很重要。"
|
||||
iHaveReadXCarefullyAndAgree: "我已经仔细阅读并同意了「{x}」的内容。"
|
||||
_initialAccountSetting:
|
||||
accountCreated: "账户创建完成了!"
|
||||
letsStartAccountSetup: "来进行帐户的初始设置吧。"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "13.14.0-beta.6",
|
||||
"version": "13.14.1",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"packageManager": "pnpm@8.6.0",
|
||||
"packageManager": "pnpm@8.6.9",
|
||||
"workspaces": [
|
||||
"packages/frontend",
|
||||
"packages/backend",
|
||||
|
@@ -2,14 +2,7 @@ import Redis from 'ioredis';
|
||||
import { loadConfig } from './built/config.js';
|
||||
|
||||
const config = loadConfig();
|
||||
const redis = new Redis({
|
||||
port: config.redis.port,
|
||||
host: config.redis.host,
|
||||
family: config.redis.family == null ? 0 : config.redis.family,
|
||||
password: config.redis.pass,
|
||||
keyPrefix: `${config.redis.prefix}:`,
|
||||
db: config.redis.db ?? 0,
|
||||
});
|
||||
const redis = new Redis(config.redis);
|
||||
|
||||
redis.on('connect', () => redis.disconnect());
|
||||
redis.on('error', (e) => {
|
||||
|
@@ -79,7 +79,6 @@
|
||||
"ajv": "8.12.0",
|
||||
"archiver": "5.3.1",
|
||||
"async-mutex": "^0.4.0",
|
||||
"autwh": "0.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"bullmq": "4.4.0",
|
||||
@@ -93,7 +92,6 @@
|
||||
"content-disposition": "0.5.4",
|
||||
"date-fns": "2.30.0",
|
||||
"deep-email-validator": "0.1.21",
|
||||
"escape-regexp": "0.0.1",
|
||||
"fastify": "4.20.0",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "18.5.0",
|
||||
@@ -139,7 +137,6 @@
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.1",
|
||||
"s-age": "1.1.2",
|
||||
"sanitize-html": "2.11.0",
|
||||
"semver": "7.5.4",
|
||||
"sharp": "0.32.3",
|
||||
@@ -157,7 +154,6 @@
|
||||
"typeorm": "0.3.17",
|
||||
"typescript": "5.1.6",
|
||||
"ulid": "2.3.0",
|
||||
"unzipper": "0.10.14",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.3",
|
||||
"ws": "8.13.0",
|
||||
@@ -172,7 +168,6 @@
|
||||
"@types/cbor": "6.0.0",
|
||||
"@types/color-convert": "2.0.0",
|
||||
"@types/content-disposition": "0.5.5",
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
"@types/fluent-ffmpeg": "2.1.21",
|
||||
"@types/jest": "29.5.3",
|
||||
"@types/js-yaml": "4.0.5",
|
||||
@@ -191,7 +186,6 @@
|
||||
"@types/qrcode": "1.5.1",
|
||||
"@types/random-seed": "0.3.3",
|
||||
"@types/ratelimiter": "3.4.4",
|
||||
"@types/redis": "4.0.11",
|
||||
"@types/rename": "1.0.4",
|
||||
"@types/sanitize-html": "2.9.0",
|
||||
"@types/semver": "7.5.0",
|
||||
@@ -199,10 +193,8 @@
|
||||
"@types/sinonjs__fake-timers": "8.1.2",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@types/tmp": "0.2.3",
|
||||
"@types/unzipper": "0.10.6",
|
||||
"@types/vary": "1.1.0",
|
||||
"@types/web-push": "3.3.2",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/ws": "8.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "5.61.0",
|
||||
"@typescript-eslint/parser": "5.61.0",
|
||||
|
@@ -41,14 +41,7 @@ const $meilisearch: Provider = {
|
||||
const $redis: Provider = {
|
||||
provide: DI.redis,
|
||||
useFactory: (config: Config) => {
|
||||
return new Redis.Redis({
|
||||
port: config.redis.port,
|
||||
host: config.redis.host,
|
||||
family: config.redis.family == null ? 0 : config.redis.family,
|
||||
password: config.redis.pass,
|
||||
keyPrefix: `${config.redis.prefix}:`,
|
||||
db: config.redis.db ?? 0,
|
||||
});
|
||||
return new Redis.Redis(config.redis);
|
||||
},
|
||||
inject: [DI.config],
|
||||
};
|
||||
@@ -56,14 +49,7 @@ const $redis: Provider = {
|
||||
const $redisForPub: Provider = {
|
||||
provide: DI.redisForPub,
|
||||
useFactory: (config: Config) => {
|
||||
const redis = new Redis.Redis({
|
||||
port: config.redisForPubsub.port,
|
||||
host: config.redisForPubsub.host,
|
||||
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
|
||||
password: config.redisForPubsub.pass,
|
||||
keyPrefix: `${config.redisForPubsub.prefix}:`,
|
||||
db: config.redisForPubsub.db ?? 0,
|
||||
});
|
||||
const redis = new Redis.Redis(config.redisForPubsub);
|
||||
return redis;
|
||||
},
|
||||
inject: [DI.config],
|
||||
@@ -72,14 +58,7 @@ const $redisForPub: Provider = {
|
||||
const $redisForSub: Provider = {
|
||||
provide: DI.redisForSub,
|
||||
useFactory: (config: Config) => {
|
||||
const redis = new Redis.Redis({
|
||||
port: config.redisForPubsub.port,
|
||||
host: config.redisForPubsub.host,
|
||||
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
|
||||
password: config.redisForPubsub.pass,
|
||||
keyPrefix: `${config.redisForPubsub.prefix}:`,
|
||||
db: config.redisForPubsub.db ?? 0,
|
||||
});
|
||||
const redis = new Redis.Redis(config.redisForPubsub);
|
||||
redis.subscribe(config.host);
|
||||
return redis;
|
||||
},
|
||||
|
@@ -6,6 +6,16 @@ import * as fs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import type { RedisOptions } from 'ioredis';
|
||||
|
||||
type RedisOptionsSource = Partial<RedisOptions> & {
|
||||
host: string;
|
||||
port: number;
|
||||
family?: number;
|
||||
pass: string;
|
||||
db?: number;
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* ユーザーが設定する必要のある情報
|
||||
@@ -35,30 +45,9 @@ export type Source = {
|
||||
user: string;
|
||||
pass: string;
|
||||
}[];
|
||||
redis: {
|
||||
host: string;
|
||||
port: number;
|
||||
family?: number;
|
||||
pass: string;
|
||||
db?: number;
|
||||
prefix?: string;
|
||||
};
|
||||
redisForPubsub?: {
|
||||
host: string;
|
||||
port: number;
|
||||
family?: number;
|
||||
pass: string;
|
||||
db?: number;
|
||||
prefix?: string;
|
||||
};
|
||||
redisForJobQueue?: {
|
||||
host: string;
|
||||
port: number;
|
||||
family?: number;
|
||||
pass: string;
|
||||
db?: number;
|
||||
prefix?: string;
|
||||
};
|
||||
redis: RedisOptionsSource;
|
||||
redisForPubsub?: RedisOptionsSource;
|
||||
redisForJobQueue?: RedisOptionsSource;
|
||||
meilisearch?: {
|
||||
host: string;
|
||||
port: string;
|
||||
@@ -119,8 +108,9 @@ export type Mixin = {
|
||||
mediaProxy: string;
|
||||
externalMediaProxyEnabled: boolean;
|
||||
videoThumbnailGenerator: string | null;
|
||||
redisForPubsub: NonNullable<Source['redisForPubsub']>;
|
||||
redisForJobQueue: NonNullable<Source['redisForJobQueue']>;
|
||||
redis: RedisOptions & RedisOptionsSource;
|
||||
redisForPubsub: RedisOptions & RedisOptionsSource;
|
||||
redisForJobQueue: RedisOptions & RedisOptionsSource;
|
||||
};
|
||||
|
||||
export type Config = Source & Mixin;
|
||||
@@ -182,9 +172,9 @@ export function loadConfig() {
|
||||
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
|
||||
: null;
|
||||
|
||||
if (!config.redis.prefix) config.redis.prefix = mixin.host;
|
||||
if (config.redisForPubsub == null) config.redisForPubsub = config.redis;
|
||||
if (config.redisForJobQueue == null) config.redisForJobQueue = config.redis;
|
||||
mixin.redis = convertRedisOptions(config.redis, mixin.host);
|
||||
mixin.redisForPubsub = config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, mixin.host) : mixin.redis;
|
||||
mixin.redisForJobQueue = config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, mixin.host) : mixin.redis;
|
||||
|
||||
return Object.assign(config, mixin);
|
||||
}
|
||||
@@ -196,3 +186,14 @@ function tryCreateUrl(url: string) {
|
||||
throw new Error(`url="${url}" is not a valid URL.`);
|
||||
}
|
||||
}
|
||||
|
||||
function convertRedisOptions(options: RedisOptionsSource, host: string): RedisOptions & RedisOptionsSource {
|
||||
return {
|
||||
...options,
|
||||
password: options.pass,
|
||||
prefix: options.prefix ?? host,
|
||||
family: options.family == null ? 0 : options.family,
|
||||
keyPrefix: `${options.prefix ?? host}:`,
|
||||
db: options.db ?? 0,
|
||||
};
|
||||
}
|
||||
|
@@ -4,10 +4,9 @@ import { IsNull, In, MoreThan, Not } from 'typeorm';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
||||
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
||||
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, Muting, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
|
@@ -33,7 +33,7 @@ export class CreateSystemUserService {
|
||||
// Generate secret
|
||||
const secret = generateNativeUserToken();
|
||||
|
||||
const keyPair = await genRsaKeyPair(4096);
|
||||
const keyPair = await genRsaKeyPair();
|
||||
|
||||
let account!: User;
|
||||
|
||||
|
@@ -2,6 +2,7 @@ import { URL } from 'node:url';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { JSDOM } from 'jsdom';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import * as Redis from 'ioredis';
|
||||
import type { Instance } from '@/models/entities/Instance.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
@@ -10,7 +11,6 @@ import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||
import type { DOMWindow } from 'jsdom';
|
||||
import * as Redis from 'ioredis';
|
||||
|
||||
type NodeInfo = {
|
||||
openRegistrations?: unknown;
|
||||
|
@@ -574,7 +574,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||
where: {
|
||||
userId: data.reply.userId,
|
||||
threadId: data.reply.threadId ?? data.reply.id,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!isThreadMuted) {
|
||||
|
@@ -108,7 +108,7 @@ export class QueueService {
|
||||
removeOnFail: true,
|
||||
};
|
||||
|
||||
await this.deliverQueue.addBulk(Array.from(inboxes.entries()).map(d => ({
|
||||
await this.deliverQueue.addBulk(Array.from(inboxes.entries(), d => ({
|
||||
name: d[0],
|
||||
data: {
|
||||
user,
|
||||
|
@@ -8,8 +8,9 @@ import type { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { WebfingerService } from '@/core/WebfingerService.js';
|
||||
import { ILink, WebfingerService } from '@/core/WebfingerService.js';
|
||||
import { RemoteLoggerService } from '@/core/RemoteLoggerService.js';
|
||||
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
|
||||
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
||||
@@ -27,6 +28,7 @@ export class RemoteUserResolveService {
|
||||
private utilityService: UtilityService,
|
||||
private webfingerService: WebfingerService,
|
||||
private remoteLoggerService: RemoteLoggerService,
|
||||
private apDbResolverService: ApDbResolverService,
|
||||
private apPersonService: ApPersonService,
|
||||
) {
|
||||
this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user');
|
||||
@@ -67,6 +69,22 @@ export class RemoteUserResolveService {
|
||||
if (user == null) {
|
||||
const self = await this.resolveSelf(acctLower);
|
||||
|
||||
if (self.href.startsWith(this.config.url)) {
|
||||
const local = this.apDbResolverService.parseUri(self.href);
|
||||
if (local.local && local.type === 'users') {
|
||||
// the LR points to local
|
||||
return (await this.apDbResolverService
|
||||
.getUserFromApId(self.href)
|
||||
.then((u) => {
|
||||
if (u == null) {
|
||||
throw new Error('local user not found');
|
||||
} else {
|
||||
return u;
|
||||
}
|
||||
})) as LocalUser;
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`);
|
||||
return await this.apPersonService.createPerson(self.href);
|
||||
}
|
||||
@@ -119,7 +137,7 @@ export class RemoteUserResolveService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async resolveSelf(acctLower: string) {
|
||||
private async resolveSelf(acctLower: string): Promise<ILink> {
|
||||
this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`);
|
||||
const finger = await this.webfingerService.webfinger(acctLower).catch(err => {
|
||||
this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ err.statusCode ?? err.message }`);
|
||||
|
@@ -220,14 +220,19 @@ export class RoleService implements OnApplicationShutdown {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getUserRoles(userId: User['id']) {
|
||||
public async getUserAssigns(userId: User['id']) {
|
||||
const now = Date.now();
|
||||
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
|
||||
// 期限切れのロールを除外
|
||||
assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now));
|
||||
const assignedRoleIds = assigns.map(x => x.roleId);
|
||||
return assigns;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getUserRoles(userId: User['id']) {
|
||||
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
||||
const assignedRoles = roles.filter(r => assignedRoleIds.includes(r.id));
|
||||
const assigns = await this.getUserAssigns(userId);
|
||||
const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
|
||||
const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
|
||||
const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula));
|
||||
return [...assignedRoles, ...matchedCondRoles];
|
||||
|
@@ -92,7 +92,7 @@ export class SignupService {
|
||||
|
||||
const keyPair = await new Promise<string[]>((res, rej) =>
|
||||
generateKeyPair('rsa', {
|
||||
modulusLength: 4096,
|
||||
modulusLength: 2048,
|
||||
publicKeyEncoding: {
|
||||
type: 'spki',
|
||||
format: 'pem',
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { IsNull } from 'typeorm';
|
||||
import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
@@ -21,9 +22,8 @@ import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import Logger from '../logger.js';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { AccountMoveService } from '@/core/AccountMoveService.js';
|
||||
import Logger from '../logger.js';
|
||||
|
||||
const logger = new Logger('following/create');
|
||||
|
||||
@@ -322,7 +322,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||
where: {
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (following === null || !following.follower || !following.followee) {
|
||||
@@ -412,8 +412,8 @@ export class UserFollowingService implements OnModuleInit {
|
||||
followerId: user.id,
|
||||
followee: {
|
||||
movedToUri: IsNull(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
const nonMovedFollowers = await this.followingsRepository.count({
|
||||
relations: {
|
||||
@@ -423,8 +423,8 @@ export class UserFollowingService implements OnModuleInit {
|
||||
followeeId: user.id,
|
||||
follower: {
|
||||
movedToUri: IsNull(),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
await this.usersRepository.update(
|
||||
{ id: user.id },
|
||||
@@ -646,7 +646,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||
where: {
|
||||
followeeId: followee.id,
|
||||
followerId: follower.id,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!following || !following.followee || !following.follower) return;
|
||||
|
@@ -52,7 +52,7 @@ export class VideoProcessingService {
|
||||
query({
|
||||
thumbnail: '1',
|
||||
url,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,12 @@ import { query as urlQuery } from '@/misc/prelude/url.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
|
||||
type ILink = {
|
||||
export type ILink = {
|
||||
href: string;
|
||||
rel?: string;
|
||||
};
|
||||
|
||||
type IWebFinger = {
|
||||
export type IWebFinger = {
|
||||
links: ILink[];
|
||||
subject: string;
|
||||
};
|
||||
|
@@ -95,7 +95,7 @@ export class ApAudienceService {
|
||||
private isPublic(id: string): boolean {
|
||||
return [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
'as#Public',
|
||||
'as:Public',
|
||||
'Public',
|
||||
].includes(id);
|
||||
}
|
||||
|
@@ -220,6 +220,23 @@ export class ApPersonService implements OnModuleInit {
|
||||
return null;
|
||||
}
|
||||
|
||||
private async resolveAvatarAndBanner(user: RemoteUser, icon: any, image: any): Promise<Pick<RemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> {
|
||||
const [avatar, banner] = await Promise.all([icon, image].map(img => {
|
||||
if (img == null) return null;
|
||||
if (user == null) throw new Error('failed to create user: user is null');
|
||||
return this.apImageService.resolveImage(user, img).catch(() => null);
|
||||
}));
|
||||
|
||||
return {
|
||||
avatarId: avatar?.id ?? null,
|
||||
bannerId: banner?.id ?? null,
|
||||
avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null,
|
||||
bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null,
|
||||
avatarBlurhash: avatar?.blurhash ?? null,
|
||||
bannerBlurhash: banner?.blurhash ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Personを作成します。
|
||||
*/
|
||||
@@ -259,6 +276,16 @@ export class ApPersonService implements OnModuleInit {
|
||||
|
||||
// Create user
|
||||
let user: RemoteUser | null = null;
|
||||
|
||||
//#region カスタム絵文字取得
|
||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host)
|
||||
.then(_emojis => _emojis.map(emoji => emoji.name))
|
||||
.catch(err => {
|
||||
this.logger.error(`error occured while fetching user emojis`, { stack: err });
|
||||
return [];
|
||||
});
|
||||
//#endregion
|
||||
|
||||
try {
|
||||
// Start transaction
|
||||
await this.db.transaction(async transactionalEntityManager => {
|
||||
@@ -285,6 +312,7 @@ export class ApPersonService implements OnModuleInit {
|
||||
tags,
|
||||
isBot,
|
||||
isCat: (person as any).isCat === true,
|
||||
emojis,
|
||||
})) as RemoteUser;
|
||||
|
||||
await transactionalEntityManager.save(new UserProfile({
|
||||
@@ -321,6 +349,9 @@ export class ApPersonService implements OnModuleInit {
|
||||
|
||||
if (user == null) throw new Error('failed to create user: user is null');
|
||||
|
||||
// Register to the cache
|
||||
this.cacheService.uriPersonCache.set(user.uri, user);
|
||||
|
||||
// Register host
|
||||
this.federatedInstanceService.fetch(host).then(async i => {
|
||||
this.instancesRepository.increment({ id: i.id }, 'usersCount', 1);
|
||||
@@ -336,45 +367,16 @@ export class ApPersonService implements OnModuleInit {
|
||||
this.hashtagService.updateUsertags(user, tags);
|
||||
|
||||
//#region アバターとヘッダー画像をフェッチ
|
||||
const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => {
|
||||
if (img == null) return null;
|
||||
if (user == null) throw new Error('failed to create user: user is null');
|
||||
return this.apImageService.resolveImage(user, img).catch(() => null);
|
||||
}));
|
||||
try {
|
||||
const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image);
|
||||
await this.usersRepository.update(user.id, updates);
|
||||
user = { ...user, ...updates };
|
||||
|
||||
const avatarId = avatar?.id ?? null;
|
||||
const bannerId = banner?.id ?? null;
|
||||
const avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null;
|
||||
const bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null;
|
||||
const avatarBlurhash = avatar?.blurhash ?? null;
|
||||
const bannerBlurhash = banner?.blurhash ?? null;
|
||||
|
||||
await this.usersRepository.update(user.id, {
|
||||
avatarId,
|
||||
bannerId,
|
||||
avatarUrl,
|
||||
bannerUrl,
|
||||
avatarBlurhash,
|
||||
bannerBlurhash,
|
||||
});
|
||||
|
||||
user.avatarId = avatarId;
|
||||
user.bannerId = bannerId;
|
||||
user.avatarUrl = avatarUrl;
|
||||
user.bannerUrl = bannerUrl;
|
||||
user.avatarBlurhash = avatarBlurhash;
|
||||
user.bannerBlurhash = bannerBlurhash;
|
||||
//#endregion
|
||||
|
||||
//#region カスタム絵文字取得
|
||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => {
|
||||
this.logger.info(`extractEmojis: ${err}`);
|
||||
return [];
|
||||
});
|
||||
|
||||
const emojiNames = emojis.map(emoji => emoji.name);
|
||||
|
||||
await this.usersRepository.update(user.id, { emojis: emojiNames });
|
||||
// Register to the cache
|
||||
this.cacheService.uriPersonCache.set(user.uri, user);
|
||||
} catch (err) {
|
||||
this.logger.error('error occured while fetching user avatar/banner', { stack: err });
|
||||
}
|
||||
//#endregion
|
||||
|
||||
await this.updateFeatured(user.id, resolver).catch(err => this.logger.error(err));
|
||||
@@ -400,7 +402,7 @@ export class ApPersonService implements OnModuleInit {
|
||||
if (uri.startsWith(`${this.config.url}/`)) return;
|
||||
|
||||
//#region このサーバーに既に登録されているか
|
||||
const exist = await this.usersRepository.findOneBy({ uri }) as RemoteUser | null;
|
||||
const exist = await this.fetchPerson(uri) as RemoteUser | null;
|
||||
if (exist === null) return;
|
||||
//#endregion
|
||||
|
||||
@@ -413,12 +415,6 @@ export class ApPersonService implements OnModuleInit {
|
||||
|
||||
this.logger.info(`Updating the Person: ${person.id}`);
|
||||
|
||||
// アバターとヘッダー画像をフェッチ
|
||||
const [avatar, banner] = await Promise.all([person.icon, person.image].map(img => {
|
||||
if (img == null) return null;
|
||||
return this.apImageService.resolveImage(exist, img).catch(() => null);
|
||||
}));
|
||||
|
||||
// カスタム絵文字取得
|
||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => {
|
||||
this.logger.info(`extractEmojis: ${e}`);
|
||||
@@ -454,6 +450,7 @@ export class ApPersonService implements OnModuleInit {
|
||||
movedToUri: person.movedTo ?? null,
|
||||
alsoKnownAs: person.alsoKnownAs ?? null,
|
||||
isExplorable: person.discoverable,
|
||||
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
|
||||
} as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
||||
|
||||
const moving = ((): boolean => {
|
||||
@@ -476,18 +473,6 @@ export class ApPersonService implements OnModuleInit {
|
||||
|
||||
if (moving) updates.movedAt = new Date();
|
||||
|
||||
if (avatar) {
|
||||
updates.avatarId = avatar.id;
|
||||
updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
|
||||
updates.avatarBlurhash = avatar.blurhash;
|
||||
}
|
||||
|
||||
if (banner) {
|
||||
updates.bannerId = banner.id;
|
||||
updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
|
||||
updates.bannerBlurhash = banner.blurhash;
|
||||
}
|
||||
|
||||
// Update user
|
||||
await this.usersRepository.update(exist.id, updates);
|
||||
|
||||
|
@@ -3,8 +3,8 @@ import { DI } from '@/di-symbols.js';
|
||||
import type { AbuseUserReportsRepository } from '@/models/index.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class AbuseUserReportEntityService {
|
||||
|
@@ -4,8 +4,8 @@ import type { AuthSessionsRepository } from '@/models/index.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { AuthSession } from '@/models/entities/AuthSession.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import { AppEntityService } from './AppEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { AppEntityService } from './AppEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class AuthSessionEntityService {
|
||||
|
@@ -50,7 +50,7 @@ export class ChannelEntityService {
|
||||
const hasUnreadNote = meId ? await this.noteUnreadsRepository.exist({
|
||||
where: {
|
||||
noteChannelId: channel.id,
|
||||
userId: meId
|
||||
userId: meId,
|
||||
},
|
||||
}) : undefined;
|
||||
|
||||
|
@@ -4,8 +4,8 @@ import type { FollowRequestsRepository } from '@/models/index.js';
|
||||
import type { } from '@/models/entities/Blocking.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import type { FollowRequest } from '@/models/entities/FollowRequest.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class FollowRequestEntityService {
|
||||
|
@@ -3,8 +3,8 @@ import { DI } from '@/di-symbols.js';
|
||||
import type { GalleryLikesRepository } from '@/models/index.js';
|
||||
import type { } from '@/models/entities/Blocking.js';
|
||||
import type { GalleryLike } from '@/models/entities/GalleryLike.js';
|
||||
import { GalleryPostEntityService } from './GalleryPostEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { GalleryPostEntityService } from './GalleryPostEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class GalleryLikeEntityService {
|
||||
|
@@ -4,8 +4,8 @@ import type { ModerationLogsRepository } from '@/models/index.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { } from '@/models/entities/Blocking.js';
|
||||
import type { ModerationLog } from '@/models/entities/ModerationLog.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class ModerationLogEntityService {
|
||||
|
@@ -4,8 +4,8 @@ import type { NoteFavoritesRepository } from '@/models/index.js';
|
||||
import type { } from '@/models/entities/Blocking.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import type { NoteFavorite } from '@/models/entities/NoteFavorite.js';
|
||||
import { NoteEntityService } from './NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { NoteEntityService } from './NoteEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class NoteFavoriteEntityService {
|
||||
|
@@ -4,8 +4,8 @@ import type { PageLikesRepository } from '@/models/index.js';
|
||||
import type { } from '@/models/entities/Blocking.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import type { PageLike } from '@/models/entities/PageLike.js';
|
||||
import { PageEntityService } from './PageEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { PageEntityService } from './PageEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class PageLikeEntityService {
|
||||
|
@@ -3,8 +3,8 @@ import { DI } from '@/di-symbols.js';
|
||||
import type { SigninsRepository } from '@/models/index.js';
|
||||
import type { } from '@/models/entities/Blocking.js';
|
||||
import type { Signin } from '@/models/entities/Signin.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class SigninEntityService {
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { Writable, WritableOptions } from "node:stream";
|
||||
import { Writable, WritableOptions } from 'node:stream';
|
||||
|
||||
export class DevNull extends Writable implements NodeJS.WritableStream {
|
||||
constructor(opts?: WritableOptions) {
|
||||
super(opts);
|
||||
}
|
||||
constructor(opts?: WritableOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
_write (chunk: any, encoding: BufferEncoding, cb: (err?: Error | null) => void) {
|
||||
setImmediate(cb);
|
||||
}
|
||||
_write (chunk: any, encoding: BufferEncoding, cb: (err?: Error | null) => void) {
|
||||
setImmediate(cb);
|
||||
}
|
||||
}
|
||||
|
@@ -5,10 +5,10 @@ const CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||||
export const ulidRegExp = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
|
||||
|
||||
export function parseUlid(id: string): { date: Date; } {
|
||||
const timestamp = id.slice(0, 10);
|
||||
let time = 0;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
time = time * 32 + CHARS.indexOf(timestamp[i]);
|
||||
}
|
||||
return { date: new Date(time) };
|
||||
const timestamp = id.slice(0, 10);
|
||||
let time = 0;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
time = time * 32 + CHARS.indexOf(timestamp[i]);
|
||||
}
|
||||
return { date: new Date(time) };
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ export async function awaitAll<T>(obj: Promiseable<T>): Promise<T> {
|
||||
const resolvedValues = await Promise.all(values.map(value =>
|
||||
(!value || !value.constructor || value.constructor.name !== 'Object')
|
||||
? value
|
||||
: awaitAll(value)
|
||||
: awaitAll(value),
|
||||
));
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
|
@@ -49,7 +49,6 @@ import { User } from '@/models/entities/User.js';
|
||||
import { UserIp } from '@/models/entities/UserIp.js';
|
||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
||||
import { UserList } from '@/models/entities/UserList.js';
|
||||
import { UserListFavorite } from './entities/UserListFavorite.js';
|
||||
import { UserListJoining } from '@/models/entities/UserListJoining.js';
|
||||
import { UserNotePining } from '@/models/entities/UserNotePining.js';
|
||||
import { UserPending } from '@/models/entities/UserPending.js';
|
||||
@@ -64,6 +63,7 @@ import { Role } from '@/models/entities/Role.js';
|
||||
import { RoleAssignment } from '@/models/entities/RoleAssignment.js';
|
||||
import { Flash } from '@/models/entities/Flash.js';
|
||||
import { FlashLike } from '@/models/entities/FlashLike.js';
|
||||
import { UserListFavorite } from './entities/UserListFavorite.js';
|
||||
import type { Repository } from 'typeorm';
|
||||
|
||||
export {
|
||||
|
@@ -15,11 +15,8 @@ export const QUEUE = {
|
||||
export function baseQueueOptions(config: Config, queueName: typeof QUEUE[keyof typeof QUEUE]): Bull.QueueOptions {
|
||||
return {
|
||||
connection: {
|
||||
port: config.redisForJobQueue.port,
|
||||
host: config.redisForJobQueue.host,
|
||||
family: config.redisForJobQueue.family == null ? 0 : config.redisForJobQueue.family,
|
||||
password: config.redisForJobQueue.pass,
|
||||
db: config.redisForJobQueue.db ?? 0,
|
||||
...config.redisForJobQueue,
|
||||
keyPrefix: undefined
|
||||
},
|
||||
prefix: config.redisForJobQueue.prefix ? `${config.redisForJobQueue.prefix}:queue:${queueName}` : `queue:${queueName}`,
|
||||
};
|
||||
|
@@ -9,10 +9,10 @@ import type { DriveFile } from '@/models/entities/DriveFile.js';
|
||||
import type { Note } from '@/models/entities/Note.js';
|
||||
import { EmailService } from '@/core/EmailService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { SearchService } from '@/core/SearchService.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
import type { DbUserDeleteJobData } from '../types.js';
|
||||
import { SearchService } from "@/core/SearchService.js";
|
||||
|
||||
@Injectable()
|
||||
export class DeleteAccountProcessorService {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import * as fs from 'node:fs';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ZipReader } from 'slacc';
|
||||
import { DataSource } from 'typeorm';
|
||||
import unzipper from 'unzipper';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { EmojisRepository, DriveFilesRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { Config } from '@/config.js';
|
||||
@@ -72,9 +72,9 @@ export class ImportCustomEmojisProcessorService {
|
||||
}
|
||||
|
||||
const outputPath = path + '/emojis';
|
||||
const unzipStream = fs.createReadStream(destPath);
|
||||
const extractor = unzipper.Extract({ path: outputPath });
|
||||
extractor.on('close', async () => {
|
||||
try {
|
||||
this.logger.succ(`Unzipping to ${outputPath}`);
|
||||
ZipReader.withDestinationPath(outputPath).viaBuffer(await fs.promises.readFile(destPath));
|
||||
const metaRaw = fs.readFileSync(outputPath + '/meta.json', 'utf-8');
|
||||
const meta = JSON.parse(metaRaw);
|
||||
|
||||
@@ -115,8 +115,12 @@ export class ImportCustomEmojisProcessorService {
|
||||
cleanup();
|
||||
|
||||
this.logger.succ('Imported');
|
||||
});
|
||||
unzipStream.pipe(extractor);
|
||||
this.logger.succ(`Unzipping to ${outputPath}`);
|
||||
} catch (e) {
|
||||
if (e instanceof Error || typeof e === 'string') {
|
||||
this.logger.error(e);
|
||||
}
|
||||
cleanup();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type * as Bull from 'bullmq';
|
||||
|
||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type Logger from '@/logger.js';
|
||||
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import { RelationshipJobData } from '../types.js';
|
||||
import type { UsersRepository } from '@/models/index.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { LocalUser, RemoteUser } from '@/models/entities/User.js';
|
||||
import { RelationshipJobData } from '../types.js';
|
||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||
import type * as Bull from 'bullmq';
|
||||
|
||||
@Injectable()
|
||||
export class RelationshipProcessorService {
|
||||
|
@@ -3,6 +3,8 @@ import { fileURLToPath } from 'node:url';
|
||||
import { dirname } from 'node:path';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import rename from 'rename';
|
||||
import sharp from 'sharp';
|
||||
import { sharpBmp } from 'sharp-read-bmp';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { DriveFile, DriveFilesRepository } from '@/models/index.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
@@ -18,11 +20,9 @@ import { contentDisposition } from '@/misc/content-disposition.js';
|
||||
import { FileInfoService } from '@/core/FileInfoService.js';
|
||||
import { LoggerService } from '@/core/LoggerService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||
import sharp from 'sharp';
|
||||
import { sharpBmp } from 'sharp-read-bmp';
|
||||
import { correctFilename } from '@/misc/correct-filename.js';
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
@@ -180,8 +180,8 @@ export class FileServerService {
|
||||
reply.header('Content-Disposition',
|
||||
contentDisposition(
|
||||
'inline',
|
||||
correctFilename(file.filename, image.ext)
|
||||
)
|
||||
correctFilename(file.filename, image.ext),
|
||||
),
|
||||
);
|
||||
return image.data;
|
||||
}
|
||||
@@ -278,11 +278,11 @@ export class FileServerService {
|
||||
};
|
||||
} else {
|
||||
const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) }))
|
||||
.resize({
|
||||
height: 'emoji' in request.query ? 128 : 320,
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.webp(webpDefault);
|
||||
.resize({
|
||||
height: 'emoji' in request.query ? 128 : 320,
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.webp(webpDefault);
|
||||
|
||||
image = {
|
||||
data,
|
||||
@@ -355,8 +355,8 @@ export class FileServerService {
|
||||
reply.header('Content-Disposition',
|
||||
contentDisposition(
|
||||
'inline',
|
||||
correctFilename(file.filename, image.ext)
|
||||
)
|
||||
correctFilename(file.filename, image.ext),
|
||||
),
|
||||
);
|
||||
return image.data;
|
||||
} catch (e) {
|
||||
|
@@ -1,18 +1,18 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { IsNull } from 'typeorm';
|
||||
import vary from 'vary';
|
||||
import fastifyAccepts from '@fastify/accepts';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { UsersRepository } from '@/models/index.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { escapeAttribute, escapeValue } from '@/misc/prelude/xml.js';
|
||||
import type { User } from '@/models/entities/User.js';
|
||||
import * as Acct from '@/misc/acct.js';
|
||||
import { NodeinfoServerService } from './NodeinfoServerService.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import type { FindOptionsWhere } from 'typeorm';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { NodeinfoServerService } from './NodeinfoServerService.js';
|
||||
import type { FindOptionsWhere } from 'typeorm';
|
||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
import fastifyAccepts from '@fastify/accepts';
|
||||
|
||||
@Injectable()
|
||||
export class WellKnownServerService {
|
||||
|
@@ -13,9 +13,9 @@ import { EmailService } from '@/core/EmailService.js';
|
||||
import { LocalUser } from '@/models/entities/User.js';
|
||||
import { FastifyReplyError } from '@/misc/fastify-reply-error.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
import { SigninService } from './SigninService.js';
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
|
||||
|
||||
@Injectable()
|
||||
export class SignupApiService {
|
||||
|
@@ -4,6 +4,7 @@ import type { DriveFilesRepository } from '@/models/index.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
||||
export const meta = {
|
||||
@@ -55,6 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
private customEmojiService: CustomEmojiService,
|
||||
|
||||
private emojiEntityService: EmojiEntityService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
@@ -77,9 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
emojiId: emoji.id,
|
||||
});
|
||||
|
||||
return {
|
||||
id: emoji.id,
|
||||
};
|
||||
return this.emojiEntityService.packDetailed(emoji);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -91,7 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
if (queryarry) {
|
||||
emojis = emojis.filter(emoji =>
|
||||
queryarry.includes(`:${emoji.name}:`)
|
||||
queryarry.includes(`:${emoji.name}:`),
|
||||
);
|
||||
} else {
|
||||
emojis = emojis.filter(emoji =>
|
||||
|
@@ -60,7 +60,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
}
|
||||
|
||||
query.limit(ps.limit);
|
||||
query.skip(ps.offset);
|
||||
query.offset(ps.offset);
|
||||
|
||||
const tickets = await query.getMany();
|
||||
|
||||
|
@@ -61,6 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
const signins = await this.signinsRepository.findBy({ userId: user.id });
|
||||
|
||||
const roleAssigns = await this.roleService.getUserAssigns(user.id);
|
||||
const roles = await this.roleService.getUserRoles(user.id);
|
||||
|
||||
return {
|
||||
@@ -85,6 +86,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
signins,
|
||||
policies: await this.roleService.getUserPolicies(user.id),
|
||||
roles: await this.roleEntityService.packMany(roles, me),
|
||||
roleAssigns: roleAssigns.map(a => ({
|
||||
createdAt: a.createdAt.toISOString(),
|
||||
expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null,
|
||||
roleId: a.roleId,
|
||||
})),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@@ -105,7 +105,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
}
|
||||
|
||||
query.limit(ps.limit);
|
||||
query.skip(ps.offset);
|
||||
query.offset(ps.offset);
|
||||
|
||||
const users = await query.getMany();
|
||||
|
||||
|
@@ -76,6 +76,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
throw new ApiError(meta.errors.noSuchAntenna);
|
||||
}
|
||||
|
||||
this.antennasRepository.update(antenna.id, {
|
||||
isActive: true,
|
||||
lastUsedAt: new Date(),
|
||||
});
|
||||
|
||||
const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1
|
||||
const noteIdsRes = await this.redisClient.xrevrange(
|
||||
`antennaTimeline:${antenna.id}`,
|
||||
@@ -112,11 +117,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
this.noteReadService.read(me.id, notes);
|
||||
}
|
||||
|
||||
this.antennasRepository.update(antenna.id, {
|
||||
isActive: true,
|
||||
lastUsedAt: new Date(),
|
||||
});
|
||||
|
||||
return await this.noteEntityService.packMany(notes, me);
|
||||
});
|
||||
}
|
||||
|
@@ -112,6 +112,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
withReplies: ps.withReplies,
|
||||
withFile: ps.withFile,
|
||||
notify: ps.notify,
|
||||
isActive: true,
|
||||
lastUsedAt: new Date(),
|
||||
});
|
||||
|
||||
this.globalEventService.publishInternalEvent('antennaUpdated', await this.antennasRepository.findOneByOrFail({ id: antenna.id }));
|
||||
|
@@ -5,8 +5,8 @@ import type { UsersRepository, BlockingsRepository } from '@/models/index.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['account'],
|
||||
@@ -88,7 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
where: {
|
||||
blockerId: blocker.id,
|
||||
blockeeId: blockee.id,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!exist) {
|
||||
|
@@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['account', 'notes', 'clips'],
|
||||
|
@@ -126,7 +126,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
query.andWhere('instance.host like :host', { host: '%' + sqlLikeEscape(ps.host.toLowerCase()) + '%' });
|
||||
}
|
||||
|
||||
const instances = await query.limit(ps.limit).skip(ps.offset).getMany();
|
||||
const instances = await query.limit(ps.limit).offset(ps.offset).getMany();
|
||||
|
||||
return await this.instanceEntityService.packMany(instances);
|
||||
});
|
||||
|
@@ -5,8 +5,8 @@ import type { UsersRepository, FollowingsRepository } from '@/models/index.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['following', 'users'],
|
||||
|
@@ -5,8 +5,8 @@ import type { UsersRepository, FollowingsRepository } from '@/models/index.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['following', 'users'],
|
||||
|
@@ -42,7 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
.orderBy('tag.count', 'DESC')
|
||||
.groupBy('tag.id')
|
||||
.limit(ps.limit)
|
||||
.skip(ps.offset)
|
||||
.offset(ps.offset)
|
||||
.getMany();
|
||||
|
||||
return hashtags.map(tag => tag.name);
|
||||
|
@@ -23,7 +23,7 @@ export const meta = {
|
||||
id: 'e5b3b9f0-2b8f-4b9f-9c1f-8c5c1b2e1b1a',
|
||||
kind: 'permission',
|
||||
},
|
||||
}
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -103,7 +103,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const procedures = this.twoFactorAuthenticationService.getProcedures();
|
||||
|
||||
if (!(procedures as any)[attestation.fmt]) {
|
||||
throw new Error('unsupported fmt');
|
||||
throw new Error(`unsupported fmt: ${attestation.fmt}. Supported ones: ${Object.keys(procedures)}`);
|
||||
}
|
||||
|
||||
const verificationData = (procedures as any)[attestation.fmt].verify({
|
||||
|
@@ -267,7 +267,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const instance = await this.metaService.fetch(true);
|
||||
|
||||
const ads = await this.adsRepository.createQueryBuilder("ads")
|
||||
const ads = await this.adsRepository.createQueryBuilder('ads')
|
||||
.where('ads.expiresAt > :now', { now: new Date() })
|
||||
.andWhere('ads.startsAt <= :now', { now: new Date() })
|
||||
.andWhere(new Brackets(qb => {
|
||||
|
@@ -4,8 +4,8 @@ import type { NotesRepository } from '@/models/index.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
@@ -83,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
const polls = await query
|
||||
.orderBy('poll.noteId', 'DESC')
|
||||
.limit(ps.limit)
|
||||
.skip(ps.offset)
|
||||
.offset(ps.offset)
|
||||
.getMany();
|
||||
|
||||
if (polls.length === 0) return [];
|
||||
|
@@ -3,8 +3,8 @@ import type { NotesRepository } from '@/models/index.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
@@ -3,8 +3,8 @@ import type { PromoReadsRepository } from '@/models/index.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
@@ -35,7 +35,7 @@ export const meta = {
|
||||
code: 'NO_SUCH_REGISTRATION',
|
||||
id: ' b09d8066-8064-5613-efb6-0e963b21d012',
|
||||
},
|
||||
}
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
|
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
if (me) this.queryService.generateBlockQueryForUsers(query, me);
|
||||
|
||||
query.limit(ps.limit);
|
||||
query.skip(ps.offset);
|
||||
query.offset(ps.offset);
|
||||
|
||||
const users = await query.getMany();
|
||||
|
||||
|
@@ -5,8 +5,8 @@ import type { NotesRepository, UsersRepository } from '@/models/index.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
import { GetterService } from '@/server/api/GetterService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['users'],
|
||||
|
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
|
||||
query.setParameters(followingQuery.getParameters());
|
||||
|
||||
const users = await query.limit(ps.limit).skip(ps.offset).getMany();
|
||||
const users = await query.limit(ps.limit).offset(ps.offset).getMany();
|
||||
|
||||
return await this.userEntityService.packMany(users, me, { detail: true });
|
||||
});
|
||||
|
@@ -75,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
users = await usernameQuery
|
||||
.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
|
||||
.limit(ps.limit)
|
||||
.skip(ps.offset)
|
||||
.offset(ps.offset)
|
||||
.getMany();
|
||||
} else {
|
||||
const nameQuery = this.usersRepository.createQueryBuilder('user')
|
||||
@@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
users = await nameQuery
|
||||
.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
|
||||
.limit(ps.limit)
|
||||
.skip(ps.offset)
|
||||
.offset(ps.offset)
|
||||
.getMany();
|
||||
|
||||
if (users.length < ps.limit) {
|
||||
@@ -128,7 +128,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
users = users.concat(await query
|
||||
.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
|
||||
.limit(ps.limit)
|
||||
.skip(ps.offset)
|
||||
.offset(ps.offset)
|
||||
.getMany(),
|
||||
);
|
||||
}
|
||||
|
@@ -3,9 +3,9 @@ import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import Channel from '../channel.js';
|
||||
import { StreamMessages } from '../types.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
|
||||
class RoleTimelineChannel extends Channel {
|
||||
public readonly chName = 'roleTimeline';
|
||||
|
@@ -3,7 +3,7 @@ import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { createBullBoard } from '@bull-board/api';
|
||||
import { BullAdapter } from '@bull-board/api/bullAdapter.js';
|
||||
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter.js';
|
||||
import { FastifyAdapter } from '@bull-board/fastify';
|
||||
import ms from 'ms';
|
||||
import sharp from 'sharp';
|
||||
@@ -168,7 +168,7 @@ export class ClientServerService {
|
||||
this.dbQueue,
|
||||
this.objectStorageQueue,
|
||||
this.webhookDeliverQueue,
|
||||
].map(q => new BullAdapter(q)),
|
||||
].map(q => new BullMQAdapter(q)),
|
||||
serverAdapter,
|
||||
});
|
||||
|
||||
|
@@ -16,8 +16,12 @@ block og
|
||||
meta(property='og:title' content= title)
|
||||
meta(property='og:description' content= post.description)
|
||||
meta(property='og:url' content= url)
|
||||
meta(property='og:image' content= post.files[0].thumbnailUrl)
|
||||
meta(property='twitter:card' content='summary_large_image')
|
||||
if post.isSensitive
|
||||
meta(property='og:image' content= avatarUrl)
|
||||
meta(property='twitter:card' content='summary')
|
||||
else
|
||||
meta(property='og:image' content= post.files[0].thumbnailUrl)
|
||||
meta(property='twitter:card' content='summary_large_image')
|
||||
|
||||
block meta
|
||||
if user.host || profile.noCrawle
|
||||
|
@@ -5,8 +5,8 @@ block vars
|
||||
- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
|
||||
- const url = `${config.url}/notes/${note.id}`;
|
||||
- const isRenote = note.renote && note.text == null && note.fileIds.length == 0 && note.poll == null;
|
||||
- const image = (note.files || []).find(file => file.type.startsWith('image/') && !file.isSensitive)
|
||||
- const video = (note.files || []).find(file => file.type.startsWith('video/') && !file.isSensitive)
|
||||
- const images = (note.files || []).filter(file => file.type.startsWith('image/') && !file.isSensitive)
|
||||
- const videos = (note.files || []).filter(file => file.type.startsWith('video/') && !file.isSensitive)
|
||||
|
||||
block title
|
||||
= `${title} | ${instanceName}`
|
||||
@@ -19,15 +19,17 @@ block og
|
||||
meta(property='og:title' content= title)
|
||||
meta(property='og:description' content= summary)
|
||||
meta(property='og:url' content= url)
|
||||
if video
|
||||
meta(property='og:video:url' content= video.url)
|
||||
meta(property='og:video:secure_url' content= video.url)
|
||||
meta(property='og:video:type' content= video.type)
|
||||
// FIXME: add width and height
|
||||
// FIXME: add embed player for Twitter
|
||||
if image
|
||||
if videos.length
|
||||
each video in videos
|
||||
meta(property='og:video:url' content= video.url)
|
||||
meta(property='og:video:secure_url' content= video.url)
|
||||
meta(property='og:video:type' content= video.type)
|
||||
// FIXME: add width and height
|
||||
// FIXME: add embed player for Twitter
|
||||
if images.length
|
||||
meta(property='twitter:card' content='summary_large_image')
|
||||
meta(property='og:image' content= image.url)
|
||||
each image in images
|
||||
meta(property='og:image' content= image.url)
|
||||
else
|
||||
meta(property='twitter:card' content='summary')
|
||||
meta(property='og:image' content= avatarUrl)
|
||||
|
@@ -4,8 +4,9 @@
|
||||
"scripts": {
|
||||
"watch": "vite",
|
||||
"build": "vite build",
|
||||
"storybook-dev": "chokidar 'src/**/*.{mdx,ts,vue}' -d 1000 -t 1000 --initial -i '**/*.stories.ts' -c 'pkill -f node_modules/storybook/index.js; node_modules/.bin/tsc -p .storybook && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js && node_modules/.bin/storybook dev -p 6006 --ci'",
|
||||
"build-storybook": "tsc -p .storybook && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js && storybook build",
|
||||
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
||||
"build-storybook-pre": "tsc -p .storybook && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||
"build-storybook": "pnpm build-storybook-pre && storybook build",
|
||||
"chromatic": "chromatic",
|
||||
"test": "vitest --run",
|
||||
"test-and-coverage": "vitest --run --coverage",
|
||||
@@ -19,7 +20,7 @@
|
||||
"@rollup/plugin-json": "6.0.0",
|
||||
"@rollup/plugin-replace": "5.0.2",
|
||||
"@rollup/pluginutils": "5.0.2",
|
||||
"@syuilo/aiscript": "0.13.3",
|
||||
"@syuilo/aiscript": "0.15.0",
|
||||
"@tabler/icons-webfont": "2.25.0",
|
||||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vue-macros/reactivity-transform": "0.3.15",
|
||||
@@ -76,24 +77,24 @@
|
||||
"vuedraggable": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "7.1.0",
|
||||
"@storybook/addon-essentials": "7.1.0",
|
||||
"@storybook/addon-interactions": "7.1.0",
|
||||
"@storybook/addon-links": "7.1.0",
|
||||
"@storybook/addon-storysource": "7.1.0",
|
||||
"@storybook/addons": "7.1.0",
|
||||
"@storybook/blocks": "7.1.0",
|
||||
"@storybook/core-events": "7.1.0",
|
||||
"@storybook/addon-actions": "7.0.27",
|
||||
"@storybook/addon-essentials": "7.0.27",
|
||||
"@storybook/addon-interactions": "7.0.27",
|
||||
"@storybook/addon-links": "7.0.27",
|
||||
"@storybook/addon-storysource": "7.0.27",
|
||||
"@storybook/addons": "7.0.27",
|
||||
"@storybook/blocks": "7.0.27",
|
||||
"@storybook/core-events": "7.0.27",
|
||||
"@storybook/jest": "0.1.0",
|
||||
"@storybook/manager-api": "7.1.0",
|
||||
"@storybook/preview-api": "7.1.0",
|
||||
"@storybook/react": "7.1.0",
|
||||
"@storybook/react-vite": "7.1.0",
|
||||
"@storybook/manager-api": "7.0.27",
|
||||
"@storybook/preview-api": "7.0.27",
|
||||
"@storybook/react": "7.0.27",
|
||||
"@storybook/react-vite": "7.0.27",
|
||||
"@storybook/testing-library": "0.2.0",
|
||||
"@storybook/theming": "7.1.0",
|
||||
"@storybook/types": "7.1.0",
|
||||
"@storybook/vue3": "7.1.0",
|
||||
"@storybook/vue3-vite": "7.1.0",
|
||||
"@storybook/theming": "7.0.27",
|
||||
"@storybook/types": "7.0.27",
|
||||
"@storybook/vue3": "7.0.27",
|
||||
"@storybook/vue3-vite": "7.0.27",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/vue": "7.0.0",
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
@@ -116,7 +117,6 @@
|
||||
"@vitest/coverage-v8": "0.33.0",
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"acorn": "8.10.0",
|
||||
"chokidar-cli": "3.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.17.1",
|
||||
"eslint": "8.45.0",
|
||||
@@ -127,11 +127,12 @@
|
||||
"micromatch": "4.0.5",
|
||||
"msw": "1.2.2",
|
||||
"msw-storybook-addon": "1.8.0",
|
||||
"nodemon": "3.0.1",
|
||||
"prettier": "3.0.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"start-server-and-test": "2.0.0",
|
||||
"storybook": "7.1.0",
|
||||
"storybook": "7.0.27",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"summaly": "github:misskey-dev/summaly",
|
||||
"vite-plugin-turbosnap": "1.0.2",
|
||||
|
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div v-if="file.isSensitive" :class="[$style.label, $style.red]">
|
||||
<img :class="$style.labelImg" src="/client-assets/label-red.svg"/>
|
||||
<p :class="$style.labelText">NSFW</p>
|
||||
<p :class="$style.labelText">{{ i18n.ts.sensitive }}</p>
|
||||
</div>
|
||||
|
||||
<MkDriveFileThumbnail :class="$style.thumbnail" :file="file" fit="contain"/>
|
||||
|
@@ -108,8 +108,7 @@ function waitForDecode() {
|
||||
.then(() => {
|
||||
loaded = true;
|
||||
}, error => {
|
||||
console.error('Error occurred during decoding image', img.value, error);
|
||||
throw Error(error);
|
||||
console.log('Error occurred during decoding image', img.value, error);
|
||||
});
|
||||
} else {
|
||||
loaded = false;
|
||||
|
@@ -20,7 +20,7 @@
|
||||
<template v-if="hide">
|
||||
<div :class="$style.hiddenText">
|
||||
<div :class="$style.hiddenTextWrapper">
|
||||
<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
|
||||
<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
|
||||
<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
|
||||
<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
|
||||
</div>
|
||||
@@ -30,7 +30,7 @@
|
||||
<div :class="$style.indicators">
|
||||
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
|
||||
<div v-if="image.comment" :class="$style.indicator">ALT</div>
|
||||
<div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--warn);">NSFW</div>
|
||||
<div v-if="image.isSensitive" :class="$style.indicator" style="color: var(--warn);" :title="i18n.ts.sensitive"><i class="ti ti-eye-exclamation"></i></div>
|
||||
</div>
|
||||
<button :class="$style.menu" class="_button" @click.stop="showMenu"><i class="ti ti-dots" style="vertical-align: middle;"></i></button>
|
||||
<i class="ti ti-eye-off" :class="$style.hide" @click.stop="hide = true"></i>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="root">
|
||||
<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/>
|
||||
<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container">
|
||||
<div
|
||||
@@ -23,7 +23,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, watch, shallowRef } from 'vue';
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||
import PhotoSwipe from 'photoswipe';
|
||||
@@ -34,19 +34,26 @@ import XVideo from '@/components/MkMediaVideo.vue';
|
||||
import * as os from '@/os';
|
||||
import { FILE_TYPE_BROWSERSAFE } from '@/const';
|
||||
import { defaultStore } from '@/store';
|
||||
import { getScrollContainer, getBodyScrollHeight } from '@/scripts/scroll';
|
||||
|
||||
const props = defineProps<{
|
||||
mediaList: misskey.entities.DriveFile[];
|
||||
raw?: boolean;
|
||||
}>();
|
||||
|
||||
const root = shallowRef<HTMLDivElement>();
|
||||
const container = shallowRef<HTMLElement | null | undefined>(undefined);
|
||||
const gallery = shallowRef<HTMLDivElement>();
|
||||
const pswpZIndex = os.claimZIndex('middle');
|
||||
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||
const count = $computed(() => props.mediaList.filter(media => previewable(media)).length);
|
||||
|
||||
/**
|
||||
* アスペクト比をmediaListWithOneImageAppearanceに基づいていい感じに調整する
|
||||
* aspect-ratioではなくheightを使う
|
||||
*/
|
||||
function calcAspectRatio() {
|
||||
if (!gallery.value) return;
|
||||
if (!gallery.value || !root.value) return;
|
||||
|
||||
let img = props.mediaList[0];
|
||||
|
||||
@@ -55,28 +62,46 @@ function calcAspectRatio() {
|
||||
return;
|
||||
}
|
||||
|
||||
// アスペクト比上限設定では、横長の場合は高さを縮小させる
|
||||
const ratioMax = (ratio: number) => `${Math.max(ratio, img.properties.width / img.properties.height).toString()} / 1`;
|
||||
const width = gallery.value.clientWidth;
|
||||
|
||||
const heightMin = (ratio: number) => {
|
||||
const imgResizeRatio = width / img.properties.width;
|
||||
const imgDrawHeight = img.properties.height * imgResizeRatio;
|
||||
const maxHeight = width * ratio;
|
||||
const height = Math.min(imgDrawHeight, maxHeight);
|
||||
if (_DEV_) console.log('Image height calculated:', { width, properties: img.properties, imgResizeRatio, imgDrawHeight, maxHeight, height });
|
||||
return `${height}px`;
|
||||
};
|
||||
|
||||
switch (defaultStore.state.mediaListWithOneImageAppearance) {
|
||||
case '16_9':
|
||||
gallery.value.style.aspectRatio = ratioMax(16 / 9);
|
||||
gallery.value.style.height = heightMin(9 / 16);
|
||||
break;
|
||||
case '1_1':
|
||||
gallery.value.style.aspectRatio = ratioMax(1);
|
||||
gallery.value.style.height = heightMin(1);
|
||||
break;
|
||||
case '2_3':
|
||||
gallery.value.style.aspectRatio = ratioMax(2 / 3);
|
||||
gallery.value.style.height = heightMin(3 / 2);
|
||||
break;
|
||||
default:
|
||||
gallery.value.style.aspectRatio = '';
|
||||
default: {
|
||||
if (!container.value) container.value = getScrollContainer(root.value);
|
||||
const maxHeight = Math.max(64, (container.value ? container.value.clientHeight : getBodyScrollHeight()) * 0.5 || 360);
|
||||
if (width === 0 || !maxHeight) return;
|
||||
const imgResizeRatio = width / img.properties.width;
|
||||
const imgDrawHeight = img.properties.height * imgResizeRatio;
|
||||
gallery.value.style.height = `${Math.max(64, Math.min(imgDrawHeight, maxHeight))}px`;
|
||||
gallery.value.style.minHeight = 'initial';
|
||||
gallery.value.style.maxHeight = 'initial';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gallery.value.style.aspectRatio = 'initial';
|
||||
}
|
||||
|
||||
watch([defaultStore.reactiveState.mediaListWithOneImageAppearance, gallery], () => calcAspectRatio());
|
||||
|
||||
onMounted(() => {
|
||||
calcAspectRatio();
|
||||
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
dataSource: props.mediaList
|
||||
.filter(media => {
|
||||
@@ -203,7 +228,7 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
|
||||
&.n1 {
|
||||
grid-template-rows: 1fr;
|
||||
|
||||
// default (expand)
|
||||
// default but fallback (expand)
|
||||
min-height: 64px;
|
||||
max-height: clamp(
|
||||
64px,
|
||||
@@ -212,20 +237,20 @@ const previewable = (file: misskey.entities.DriveFile): boolean => {
|
||||
);
|
||||
|
||||
&.n116_9 {
|
||||
min-height: none;
|
||||
max-height: none;
|
||||
min-height: initial;
|
||||
max-height: initial;
|
||||
aspect-ratio: 16 / 9; // fallback
|
||||
}
|
||||
|
||||
&.n11_1{
|
||||
min-height: none;
|
||||
max-height: none;
|
||||
min-height: initial;
|
||||
max-height: initial;
|
||||
aspect-ratio: 1 / 1; // fallback
|
||||
}
|
||||
|
||||
&.n12_3 {
|
||||
min-height: none;
|
||||
max-height: none;
|
||||
min-height: initial;
|
||||
max-height: initial;
|
||||
aspect-ratio: 2 / 3; // fallback
|
||||
}
|
||||
}
|
||||
|
@@ -431,9 +431,15 @@ defineExpose({
|
||||
margin: auto;
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
// TODO: mask-imageはiOSだとやたら重い。なんとかしたい
|
||||
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
|
||||
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
|
||||
overflow: auto;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 16px;
|
||||
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
|
||||
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -165,6 +165,7 @@ import { getNoteSummary } from '@/scripts/get-note-summary';
|
||||
import { MenuItem } from '@/types/menu';
|
||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||
import { showMovedDialog } from '@/scripts/show-moved-dialog';
|
||||
import { shouldCollapsed } from '@/scripts/collapsed';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
@@ -204,17 +205,7 @@ let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note
|
||||
const isMyRenote = $i && ($i.id === note.userId);
|
||||
const showContent = ref(false);
|
||||
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
|
||||
const isLong = (appearNote.cw == null && appearNote.text != null && (
|
||||
(appearNote.text.includes('$[x2')) ||
|
||||
(appearNote.text.includes('$[x3')) ||
|
||||
(appearNote.text.includes('$[x4')) ||
|
||||
(appearNote.text.includes('$[scale')) ||
|
||||
(appearNote.text.includes('$[position')) ||
|
||||
(appearNote.text.split('\n').length > 9) ||
|
||||
(appearNote.text.length > 500) ||
|
||||
(appearNote.files.length >= 5) ||
|
||||
(urls && urls.length >= 4)
|
||||
));
|
||||
const isLong = shouldCollapsed(appearNote);
|
||||
const collapsed = ref(appearNote.cw == null && isLong);
|
||||
const isDeleted = ref(false);
|
||||
const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
||||
|
@@ -540,7 +540,7 @@ function onCompositionEnd(ev: CompositionEvent) {
|
||||
}
|
||||
|
||||
async function onPaste(ev: ClipboardEvent) {
|
||||
for (const { item, i } of Array.from(ev.clipboardData.items).map((item, i) => ({ item, i }))) {
|
||||
for (const { item, i } of Array.from(ev.clipboardData.items, (item, i) => ({ item, i }))) {
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile();
|
||||
const lio = file.name.lastIndexOf('.');
|
||||
@@ -907,6 +907,7 @@ defineExpose({
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
@@ -1024,7 +1025,7 @@ defineExpose({
|
||||
}
|
||||
|
||||
.targetNote {
|
||||
padding: 0 20px 16px 20px;
|
||||
padding: 10px 20px 16px 20px;
|
||||
}
|
||||
|
||||
.withQuote {
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<div :class="$style.file" @click="showFileMenu(element, $event)" @contextmenu.prevent="showFileMenu(element, $event)">
|
||||
<MkDriveFileThumbnail :data-id="element.id" :class="$style.thumbnail" :file="element" fit="cover"/>
|
||||
<div v-if="element.isSensitive" :class="$style.sensitive">
|
||||
<i class="ti ti-alert-triangle" style="margin: auto;"></i>
|
||||
<i class="ti ti-eye-exclamation" style="margin: auto;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -9,7 +9,10 @@
|
||||
<MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">{{ i18n.ts.pleaseConfirmBelowBeforeSignup }}</div>
|
||||
<div style="text-align: center;">
|
||||
<div>{{ i18n.ts.pleaseConfirmBelowBeforeSignup }}</div>
|
||||
<div style="font-weight: bold; margin-top: 0.5em;">{{ i18n.ts.beSureToReadThisAsItIsImportant }}</div>
|
||||
</div>
|
||||
|
||||
<MkFolder v-if="availableServerRules" :defaultOpen="true">
|
||||
<template #label>{{ i18n.ts.serverRules }}</template>
|
||||
@@ -19,7 +22,7 @@
|
||||
<li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li>
|
||||
</ol>
|
||||
|
||||
<MkSwitch v-model="agreeServerRules" style="margin-top: 16px;">{{ i18n.ts.agree }}</MkSwitch>
|
||||
<MkSwitch :modelValue="agreeServerRules" style="margin-top: 16px;" @update:modelValue="updateAgreeServerRules">{{ i18n.ts.agree }}</MkSwitch>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="availableTos" :defaultOpen="true">
|
||||
@@ -28,7 +31,7 @@
|
||||
|
||||
<a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ti ti-external-link"></i></a>
|
||||
|
||||
<MkSwitch v-model="agreeTos" style="margin-top: 16px;">{{ i18n.ts.agree }}</MkSwitch>
|
||||
<MkSwitch :modelValue="agreeTos" style="margin-top: 16px;" @update:modelValue="updateAgreeTos">{{ i18n.ts.agree }}</MkSwitch>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
@@ -37,7 +40,7 @@
|
||||
|
||||
<a href="https://misskey-hub.net/docs/notes.html" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ti ti-external-link"></i></a>
|
||||
|
||||
<MkSwitch v-model="agreeNote" style="margin-top: 16px;" data-cy-signup-rules-notes-agree>{{ i18n.ts.agree }}</MkSwitch>
|
||||
<MkSwitch :modelValue="agreeNote" style="margin-top: 16px;" data-cy-signup-rules-notes-agree @update:modelValue="updateAgreeNote">{{ i18n.ts.agree }}</MkSwitch>
|
||||
</MkFolder>
|
||||
|
||||
<div v-if="!agreed" style="text-align: center;">{{ i18n.ts.pleaseAgreeAllToContinue }}</div>
|
||||
@@ -52,13 +55,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { instance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
const availableServerRules = instance.serverRules.length > 0;
|
||||
const availableTos = instance.tosUrl != null;
|
||||
@@ -75,6 +79,48 @@ const emit = defineEmits<{
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'done'): void;
|
||||
}>();
|
||||
|
||||
async function updateAgreeServerRules(v: boolean) {
|
||||
if (v) {
|
||||
const confirm = await os.confirm({
|
||||
type: 'question',
|
||||
title: i18n.ts.doYouAgree,
|
||||
text: i18n.t('iHaveReadXCarefullyAndAgree', { x: i18n.ts.serverRules }),
|
||||
});
|
||||
if (confirm.canceled) return;
|
||||
agreeServerRules.value = true;
|
||||
} else {
|
||||
agreeServerRules.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAgreeTos(v: boolean) {
|
||||
if (v) {
|
||||
const confirm = await os.confirm({
|
||||
type: 'question',
|
||||
title: i18n.ts.doYouAgree,
|
||||
text: i18n.t('iHaveReadXCarefullyAndAgree', { x: i18n.ts.termsOfService }),
|
||||
});
|
||||
if (confirm.canceled) return;
|
||||
agreeTos.value = true;
|
||||
} else {
|
||||
agreeTos.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAgreeNote(v: boolean) {
|
||||
if (v) {
|
||||
const confirm = await os.confirm({
|
||||
type: 'question',
|
||||
title: i18n.ts.doYouAgree,
|
||||
text: i18n.t('iHaveReadXCarefullyAndAgree', { x: i18n.ts.basicNotesBeforeCreateAccount }),
|
||||
});
|
||||
if (confirm.canceled) return;
|
||||
agreeNote.value = true;
|
||||
} else {
|
||||
agreeNote.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@@ -31,16 +31,13 @@ import MkMediaList from '@/components/MkMediaList.vue';
|
||||
import MkPoll from '@/components/MkPoll.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
import { $i } from '@/account';
|
||||
import { shouldCollapsed } from '@/scripts/collapsed';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const isLong =
|
||||
props.note.cw == null && props.note.text != null && (
|
||||
(props.note.text.split('\n').length > 9) ||
|
||||
(props.note.text.length > 500)
|
||||
);
|
||||
const isLong = shouldCollapsed(props.note);
|
||||
|
||||
const collapsed = $ref(isLong);
|
||||
</script>
|
||||
|
@@ -16,8 +16,8 @@ import tinycolor from 'tinycolor2';
|
||||
const loaded = !!window.TagCanvas;
|
||||
const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const idForCanvas = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||
const idForTags = Array.from(Array(16)).map(() => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||
const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||
const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
|
||||
let available = $ref(false);
|
||||
let rootEl = $shallowRef<HTMLElement | null>(null);
|
||||
let canvasEl = $shallowRef<HTMLCanvasElement | null>(null);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user