Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
72b03e009c | ||
![]() |
1b113c1045 | ||
![]() |
54959557ea | ||
![]() |
d44cb7f256 | ||
![]() |
3d063c95d1 | ||
![]() |
09cab605fc | ||
![]() |
666c8c0498 | ||
![]() |
d3e764d7f9 | ||
![]() |
7060625adf | ||
![]() |
21b6e23e98 | ||
![]() |
a0f794e372 | ||
![]() |
9195504329 | ||
![]() |
8c5d9dd549 | ||
![]() |
580f6a5b6c | ||
![]() |
74e76b460b | ||
![]() |
c4570b37b7 | ||
![]() |
cd0b0012d9 | ||
![]() |
c055b4d32d | ||
![]() |
75a9ff832a | ||
![]() |
b64d3af1f3 | ||
![]() |
fb6605bb40 | ||
![]() |
3bfae80fa7 | ||
![]() |
cb16cb0610 | ||
![]() |
0baed1a275 | ||
![]() |
42162c8015 | ||
![]() |
0fab0c416d | ||
![]() |
e2e262c8ce | ||
![]() |
cf6596203b | ||
![]() |
471911a54f | ||
![]() |
9394f4f540 | ||
![]() |
4e968216ad | ||
![]() |
84a7a9555f | ||
![]() |
8d12fd152b |
@@ -412,7 +412,7 @@ noMessagesYet: "Noch keine Nachrichten"
|
|||||||
newMessageExists: "Du hast eine neue Nachricht"
|
newMessageExists: "Du hast eine neue Nachricht"
|
||||||
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
|
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
|
||||||
signinRequired: "Anmeldung erforderlich"
|
signinRequired: "Anmeldung erforderlich"
|
||||||
invitations: "Einladen"
|
invitations: "Einladungen"
|
||||||
invitationCode: "Einladungscode"
|
invitationCode: "Einladungscode"
|
||||||
checking: "Wird überprüft..."
|
checking: "Wird überprüft..."
|
||||||
available: "Verfügbar"
|
available: "Verfügbar"
|
||||||
@@ -598,10 +598,43 @@ openInNewTab: "In neuem Tab öffnen"
|
|||||||
openInSideView: "In Seitenansicht öffnen"
|
openInSideView: "In Seitenansicht öffnen"
|
||||||
defaultNavigationBehaviour: "Standardnavigationsverhalten"
|
defaultNavigationBehaviour: "Standardnavigationsverhalten"
|
||||||
editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen."
|
editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen."
|
||||||
instanceTicker: "Instanz-Informationen der Notiz"
|
instanceTicker: "Instanz-Informationen von Notizen"
|
||||||
|
waitingFor: "Warte auf {x}"
|
||||||
random: "Zufällig"
|
random: "Zufällig"
|
||||||
|
system: "System"
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "Reversi"
|
||||||
|
gameSettings: "Spieleinstellungen"
|
||||||
|
chooseBoard: "Spielbrett auswählen"
|
||||||
|
blackOrWhite: "Schwarz/Weiß"
|
||||||
|
blackIs: "{name} spielt Schwarz"
|
||||||
|
rules: "Regeln"
|
||||||
|
botSettings: "Optionen des Computergegners"
|
||||||
|
thisGameIsStartedSoon: "Dieses Spiel beginnt in wenigen Sekunden"
|
||||||
|
waitingForOther: "Warte auf den Zug des Gegenspielers"
|
||||||
|
waitingForMe: "Warte auf deinen Zug"
|
||||||
|
waitingBoth: "Mach dich bereit"
|
||||||
|
ready: "Bereit"
|
||||||
|
cancelReady: "Nicht bereit"
|
||||||
|
opponentTurn: "Zug deines Gegners"
|
||||||
|
myTurn: "Dein Zug"
|
||||||
|
turnOf: "Zug von {name}"
|
||||||
|
pastTurnOf: "Zug von {name}"
|
||||||
|
surrender: "Aufgeben"
|
||||||
|
surrendered: "durch Aufgabe"
|
||||||
|
drawn: "Unentschieden"
|
||||||
|
won: "{name} hat gesiegt"
|
||||||
|
black: "Schwarz"
|
||||||
|
white: "Weiß"
|
||||||
total: "Gesamt"
|
total: "Gesamt"
|
||||||
|
turnCount: " Zug {count}"
|
||||||
|
myGames: "Meine Runden"
|
||||||
|
allGames: "Alle Runden"
|
||||||
|
ended: "Beendet"
|
||||||
|
playing: "Laufend"
|
||||||
|
isLlotheo: "Der mit weniger Steinen gewinnt (Llotheo)"
|
||||||
|
loopedMap: "Wiederholendes Spielbrett"
|
||||||
|
canPutEverywhere: "Steine können überall platziert werden"
|
||||||
_instanceTicker:
|
_instanceTicker:
|
||||||
none: "Nie anzeigen"
|
none: "Nie anzeigen"
|
||||||
remote: "Für Benutzer fremder Instanzen anzeigen"
|
remote: "Für Benutzer fremder Instanzen anzeigen"
|
||||||
|
@@ -405,14 +405,14 @@ next: "Next"
|
|||||||
retype: "Enter again"
|
retype: "Enter again"
|
||||||
noteOf: "{user}'s notes"
|
noteOf: "{user}'s notes"
|
||||||
inviteToGroup: "Invite to group"
|
inviteToGroup: "Invite to group"
|
||||||
maxNoteTextLength: "Character limit of the note"
|
maxNoteTextLength: "Character limit of notes"
|
||||||
quoteAttached: "Quoted"
|
quoteAttached: "Quoted"
|
||||||
quoteQuestion: "Do you want to append a quote?"
|
quoteQuestion: "Do you want to append a quote?"
|
||||||
noMessagesYet: "No messages yet"
|
noMessagesYet: "No messages yet"
|
||||||
newMessageExists: "You've got a new message"
|
newMessageExists: "You've got a new message"
|
||||||
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
||||||
signinRequired: "Please sign in"
|
signinRequired: "Please sign in"
|
||||||
invitations: "Invite"
|
invitations: "Invitations"
|
||||||
invitationCode: "Invitation code"
|
invitationCode: "Invitation code"
|
||||||
checking: "Checking"
|
checking: "Checking"
|
||||||
available: "Available"
|
available: "Available"
|
||||||
@@ -598,10 +598,43 @@ openInNewTab: "Open in new tab"
|
|||||||
openInSideView: "Open in side view"
|
openInSideView: "Open in side view"
|
||||||
defaultNavigationBehaviour: "Default navigation behavior"
|
defaultNavigationBehaviour: "Default navigation behavior"
|
||||||
editTheseSettingsMayBreakAccount: "Editing these settings may damage your account."
|
editTheseSettingsMayBreakAccount: "Editing these settings may damage your account."
|
||||||
instanceTicker: "Instance information of the Note"
|
instanceTicker: "Instance information of notes"
|
||||||
|
waitingFor: "Waiting for {x}"
|
||||||
random: "Random"
|
random: "Random"
|
||||||
|
system: "System"
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "Reversi"
|
||||||
|
gameSettings: "Game settings"
|
||||||
|
chooseBoard: "Choose a board"
|
||||||
|
blackOrWhite: "Black/White"
|
||||||
|
blackIs: "{name} is playing Black"
|
||||||
|
rules: "Rules"
|
||||||
|
botSettings: "Bot options"
|
||||||
|
thisGameIsStartedSoon: "The game will start in a few seconds"
|
||||||
|
waitingForOther: "Waiting for the opponent's turn"
|
||||||
|
waitingForMe: "Waiting for your turn"
|
||||||
|
waitingBoth: "Get ready"
|
||||||
|
ready: "Ready"
|
||||||
|
cancelReady: "Cancel ready"
|
||||||
|
opponentTurn: "Opponent's turn"
|
||||||
|
myTurn: "Your turn"
|
||||||
|
turnOf: "{name}'s turn"
|
||||||
|
pastTurnOf: "{name}'s turn"
|
||||||
|
surrender: "Surrender"
|
||||||
|
surrendered: "By surrender"
|
||||||
|
drawn: "Draw"
|
||||||
|
won: "{name}'s win"
|
||||||
|
black: "Black"
|
||||||
|
white: "White"
|
||||||
total: "Total"
|
total: "Total"
|
||||||
|
turnCount: "Turn {count}"
|
||||||
|
myGames: "My rounds"
|
||||||
|
allGames: "All rounds"
|
||||||
|
ended: "Ended"
|
||||||
|
playing: "Currently playing"
|
||||||
|
isLlotheo: "The one with fewer stones wins (Llotheo)"
|
||||||
|
loopedMap: "Looped map"
|
||||||
|
canPutEverywhere: "Tiles are placeable everywhere"
|
||||||
_instanceTicker:
|
_instanceTicker:
|
||||||
none: "Never show"
|
none: "Never show"
|
||||||
remote: "Show for remote users"
|
remote: "Show for remote users"
|
||||||
|
@@ -601,6 +601,7 @@ editTheseSettingsMayBreakAccount: "これらの設定を編集するとアカウ
|
|||||||
instanceTicker: "ノートのインスタンス情報"
|
instanceTicker: "ノートのインスタンス情報"
|
||||||
waitingFor: "{x}を待っています"
|
waitingFor: "{x}を待っています"
|
||||||
random: "ランダム"
|
random: "ランダム"
|
||||||
|
system: "システム"
|
||||||
|
|
||||||
_reversi:
|
_reversi:
|
||||||
reversi: "リバーシ"
|
reversi: "リバーシ"
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
_lang_: "日本語 (関西弁)"
|
_lang_: "日本語 (関西弁)"
|
||||||
introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作成しぃ、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素はよ反応を追加することもできます✌\n新しい世界を探検しよう🚀"
|
introMisskey: "ようこそ!Misskeyってのは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作成し、いま起こっとることを共有したり、あんたんこととか皆に伝えていこう📡\n「リアクション」機能で、皆のノートに素はよ反応を追加することもできるんやで✌\n新しい世界を探検してみらん?🚀"
|
||||||
monthAndDay: "{month}月 {day}日"
|
monthAndDay: "{month}月 {day}日"
|
||||||
search: "探す"
|
search: "探す"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
username: "ユーザー名"
|
username: "ユーザー名"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
fetchingAsApObject: "連合に照会中"
|
fetchingAsApObject: "今ちと連合に照会しとるで"
|
||||||
ok: "おっけー"
|
ok: "おっけー"
|
||||||
gotIt: "ほい"
|
gotIt: "ほい"
|
||||||
cancel: "やめとくわ"
|
cancel: "やめとくわ"
|
||||||
@@ -16,35 +16,40 @@ noNotes: "ノートはあらへん"
|
|||||||
noNotifications: "通知はあらへん"
|
noNotifications: "通知はあらへん"
|
||||||
instance: "インスタンス"
|
instance: "インスタンス"
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
|
basicSettings: "基本設定"
|
||||||
|
otherSettings: "その他の設定"
|
||||||
|
openInWindow: "ウィンドウで開いてや"
|
||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
noAccountDescription: "自己紹介はあらへん"
|
noAccountDescription: "自己紹介はあらへん"
|
||||||
login: "ログイン"
|
login: "ログイン"
|
||||||
loggingIn: "ログインしとります"
|
loggingIn: "ログインしよるで"
|
||||||
logout: "ログアウト"
|
logout: "ログアウト"
|
||||||
signup: "新規登録"
|
signup: "新規登録"
|
||||||
uploading: "アップロードしとります"
|
uploading: "アップロードしよるで"
|
||||||
save: "保存"
|
save: "とっとく"
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
addUser: "ユーザー増やす"
|
addUser: "ユーザーを追加や"
|
||||||
favorite: "お気に入り"
|
favorite: "お気に入り"
|
||||||
favorites: "お気に入り"
|
favorites: "お気に入り"
|
||||||
unfavorite: "お気に入りやめる"
|
unfavorite: "やっぱ気に入らん"
|
||||||
pin: "ピン留め"
|
pin: "ピン留めしとく"
|
||||||
unpin: "ピン留めやめる"
|
unpin: "やっぱピン留めせん"
|
||||||
copyContent: "内容をコピー"
|
copyContent: "内容をコピー"
|
||||||
copyLink: "リンクをコピー"
|
copyLink: "リンクをコピー"
|
||||||
delete: "ほかす"
|
delete: "ほかす"
|
||||||
deleteAndEdit: "ほかして直す"
|
deleteAndEdit: "ほかして直す"
|
||||||
deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのリアクション、Remote、返信も全部消えんで"
|
deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのリアクション、Renote、返信も全部消えるんやけどそれでもええん?"
|
||||||
addToList: "リストに入れたる"
|
addToList: "リストに入れたる"
|
||||||
sendMessage: "メッセージを送る"
|
sendMessage: "メッセージを送る"
|
||||||
copyUsername: "ユーザー名をコピー"
|
copyUsername: "ユーザー名をコピー"
|
||||||
|
searchUser: "ユーザーを検索"
|
||||||
reply: "返す"
|
reply: "返す"
|
||||||
loadMore: "もっとあるやろ!"
|
loadMore: "もっとあるやろ!"
|
||||||
youGotNewFollower: "フォローされたで"
|
youGotNewFollower: "フォローされたで"
|
||||||
receiveFollowRequest: "フォローリクエストされたで"
|
receiveFollowRequest: "フォローリクエストされたで"
|
||||||
followRequestAccepted: "フォローが承認されたで"
|
followRequestAccepted: "フォローが承認されたで"
|
||||||
|
mention: "メンション"
|
||||||
mentions: "あんた宛て"
|
mentions: "あんた宛て"
|
||||||
directNotes: "ダイレクト投稿"
|
directNotes: "ダイレクト投稿"
|
||||||
importAndExport: "インポートとエクスポート"
|
importAndExport: "インポートとエクスポート"
|
||||||
@@ -57,7 +62,7 @@ unfollowConfirm: "{name}のフォローを解除してもええんか?"
|
|||||||
exportRequested: "エクスポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。"
|
exportRequested: "エクスポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。"
|
||||||
importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。"
|
importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。"
|
||||||
lists: "リスト"
|
lists: "リスト"
|
||||||
noLists: "リストはあらへん"
|
noLists: "リストなんてあらへんで"
|
||||||
note: "ノート"
|
note: "ノート"
|
||||||
notes: "ノート"
|
notes: "ノート"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
@@ -65,31 +70,35 @@ followers: "フォロワー"
|
|||||||
followsYou: "フォローされとるで"
|
followsYou: "フォローされとるで"
|
||||||
createList: "リスト作る"
|
createList: "リスト作る"
|
||||||
manageLists: "リストの管理"
|
manageLists: "リストの管理"
|
||||||
retry: "もっぺんやってみる"
|
error: "エラー"
|
||||||
|
somethingHappened: "なんかアカンことが起こったで"
|
||||||
|
retry: "もっぺんやる?"
|
||||||
|
pageLoadError: "ページの読み込みに失敗してしもうたで…"
|
||||||
|
pageLoadErrorDescription: "これは普通、ネットワークかブラウザキャッシュが原因やからね。キャッシュをクリアするか、もうちっとだけ待ってくれへんか?"
|
||||||
enterListName: "リスト名を入れてや"
|
enterListName: "リスト名を入れてや"
|
||||||
privacy: "プライバシーってなんや?オカンの年齢か?"
|
privacy: "プライバシーってなんぞや?"
|
||||||
makeFollowManuallyApprove: "他人のフォローは許可してからや!"
|
makeFollowManuallyApprove: "他人からのフォローは自分が決める"
|
||||||
defaultNoteVisibility: "もとからの公開範囲"
|
defaultNoteVisibility: "もとからの公開範囲"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
followRequest: "フォロー許してくれや!言うてみる"
|
followRequest: "フォローを頼む"
|
||||||
followRequests: "フォロー許してくれや!"
|
followRequests: "フォローを頼む"
|
||||||
unfollow: "フォローやめる"
|
unfollow: "フォローやめる"
|
||||||
followRequestPending: "フォロー許してくれるん待っとる"
|
followRequestPending: "フォロー許してくれるん待っとる"
|
||||||
enterEmoji: "絵文字を入れてや"
|
enterEmoji: "絵文字を入れてや"
|
||||||
renote: "Renote"
|
renote: "Renote"
|
||||||
unrenote: "Renoteやめる"
|
unrenote: "Renoteやめる"
|
||||||
quote: "引用"
|
quote: "引用"
|
||||||
pinnedNote: "ピン留めされたノート"
|
pinnedNote: "ピン留めされとるノート"
|
||||||
you: "あんた"
|
you: "あんた"
|
||||||
clickToShow: "押してみ、見せたるわ"
|
clickToShow: "押したら見えるようになるで"
|
||||||
sensitive: "見たらあかんで"
|
sensitive: "ちょっとアカンやつやで"
|
||||||
add: "増やす"
|
add: "増やす"
|
||||||
reaction: "リアクション"
|
reaction: "リアクション"
|
||||||
reactionSettingDescription: "リアクションピッカーに出しとくリアクションを選んでや。"
|
reactionSettingDescription: "リアクションピッカーに出しとくリアクションを選んでや。"
|
||||||
rememberNoteVisibility: "公開範囲覚えといて"
|
rememberNoteVisibility: "公開範囲覚えといて"
|
||||||
attachCancel: "くっつけるのやめよか"
|
attachCancel: "やっぱ添付やめてくれん?"
|
||||||
markAsSensitive: "ちょっと見せられへんわ"
|
markAsSensitive: "ちょっとこれはアカン"
|
||||||
unmarkAsSensitive: "別にええんじゃね?"
|
unmarkAsSensitive: "そこまでアカンことないやろ"
|
||||||
enterFileName: "ファイル名を入れてや"
|
enterFileName: "ファイル名を入れてや"
|
||||||
mute: "ミュート"
|
mute: "ミュート"
|
||||||
unmute: "ミュートやめたる"
|
unmute: "ミュートやめたる"
|
||||||
@@ -97,30 +106,35 @@ block: "ブロック"
|
|||||||
unblock: "ブロックやめたる"
|
unblock: "ブロックやめたる"
|
||||||
suspend: "凍結"
|
suspend: "凍結"
|
||||||
unsuspend: "溶かす"
|
unsuspend: "溶かす"
|
||||||
blockConfirm: "ブロックしてしもうてええか?"
|
blockConfirm: "ブロックしてもええんか?"
|
||||||
unblockConfirm: "ブロックすんのやめるけどええか?"
|
unblockConfirm: "ブロックやめたるってほんまか?"
|
||||||
suspendConfirm: "凍結してしもうてええか?"
|
suspendConfirm: "凍結してしもうてええか?"
|
||||||
unsuspendConfirm: "解凍するけどええか?"
|
unsuspendConfirm: "解凍するけどええか?"
|
||||||
selectList: "リストを選ぶ"
|
selectList: "リストを選ぶ"
|
||||||
|
selectAntenna: "アンテナを選ぶ"
|
||||||
|
selectWidget: "ウィジェットを選ぶ"
|
||||||
|
editWidgets: "ウィジェットをいじる"
|
||||||
|
editWidgetsExit: "編集終ったで"
|
||||||
customEmojis: "カスタム絵文字"
|
customEmojis: "カスタム絵文字"
|
||||||
|
emoji: "絵文字"
|
||||||
emojiName: "絵文字名"
|
emojiName: "絵文字名"
|
||||||
emojiUrl: "絵文字画像URL"
|
emojiUrl: "絵文字画像URL"
|
||||||
addEmoji: "絵文字を追加"
|
addEmoji: "絵文字を追加"
|
||||||
settingGuide: "ええ感じの設定"
|
settingGuide: "ええ感じの設定"
|
||||||
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
cacheRemoteFiles: "リモートのファイルをキャッシュする"
|
||||||
cacheRemoteFilesDescription: "この設定をチャラにすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されへんので通信量が増加します。"
|
cacheRemoteFilesDescription: "この設定を切っとくと、リモートファイルをキャッシュせず直リンクするようになってしまうんやで? サーバーのストレージは節約できるんやけど、かわりにサムネイルが作られんくなるから通信量が増えるで?"
|
||||||
flagAsBot: "Botやでと言っとく"
|
flagAsBot: "Botやで"
|
||||||
flagAsCat: "Catやでと言っとく"
|
flagAsCat: "Catやで"
|
||||||
autoAcceptFollowed: "フォローしとるユーザーからのフォロリクは全部勝手にええでって言うで"
|
autoAcceptFollowed: "フォローしとるユーザーからのフォローリクエストには勝手に許可しとくで。"
|
||||||
addAcount: "アカウント追加"
|
addAcount: "アカウント追加"
|
||||||
loginFailed: "ログインに失敗してん"
|
loginFailed: "ログインに失敗してしもうた…"
|
||||||
showOnRemote: "リモートで見る"
|
showOnRemote: "リモートで見る"
|
||||||
general: "全般"
|
general: "全般"
|
||||||
wallpaper: "壁紙"
|
wallpaper: "壁紙"
|
||||||
setWallpaper: "壁紙を設定"
|
setWallpaper: "壁紙を設定"
|
||||||
removeWallpaper: "壁紙ほかす"
|
removeWallpaper: "壁紙を削除"
|
||||||
searchWith: "検索: {q}"
|
searchWith: "検索: {q}"
|
||||||
youHaveNoLists: "リストはあらへん"
|
youHaveNoLists: "リストがあらへんで?"
|
||||||
followConfirm: "{name}をフォローしてええか?"
|
followConfirm: "{name}をフォローしてええか?"
|
||||||
proxyAccount: "プロキシアカウント"
|
proxyAccount: "プロキシアカウント"
|
||||||
proxyAccountDescription: "プロキシアカウントは、代わりにフォローしてくれるアカウントや。例えば、551に豚まんが無いときやったり、ユーザーがリモートユーザーをアカウントに入れたとき、リストに入れられたユーザーが誰からもフォローされてないと寂しいやん。寂しいし、アクティビティも配達されへんから、プロキシアカウントがフォローしてくれるで。ええやつやん…"
|
proxyAccountDescription: "プロキシアカウントは、代わりにフォローしてくれるアカウントや。例えば、551に豚まんが無いときやったり、ユーザーがリモートユーザーをアカウントに入れたとき、リストに入れられたユーザーが誰からもフォローされてないと寂しいやん。寂しいし、アクティビティも配達されへんから、プロキシアカウントがフォローしてくれるで。ええやつやん…"
|
||||||
@@ -130,7 +144,7 @@ recipient: "宛先"
|
|||||||
annotation: "注釈"
|
annotation: "注釈"
|
||||||
federation: "連合"
|
federation: "連合"
|
||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
registeredAt: "一見さんになった日"
|
registeredAt: "初観測"
|
||||||
latestRequestSentAt: "ちょっと前のリクエスト送信"
|
latestRequestSentAt: "ちょっと前のリクエスト送信"
|
||||||
latestRequestReceivedAt: "ちょっと前のリクエスト受信"
|
latestRequestReceivedAt: "ちょっと前のリクエスト受信"
|
||||||
latestStatus: "ちょっと前のステータス"
|
latestStatus: "ちょっと前のステータス"
|
||||||
@@ -257,7 +271,8 @@ copyUrl: "URLをコピー"
|
|||||||
rename: "名前を変えるで"
|
rename: "名前を変えるで"
|
||||||
avatar: "アイコン"
|
avatar: "アイコン"
|
||||||
banner: "バナー"
|
banner: "バナー"
|
||||||
nsfw: "見たらあかんで"
|
nsfw: "ちょっとアカンやつやで"
|
||||||
|
whenServerDisconnected: "サーバーとの接続が失くなってしもうたとき"
|
||||||
disconnectedFromServer: "サーバーが機嫌悪いねん"
|
disconnectedFromServer: "サーバーが機嫌悪いねん"
|
||||||
reload: "リロード"
|
reload: "リロード"
|
||||||
doNothing: "何もせんとく"
|
doNothing: "何もせんとく"
|
||||||
@@ -293,7 +308,19 @@ proxyRemoteFilesDescription: "この設定を入れると、保存しとらん
|
|||||||
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
|
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
|
||||||
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
|
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
|
||||||
inMb: "メガバイト単位"
|
inMb: "メガバイト単位"
|
||||||
|
iconUrl: "アイコン画像のURL"
|
||||||
|
bannerUrl: "バナー画像のURL"
|
||||||
|
basicInfo: "基本情報"
|
||||||
|
pinnedUsers: "ピン留めしたユーザー"
|
||||||
|
pinnedUsersDescription: "「みつける」ページとかにピン留めしたいユーザーをここに書けばええんやで。他ん人との名前は改行で区切ればええんやで。"
|
||||||
|
hcaptcha: "hCaptcha(キャプチャ)"
|
||||||
|
enableHcaptcha: "hCaptcha(キャプチャ)をつけとく"
|
||||||
|
hcaptchaSiteKey: "サイトキー"
|
||||||
|
hcaptchaSecretKey: "シークレットキー"
|
||||||
recaptcha: "reCAPTCHA"
|
recaptcha: "reCAPTCHA"
|
||||||
|
enableRecaptcha: "reCAPTCHA(リキャプチャ)を有効にする"
|
||||||
|
recaptchaSiteKey: "サイトキー"
|
||||||
|
recaptchaSecretKey: "シークレットキー"
|
||||||
avoidMultiCaptchaConfirm: "ぎょうさんのCaptchaをつこてしまうと、仲良うせんことがあるんや。他のCaptchaをなおしとこか?別にキャンセルしてもろうたらCaptchaは消されへんで済むけど知らんで。"
|
avoidMultiCaptchaConfirm: "ぎょうさんのCaptchaをつこてしまうと、仲良うせんことがあるんや。他のCaptchaをなおしとこか?別にキャンセルしてもろうたらCaptchaは消されへんで済むけど知らんで。"
|
||||||
antennas: "アンテナ"
|
antennas: "アンテナ"
|
||||||
manageAntennas: "アンテナいじる"
|
manageAntennas: "アンテナいじる"
|
||||||
@@ -346,11 +373,47 @@ unregister: "登録やめる"
|
|||||||
passwordLessLogin: "パスワード無くてもログインできるようにする"
|
passwordLessLogin: "パスワード無くてもログインできるようにする"
|
||||||
resetPassword: "パスワードをリセット"
|
resetPassword: "パスワードをリセット"
|
||||||
newPasswordIs: "今度のパスワードは「{password}」や"
|
newPasswordIs: "今度のパスワードは「{password}」や"
|
||||||
|
autoNoteWatch: "ノートを勝手に見張っとく"
|
||||||
|
autoNoteWatchDescription: "あんたがリアクションや返信した他のユーザーのノートの通知をあんたも受け取れるようになるんやで。通知欄の流れがめっちゃ早くなるで。"
|
||||||
|
reduceUiAnimation: "UIの動きやアニメーションを減らしてくれや。"
|
||||||
|
share: "わけわけ"
|
||||||
|
notFound: "見つからへんね"
|
||||||
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
|
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
|
||||||
|
uploadFolder: "とりあえずここへアップロード"
|
||||||
|
cacheClear: "キャッシュをほかす"
|
||||||
|
markAsReadAllNotifications: "通知はもう全て読んだわっ"
|
||||||
|
markAsReadAllUnreadNotes: "投稿は全て読んだわっ"
|
||||||
|
markAsReadAllTalkMessages: "チャットはもうぜんぶ読んだわっ"
|
||||||
|
help: "ヘルプ"
|
||||||
|
inputMessageHere: "ここにメッセージ書いてや"
|
||||||
close: "さいなら"
|
close: "さいなら"
|
||||||
|
group: "グループ"
|
||||||
|
groups: "グループ"
|
||||||
|
createGroup: "グループを作るで"
|
||||||
|
ownedGroups: "所有しとるグループ"
|
||||||
joinedGroups: "参加しとるグループ"
|
joinedGroups: "参加しとるグループ"
|
||||||
invites: "来てや"
|
invites: "来てや"
|
||||||
|
groupName: "グループ名"
|
||||||
|
members: "メンバー"
|
||||||
|
transfer: "譲渡"
|
||||||
|
messagingWithUser: "ユーザーとチャット"
|
||||||
|
messagingWithGroup: "グループでチャット"
|
||||||
|
title: "タイトル"
|
||||||
|
text: "テキスト"
|
||||||
|
enable: "有効にするで"
|
||||||
|
next: "次"
|
||||||
|
retype: "もっかい入力"
|
||||||
|
noteOf: "{user}のノート"
|
||||||
|
inviteToGroup: "グループに招く"
|
||||||
|
maxNoteTextLength: "ノートの文字数制限"
|
||||||
|
quoteAttached: "引用付いとるで"
|
||||||
|
quoteQuestion: "引用として添付してもええか?"
|
||||||
|
noMessagesYet: "まだチャットはあらへんで"
|
||||||
|
newMessageExists: "新しいメッセージがきたで"
|
||||||
|
onlyOneFileCanBeAttached: "すまん、メッセージに添付できるファイルはひとつだけなんや。"
|
||||||
invitations: "来てや"
|
invitations: "来てや"
|
||||||
|
invitationCode: "招待コード"
|
||||||
|
checking: "確認しとるで"
|
||||||
smtpHost: "ホスト"
|
smtpHost: "ホスト"
|
||||||
smtpUser: "ユーザー名"
|
smtpUser: "ユーザー名"
|
||||||
smtpPass: "パスワード"
|
smtpPass: "パスワード"
|
||||||
@@ -358,6 +421,7 @@ _sidebar:
|
|||||||
icon: "アイコン"
|
icon: "アイコン"
|
||||||
_theme:
|
_theme:
|
||||||
keys:
|
keys:
|
||||||
|
mention: "メンション"
|
||||||
renote: "Renote"
|
renote: "Renote"
|
||||||
_sfx:
|
_sfx:
|
||||||
note: "ノート"
|
note: "ノート"
|
||||||
@@ -441,6 +505,7 @@ _notification:
|
|||||||
youWereFollowed: "フォローされたで"
|
youWereFollowed: "フォローされたで"
|
||||||
_types:
|
_types:
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
|
mention: "メンション"
|
||||||
renote: "Renote"
|
renote: "Renote"
|
||||||
quote: "引用"
|
quote: "引用"
|
||||||
reaction: "リアクション"
|
reaction: "リアクション"
|
||||||
|
@@ -598,9 +598,47 @@ openInNewTab: "Открыть в новой вкладке"
|
|||||||
openInSideView: "Открывать в боковой колонке"
|
openInSideView: "Открывать в боковой колонке"
|
||||||
defaultNavigationBehaviour: "Поведение навигации по умолчанию"
|
defaultNavigationBehaviour: "Поведение навигации по умолчанию"
|
||||||
editTheseSettingsMayBreakAccount: "От изменений в этих настройках ваша учётная запись может поломаться."
|
editTheseSettingsMayBreakAccount: "От изменений в этих настройках ваша учётная запись может поломаться."
|
||||||
|
instanceTicker: "Строка с инстансом в заметке"
|
||||||
|
waitingFor: "Ждём {x}"
|
||||||
random: "Случайные"
|
random: "Случайные"
|
||||||
|
system: "Система"
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "Реверси"
|
||||||
|
gameSettings: "Настройки игры"
|
||||||
|
chooseBoard: "Выберите доску"
|
||||||
|
blackOrWhite: "Черные/Белые"
|
||||||
|
blackIs: "{name} за чёрных"
|
||||||
|
rules: "Правила"
|
||||||
|
botSettings: "Настройки бота"
|
||||||
|
thisGameIsStartedSoon: "Игра скоро начнётся"
|
||||||
|
waitingForOther: "Ожидание оппонента..."
|
||||||
|
waitingForMe: "В ожидании, когда будете готовы"
|
||||||
|
waitingBoth: "Приготовьтесь"
|
||||||
|
ready: "Готово"
|
||||||
|
cancelReady: "Возврат к подготовке"
|
||||||
|
opponentTurn: "Ход соперника"
|
||||||
|
myTurn: "Ваш ход"
|
||||||
|
turnOf: "Ходит {name}"
|
||||||
|
pastTurnOf: "Ходит {name}"
|
||||||
|
surrender: "Сдаться"
|
||||||
|
surrendered: "Сдавшись"
|
||||||
|
drawn: "Ничья"
|
||||||
|
won: "{name} — победитель"
|
||||||
|
black: "Чёрные"
|
||||||
|
white: "Белые"
|
||||||
total: "Всего"
|
total: "Всего"
|
||||||
|
turnCount: "Ход {count}"
|
||||||
|
myGames: "Мои игры"
|
||||||
|
allGames: "Все игры"
|
||||||
|
ended: "Завершено"
|
||||||
|
playing: "Идёт игра"
|
||||||
|
isLlotheo: "Выигрывает меньшее число камней (LLoTheO)"
|
||||||
|
loopedMap: "Замкнутая в кольцо доска"
|
||||||
|
canPutEverywhere: "Камни можно ставить везде"
|
||||||
|
_instanceTicker:
|
||||||
|
none: "Не показывать"
|
||||||
|
remote: "Только у пользователей с других сайтов"
|
||||||
|
always: "Показывать всегда"
|
||||||
_serverDisconnectedBehavior:
|
_serverDisconnectedBehavior:
|
||||||
reload: "Автоматическая перезагрузка"
|
reload: "Автоматическая перезагрузка"
|
||||||
dialog: "Предупреждение"
|
dialog: "Предупреждение"
|
||||||
|
133
locales/uk-UA.yml
Normal file
133
locales/uk-UA.yml
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
_lang_: "Українська"
|
||||||
|
monthAndDay: "{month}/{day}"
|
||||||
|
search: "Пошук"
|
||||||
|
notifications: "Сповіщення"
|
||||||
|
username: "Ім'я користувача"
|
||||||
|
password: "Пароль"
|
||||||
|
ok: "OK"
|
||||||
|
gotIt: "Зрозуміло!"
|
||||||
|
cancel: "Скасувати"
|
||||||
|
enterUsername: "Введіть ім'я користувача"
|
||||||
|
renotedBy: "Поширено {user}"
|
||||||
|
noNotes: "Немає дописів"
|
||||||
|
noNotifications: "Немає сповіщень"
|
||||||
|
instance: "Інстанс"
|
||||||
|
settings: "Налаштування"
|
||||||
|
basicSettings: "Основні налаштування"
|
||||||
|
otherSettings: "Інші налаштування"
|
||||||
|
openInWindow: "Відкрити у вікні"
|
||||||
|
profile: "Профіль"
|
||||||
|
timeline: "Стрічка"
|
||||||
|
noAccountDescription: "Цей користувач ще нічого не написав про себе"
|
||||||
|
login: "Увійти"
|
||||||
|
loggingIn: "Здійснюємо вхід..."
|
||||||
|
logout: "Вийти"
|
||||||
|
signup: "Реєстрація"
|
||||||
|
uploading: "Завантаження..."
|
||||||
|
save: "Зберегти"
|
||||||
|
users: "Користувачі"
|
||||||
|
addUser: "Додати користувача"
|
||||||
|
favorite: "Обране"
|
||||||
|
favorites: "Обране"
|
||||||
|
unfavorite: "Видалити з обраного"
|
||||||
|
pin: "Закріпити"
|
||||||
|
unpin: "Відкріпити"
|
||||||
|
copyContent: "Скопіювати контент"
|
||||||
|
copyLink: "Скопіювати посилання"
|
||||||
|
delete: "Видалити"
|
||||||
|
addToList: "Додати до списку"
|
||||||
|
sendMessage: "Надіслати повідомлення"
|
||||||
|
copyUsername: "Скопіювати ім’я користувача"
|
||||||
|
searchUser: "Пошук користувачів"
|
||||||
|
reply: "Відповісти"
|
||||||
|
loadMore: "Показати більше"
|
||||||
|
mention: "Згадка"
|
||||||
|
mentions: "Згадки"
|
||||||
|
importAndExport: "Імпорт та експорт"
|
||||||
|
import: "Імпорт"
|
||||||
|
export: "Експорт"
|
||||||
|
files: "Файли"
|
||||||
|
download: "Завантажити"
|
||||||
|
lists: "Списки"
|
||||||
|
noLists: "Немає списків"
|
||||||
|
following: "Підписки"
|
||||||
|
followers: "Підписники"
|
||||||
|
followsYou: "Підписаний(-а) на вас"
|
||||||
|
error: "Помилка"
|
||||||
|
somethingHappened: "Щось пішло не так"
|
||||||
|
retry: "Спробувати знову"
|
||||||
|
pageLoadError: "Помилка при завантаженні сторінки"
|
||||||
|
privacy: "Приватність"
|
||||||
|
follow: "Підписки"
|
||||||
|
unfollow: "Відписатися"
|
||||||
|
quote: "Цитата"
|
||||||
|
you: "Ви"
|
||||||
|
clickToShow: "Натисніть для перегляду"
|
||||||
|
sensitive: "NSFW"
|
||||||
|
add: "Додати"
|
||||||
|
reaction: "Реакції"
|
||||||
|
markAsSensitive: "Відмітити як NSFW"
|
||||||
|
enterFileName: "Введіть ім'я файлу"
|
||||||
|
mute: "Ігнорувати"
|
||||||
|
unmute: "Показувати"
|
||||||
|
block: "Заблокувати"
|
||||||
|
unblock: "Розблокувати"
|
||||||
|
instances: "Інстанс"
|
||||||
|
remove: "Видалити"
|
||||||
|
nsfw: "NSFW"
|
||||||
|
userList: "Списки"
|
||||||
|
smtpUser: "Ім'я користувача"
|
||||||
|
smtpPass: "Пароль"
|
||||||
|
_theme:
|
||||||
|
keys:
|
||||||
|
mention: "Згадка"
|
||||||
|
_sfx:
|
||||||
|
notification: "Сповіщення"
|
||||||
|
_widgets:
|
||||||
|
notifications: "Сповіщення"
|
||||||
|
timeline: "Стрічка"
|
||||||
|
_cw:
|
||||||
|
show: "Показати більше"
|
||||||
|
_visibility:
|
||||||
|
followers: "Підписники"
|
||||||
|
_postForm:
|
||||||
|
replyPlaceholder: "Відповідь на допис..."
|
||||||
|
_profile:
|
||||||
|
username: "Ім'я користувача"
|
||||||
|
_exportOrImport:
|
||||||
|
followingList: "Підписки"
|
||||||
|
muteList: "Ігнорувати"
|
||||||
|
blockingList: "Заблокувати"
|
||||||
|
userLists: "Списки"
|
||||||
|
_pages:
|
||||||
|
script:
|
||||||
|
categories:
|
||||||
|
list: "Списки"
|
||||||
|
blocks:
|
||||||
|
_join:
|
||||||
|
arg1: "Списки"
|
||||||
|
_randomPick:
|
||||||
|
arg1: "Списки"
|
||||||
|
_dailyRandomPick:
|
||||||
|
arg1: "Списки"
|
||||||
|
_seedRandomPick:
|
||||||
|
arg2: "Списки"
|
||||||
|
_pick:
|
||||||
|
arg1: "Списки"
|
||||||
|
_listLen:
|
||||||
|
arg1: "Списки"
|
||||||
|
types:
|
||||||
|
array: "Списки"
|
||||||
|
_notification:
|
||||||
|
_types:
|
||||||
|
follow: "Підписки"
|
||||||
|
mention: "Згадка"
|
||||||
|
quote: "Цитата"
|
||||||
|
reaction: "Реакції"
|
||||||
|
_deck:
|
||||||
|
_columns:
|
||||||
|
notifications: "Сповіщення"
|
||||||
|
tl: "Стрічка"
|
||||||
|
list: "Списки"
|
||||||
|
mentions: "Згадки"
|
@@ -598,9 +598,47 @@ openInNewTab: "在新标签页中打开"
|
|||||||
openInSideView: "在侧边栏中打开"
|
openInSideView: "在侧边栏中打开"
|
||||||
defaultNavigationBehaviour: "默认导航"
|
defaultNavigationBehaviour: "默认导航"
|
||||||
editTheseSettingsMayBreakAccount: "编辑这些设置可以会损坏您的账号"
|
editTheseSettingsMayBreakAccount: "编辑这些设置可以会损坏您的账号"
|
||||||
|
instanceTicker: "帖子的实例信息"
|
||||||
|
waitingFor: "等待{x}"
|
||||||
random: "随机"
|
random: "随机"
|
||||||
|
system: "系统"
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "黑白棋"
|
||||||
|
gameSettings: "对局设置"
|
||||||
|
chooseBoard: "棋盘选择"
|
||||||
|
blackOrWhite: "先手/后手"
|
||||||
|
blackIs: "{name}执黑(先走)"
|
||||||
|
rules: "规则"
|
||||||
|
botSettings: "机器人设置"
|
||||||
|
thisGameIsStartedSoon: "对局在几秒后开始"
|
||||||
|
waitingForOther: "等待对手准备"
|
||||||
|
waitingForMe: "等待您的准备"
|
||||||
|
waitingBoth: "请准备"
|
||||||
|
ready: "准备就绪"
|
||||||
|
cancelReady: "重新准备"
|
||||||
|
opponentTurn: "对手的会合"
|
||||||
|
myTurn: "您的回合"
|
||||||
|
turnOf: "{name}的回合"
|
||||||
|
pastTurnOf: "{name}的回合"
|
||||||
|
surrender: "认输 "
|
||||||
|
surrendered: "对手认输"
|
||||||
|
drawn: "平局"
|
||||||
|
won: "{name}获胜"
|
||||||
|
black: "黑"
|
||||||
|
white: "白"
|
||||||
total: "总计"
|
total: "总计"
|
||||||
|
turnCount: "{count}回合"
|
||||||
|
myGames: "我的对局"
|
||||||
|
allGames: "所有对局"
|
||||||
|
ended: "结束"
|
||||||
|
playing: "对局中"
|
||||||
|
isLlotheo: "棋子较少一方获胜(LLoTheO规则)"
|
||||||
|
loopedMap: "循环棋盘"
|
||||||
|
canPutEverywhere: "可以下在任意位置"
|
||||||
|
_instanceTicker:
|
||||||
|
none: "不显示"
|
||||||
|
remote: "显示给远程用户"
|
||||||
|
always: "始终显示"
|
||||||
_serverDisconnectedBehavior:
|
_serverDisconnectedBehavior:
|
||||||
reload: "自动重载"
|
reload: "自动重载"
|
||||||
dialog: "对话框警告"
|
dialog: "对话框警告"
|
||||||
|
@@ -99,8 +99,8 @@ attachCancel: "移除附件"
|
|||||||
markAsSensitive: "標記為敏感內容"
|
markAsSensitive: "標記為敏感內容"
|
||||||
unmarkAsSensitive: "取消標記為敏感內容"
|
unmarkAsSensitive: "取消標記為敏感內容"
|
||||||
enterFileName: "請輸入檔案名稱"
|
enterFileName: "請輸入檔案名稱"
|
||||||
mute: "消音"
|
mute: "靜音"
|
||||||
unmute: "解除消音"
|
unmute: "解除靜音"
|
||||||
block: "封鎖"
|
block: "封鎖"
|
||||||
unblock: "解除封鎖"
|
unblock: "解除封鎖"
|
||||||
suspend: "凍結"
|
suspend: "凍結"
|
||||||
@@ -171,8 +171,8 @@ clearCachedFiles: "清除快取資料"
|
|||||||
clearCachedFilesConfirm: "確定要清除緩存資料嗎?"
|
clearCachedFilesConfirm: "確定要清除緩存資料嗎?"
|
||||||
blockedInstances: "已封鎖的實例"
|
blockedInstances: "已封鎖的實例"
|
||||||
blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。"
|
blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。"
|
||||||
muteAndBlock: "禁言 / 封鎖"
|
muteAndBlock: "靜音/封鎖"
|
||||||
mutedUsers: "已禁言用戶"
|
mutedUsers: "已靜音用戶"
|
||||||
blockedUsers: "已封鎖用戶"
|
blockedUsers: "已封鎖用戶"
|
||||||
noUsers: "無用戶"
|
noUsers: "無用戶"
|
||||||
editProfile: "編輯個人檔案"
|
editProfile: "編輯個人檔案"
|
||||||
@@ -452,8 +452,10 @@ appearance: "外觀"
|
|||||||
clientSettings: "用戶端設定"
|
clientSettings: "用戶端設定"
|
||||||
accountSettings: "帳號設定"
|
accountSettings: "帳號設定"
|
||||||
promotion: "推廣貼文"
|
promotion: "推廣貼文"
|
||||||
|
promote: "推廣"
|
||||||
numberOfDays: "有效天數"
|
numberOfDays: "有效天數"
|
||||||
hideThisNote: "隱藏此貼文"
|
hideThisNote: "隱藏此貼文"
|
||||||
|
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
|
||||||
objectStorageBaseUrl: "Base URL"
|
objectStorageBaseUrl: "Base URL"
|
||||||
objectStorageBucket: "儲存空間(Bucket)"
|
objectStorageBucket: "儲存空間(Bucket)"
|
||||||
objectStoragePrefix: "前綴"
|
objectStoragePrefix: "前綴"
|
||||||
@@ -536,6 +538,7 @@ smtpUser: "使用者名稱"
|
|||||||
smtpPass: "密碼"
|
smtpPass: "密碼"
|
||||||
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以禁用SMTP驗證。"
|
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以禁用SMTP驗證。"
|
||||||
testEmail: "郵件測試發送"
|
testEmail: "郵件測試發送"
|
||||||
|
wordMute: "靜音文字"
|
||||||
display: "檢視"
|
display: "檢視"
|
||||||
copy: "複製"
|
copy: "複製"
|
||||||
metrics: "指標"
|
metrics: "指標"
|
||||||
@@ -547,6 +550,7 @@ channel: "頻道"
|
|||||||
create: "新增"
|
create: "新增"
|
||||||
notificationSetting: "通知設定"
|
notificationSetting: "通知設定"
|
||||||
other: "其他"
|
other: "其他"
|
||||||
|
regenerateLoginTokenDescription: "再生用於登入的內部權杖。一般情況下是不需要這樣做的。一旦再生,所有裝置將會被登出。"
|
||||||
sample: "範例 "
|
sample: "範例 "
|
||||||
abuseReports: "檢舉"
|
abuseReports: "檢舉"
|
||||||
reportAbuse: "檢舉"
|
reportAbuse: "檢舉"
|
||||||
@@ -554,8 +558,25 @@ reportAbuseOf: "檢舉{name}"
|
|||||||
send: "發送"
|
send: "發送"
|
||||||
openInNewTab: "在新分頁中開啟"
|
openInNewTab: "在新分頁中開啟"
|
||||||
random: "隨機"
|
random: "隨機"
|
||||||
|
system: "系統"
|
||||||
_reversi:
|
_reversi:
|
||||||
|
reversi: "黑白棋"
|
||||||
|
gameSettings: "對弈設定"
|
||||||
|
chooseBoard: "選擇棋盤"
|
||||||
|
rules: "規則"
|
||||||
|
botSettings: "機器人設定"
|
||||||
|
opponentTurn: "對手回合"
|
||||||
|
myTurn: "你的回合"
|
||||||
|
turnOf: "{name}的回合"
|
||||||
|
pastTurnOf: "{name}的回合"
|
||||||
|
surrender: "認輸"
|
||||||
|
black: "黑"
|
||||||
|
white: "白"
|
||||||
total: "合計"
|
total: "合計"
|
||||||
|
ended: "已結束"
|
||||||
|
playing: "正在對弈"
|
||||||
|
_instanceTicker:
|
||||||
|
always: "總是顯示"
|
||||||
_serverDisconnectedBehavior:
|
_serverDisconnectedBehavior:
|
||||||
reload: "自動重載"
|
reload: "自動重載"
|
||||||
dialog: "以對話框警告"
|
dialog: "以對話框警告"
|
||||||
@@ -575,14 +596,24 @@ _sidebar:
|
|||||||
hide: "隱藏"
|
hide: "隱藏"
|
||||||
_wordMute:
|
_wordMute:
|
||||||
muteWords: "加入靜音文字"
|
muteWords: "加入靜音文字"
|
||||||
|
mutedNotes: "已靜音的貼文"
|
||||||
_theme:
|
_theme:
|
||||||
constant: "常數"
|
constant: "常數"
|
||||||
|
defaultValue: "預設值"
|
||||||
color: "顏色"
|
color: "顏色"
|
||||||
func: "函数"
|
func: "函数"
|
||||||
|
argument: "引數"
|
||||||
|
alpha: "透明度"
|
||||||
|
darken: "暗度"
|
||||||
|
lighten: "亮度"
|
||||||
keys:
|
keys:
|
||||||
bg: "背景"
|
bg: "背景"
|
||||||
fg: "文本"
|
fg: "文本"
|
||||||
|
shadow: "陰影"
|
||||||
|
link: "鏈接"
|
||||||
|
hashtag: "#tag"
|
||||||
mention: "提及"
|
mention: "提及"
|
||||||
|
mentionMe: "提及我"
|
||||||
renote: "轉發貼文"
|
renote: "轉發貼文"
|
||||||
divider: "分割線"
|
divider: "分割線"
|
||||||
infoBg: "資訊背景"
|
infoBg: "資訊背景"
|
||||||
@@ -633,6 +664,8 @@ _tutorial:
|
|||||||
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
|
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
|
||||||
step7_1: "以上為Misskey的基本操作說明,教學在此告一段落。辛苦了。"
|
step7_1: "以上為Misskey的基本操作說明,教學在此告一段落。辛苦了。"
|
||||||
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
|
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
|
||||||
|
_2fa:
|
||||||
|
registerDevice: "註冊裝置"
|
||||||
_permissions:
|
_permissions:
|
||||||
"read:blocks": "已封鎖用戶名單"
|
"read:blocks": "已封鎖用戶名單"
|
||||||
"write:blocks": "編輯已封鎖用戶名單"
|
"write:blocks": "編輯已封鎖用戶名單"
|
||||||
@@ -641,15 +674,26 @@ _permissions:
|
|||||||
"read:favorites": "瀏覽已收藏"
|
"read:favorites": "瀏覽已收藏"
|
||||||
"write:favorites": "編輯收藏清單"
|
"write:favorites": "編輯收藏清單"
|
||||||
"write:following": "追隨/解除追隨"
|
"write:following": "追隨/解除追隨"
|
||||||
|
"read:messaging": "顯示訊息"
|
||||||
|
"write:messaging": "撰寫或刪除私人訊息"
|
||||||
|
"read:mutes": "顯示已靜音列表"
|
||||||
|
"write:mutes": "編輯已靜音列表"
|
||||||
"write:notes": "撰寫或刪除貼文"
|
"write:notes": "撰寫或刪除貼文"
|
||||||
"read:notifications": "查看通知"
|
"read:notifications": "查看通知"
|
||||||
|
"write:notifications": "編輯通知"
|
||||||
"read:reactions": "查看反應"
|
"read:reactions": "查看反應"
|
||||||
"write:reactions": "編輯反應"
|
"write:reactions": "編輯反應"
|
||||||
"write:votes": "投票"
|
"write:votes": "投票"
|
||||||
|
"read:pages": "顯示頁面"
|
||||||
|
"write:pages": "編輯頁面"
|
||||||
|
"read:page-likes": "顯示頁面的已喜歡"
|
||||||
|
"write:page-likes": "編輯頁面上喜歡"
|
||||||
"read:user-groups": "顯示使用者群組"
|
"read:user-groups": "顯示使用者群組"
|
||||||
"write:user-groups": "編輯使用者群組"
|
"write:user-groups": "編輯使用者群組"
|
||||||
"read:channels": "已查看的頻道"
|
"read:channels": "已查看的頻道"
|
||||||
"write:channels": "操作頻道"
|
"write:channels": "編輯頻道"
|
||||||
|
_auth:
|
||||||
|
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
|
||||||
_antennaSources:
|
_antennaSources:
|
||||||
all: "全部貼文"
|
all: "全部貼文"
|
||||||
homeTimeline: "來自已追隨使用者的貼文"
|
homeTimeline: "來自已追隨使用者的貼文"
|
||||||
@@ -685,14 +729,17 @@ _poll:
|
|||||||
noOnlyOneChoice: "至少需要兩個選項。"
|
noOnlyOneChoice: "至少需要兩個選項。"
|
||||||
expiration: "期限"
|
expiration: "期限"
|
||||||
infinite: "無期限"
|
infinite: "無期限"
|
||||||
|
at: "結束時間"
|
||||||
deadlineDate: "截止日期"
|
deadlineDate: "截止日期"
|
||||||
deadlineTime: "小時"
|
deadlineTime: "小時"
|
||||||
|
duration: "時長"
|
||||||
votesCount: "{n}票"
|
votesCount: "{n}票"
|
||||||
totalVotes: "一共{n}票"
|
totalVotes: "一共{n}票"
|
||||||
vote: "投票"
|
vote: "投票"
|
||||||
showResult: "顯示結果"
|
showResult: "顯示結果"
|
||||||
voted: "已投票"
|
voted: "已投票"
|
||||||
closed: "已結束"
|
closed: "已結束"
|
||||||
|
remainingDays: "{d}天{h}小時後結束"
|
||||||
_visibility:
|
_visibility:
|
||||||
public: "公開"
|
public: "公開"
|
||||||
home: "首頁"
|
home: "首頁"
|
||||||
@@ -723,7 +770,7 @@ _profile:
|
|||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "全部貼文"
|
allNotes: "全部貼文"
|
||||||
followingList: "追隨中"
|
followingList: "追隨中"
|
||||||
muteList: "消音"
|
muteList: "靜音"
|
||||||
blockingList: "封鎖"
|
blockingList: "封鎖"
|
||||||
userLists: "清單"
|
userLists: "清單"
|
||||||
_charts:
|
_charts:
|
||||||
@@ -820,10 +867,13 @@ _pages:
|
|||||||
variables: "變數"
|
variables: "變數"
|
||||||
title: "標題"
|
title: "標題"
|
||||||
url: "頁面網址"
|
url: "頁面網址"
|
||||||
|
font: "字型"
|
||||||
fontSerif: "襯線體"
|
fontSerif: "襯線體"
|
||||||
fontSansSerif: "無襯線體"
|
fontSansSerif: "無襯線體"
|
||||||
inputBlocks: "輸入"
|
inputBlocks: "輸入"
|
||||||
blocks:
|
blocks:
|
||||||
|
text: "文本"
|
||||||
|
textarea: "文字區域"
|
||||||
section: "區段"
|
section: "區段"
|
||||||
image: "圖片"
|
image: "圖片"
|
||||||
button: "按鈕"
|
button: "按鈕"
|
||||||
@@ -832,17 +882,25 @@ _pages:
|
|||||||
variable: "變數"
|
variable: "變數"
|
||||||
_post:
|
_post:
|
||||||
text: "内容"
|
text: "内容"
|
||||||
|
canvasId: "畫布ID"
|
||||||
|
textInput: "插入文字"
|
||||||
_textInput:
|
_textInput:
|
||||||
name: "變數名稱"
|
name: "變數名稱"
|
||||||
text: "標題"
|
text: "標題"
|
||||||
|
default: "預設值"
|
||||||
|
textareaInput: "多行文字输入"
|
||||||
_textareaInput:
|
_textareaInput:
|
||||||
name: "變數名稱"
|
name: "變數名稱"
|
||||||
text: "標題"
|
text: "標題"
|
||||||
|
default: "預設值"
|
||||||
numberInput: "輸入數值"
|
numberInput: "輸入數值"
|
||||||
_numberInput:
|
_numberInput:
|
||||||
name: "變數名稱"
|
name: "變數名稱"
|
||||||
text: "標題"
|
text: "標題"
|
||||||
|
default: "預設值"
|
||||||
|
canvas: "畫布"
|
||||||
_canvas:
|
_canvas:
|
||||||
|
id: "畫布ID"
|
||||||
width: "寬度"
|
width: "寬度"
|
||||||
height: "高度"
|
height: "高度"
|
||||||
switch: "開關"
|
switch: "開關"
|
||||||
@@ -858,6 +916,7 @@ _pages:
|
|||||||
_button:
|
_button:
|
||||||
text: "標題"
|
text: "標題"
|
||||||
colored: "彩色"
|
colored: "彩色"
|
||||||
|
action: "按下按鈕後發生的行為"
|
||||||
_action:
|
_action:
|
||||||
_dialog:
|
_dialog:
|
||||||
content: "内容"
|
content: "内容"
|
||||||
@@ -873,6 +932,7 @@ _pages:
|
|||||||
_radioButton:
|
_radioButton:
|
||||||
name: "變數名稱"
|
name: "變數名稱"
|
||||||
title: "標題"
|
title: "標題"
|
||||||
|
default: "預設值"
|
||||||
script:
|
script:
|
||||||
categories:
|
categories:
|
||||||
logical: "邏輯運算"
|
logical: "邏輯運算"
|
||||||
@@ -888,6 +948,8 @@ _pages:
|
|||||||
text: "文本"
|
text: "文本"
|
||||||
multiLineText: "文本 (多行)"
|
multiLineText: "文本 (多行)"
|
||||||
textList: "文本列表"
|
textList: "文本列表"
|
||||||
|
_strLen:
|
||||||
|
arg1: "文本"
|
||||||
_strPick:
|
_strPick:
|
||||||
arg1: "文本"
|
arg1: "文本"
|
||||||
arg2: "字元位置"
|
arg2: "字元位置"
|
||||||
@@ -1001,6 +1063,8 @@ _pages:
|
|||||||
arg1: "文字"
|
arg1: "文字"
|
||||||
_numberToString:
|
_numberToString:
|
||||||
arg1: "數值"
|
arg1: "數值"
|
||||||
|
_splitStrByLine:
|
||||||
|
arg1: "文本"
|
||||||
ref: "變數"
|
ref: "變數"
|
||||||
aiScriptVar: "AiScript的變數"
|
aiScriptVar: "AiScript的變數"
|
||||||
fn: "函数"
|
fn: "函数"
|
||||||
@@ -1016,6 +1080,7 @@ _pages:
|
|||||||
array: "清單"
|
array: "清單"
|
||||||
stringArray: "文本列表"
|
stringArray: "文本列表"
|
||||||
enviromentVariables: "環境變數"
|
enviromentVariables: "環境變數"
|
||||||
|
pageVariables: "頁面元素"
|
||||||
_relayStatus:
|
_relayStatus:
|
||||||
requesting: "等待核准"
|
requesting: "等待核准"
|
||||||
accepted: "已通過核准"
|
accepted: "已通過核准"
|
||||||
@@ -1034,7 +1099,13 @@ _notification:
|
|||||||
renote: "轉發貼文"
|
renote: "轉發貼文"
|
||||||
quote: "引用"
|
quote: "引用"
|
||||||
reaction: "反應"
|
reaction: "反應"
|
||||||
|
receiveFollowRequest: "已收到追隨請求"
|
||||||
|
followRequestAccepted: "追隨請求已接受"
|
||||||
|
app: "應用程式通知"
|
||||||
_deck:
|
_deck:
|
||||||
|
alwaysShowMainColumn: "總是顯示主欄"
|
||||||
|
columnAlign: "對齊欄位"
|
||||||
|
addColumn: "新增欄位"
|
||||||
swapLeft: "向左移動"
|
swapLeft: "向左移動"
|
||||||
swapRight: "向右移動"
|
swapRight: "向右移動"
|
||||||
swapUp: "往上移動"
|
swapUp: "往上移動"
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||||
"version": "12.51.0",
|
"version": "12.52.0",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
"vuex": "4.0.0-beta.4",
|
"vuex": "4.0.0-beta.4",
|
||||||
"vuex-persistedstate": "3.1.0",
|
"vuex-persistedstate": "3.1.0",
|
||||||
"web-push": "3.4.4",
|
"web-push": "3.4.4",
|
||||||
"webpack": "5.2.0",
|
"webpack": "5.3.2",
|
||||||
"webpack-cli": "4.1.0",
|
"webpack-cli": "4.1.0",
|
||||||
"websocket": "1.0.32",
|
"websocket": "1.0.32",
|
||||||
"ws": "7.3.1",
|
"ws": "7.3.1",
|
||||||
|
@@ -122,6 +122,7 @@ export default defineComponent({
|
|||||||
users: [],
|
users: [],
|
||||||
hashtags: [],
|
hashtags: [],
|
||||||
emojis: [],
|
emojis: [],
|
||||||
|
items: [],
|
||||||
select: -1,
|
select: -1,
|
||||||
emojilist,
|
emojilist,
|
||||||
emojiDb: [] as EmojiDef[]
|
emojiDb: [] as EmojiDef[]
|
||||||
@@ -129,10 +130,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
items(): HTMLCollection {
|
|
||||||
return (this.$refs.suggests as Element).children;
|
|
||||||
},
|
|
||||||
|
|
||||||
useOsNativeEmojis(): boolean {
|
useOsNativeEmojis(): boolean {
|
||||||
return this.$store.state.device.useOsNativeEmojis;
|
return this.$store.state.device.useOsNativeEmojis;
|
||||||
}
|
}
|
||||||
@@ -148,6 +145,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
updated() {
|
updated() {
|
||||||
this.setPosition();
|
this.setPosition();
|
||||||
|
this.items = (this.$refs.suggests as Element | undefined)?.children || [];
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -371,6 +369,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
selectNext() {
|
selectNext() {
|
||||||
if (++this.select >= this.items.length) this.select = 0;
|
if (++this.select >= this.items.length) this.select = 0;
|
||||||
|
if (this.items.length === 0) this.select = -1;
|
||||||
this.applySelect();
|
this.applySelect();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -384,8 +383,10 @@ export default defineComponent({
|
|||||||
el.removeAttribute('data-selected');
|
el.removeAttribute('data-selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.select !== -1) {
|
||||||
this.items[this.select].setAttribute('data-selected', 'true');
|
this.items[this.select].setAttribute('data-selected', 'true');
|
||||||
(this.items[this.select] as any).focus();
|
(this.items[this.select] as any).focus();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
chooseUser() {
|
chooseUser() {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
|
<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
|
||||||
<div class="banner" v-if="channel.bannerUrl" :style="`background-image: url('${channel.bannerUrl}')`">
|
<div class="banner" :style="bannerStyle">
|
||||||
<div class="fade"></div>
|
<div class="fade"></div>
|
||||||
<div class="name"><Fa :icon="faSatelliteDish"/> {{ channel.name }}</div>
|
<div class="name"><Fa :icon="faSatelliteDish"/> {{ channel.name }}</div>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
@@ -45,6 +45,16 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
bannerStyle() {
|
||||||
|
if (this.channel.bannerUrl) {
|
||||||
|
return { backgroundImage: `url(${this.channel.bannerUrl})` };
|
||||||
|
} else {
|
||||||
|
return { backgroundColor: '#4c5e6d' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
faSatelliteDish, faUsers, faPencilAlt,
|
faSatelliteDish, faUsers, faPencilAlt,
|
||||||
|
@@ -169,15 +169,15 @@ export default defineComponent({
|
|||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
|
|
||||||
&.success {
|
&.success {
|
||||||
color: var(--accent);
|
color: var(--success);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.error {
|
&.error {
|
||||||
color: #ec4137;
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
color: #ecb637;
|
color: var(--warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
@@ -32,8 +32,6 @@ export default defineComponent({
|
|||||||
raw: {
|
raw: {
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
// specify the parent element
|
|
||||||
parentElement: {}
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -66,7 +64,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (this.$refs.gridOuter) {
|
if (this.$refs.gridOuter) {
|
||||||
let height = 287;
|
let height = 287;
|
||||||
const parent = this.parentElement || this.$parent.$el;
|
const parent = this.$parent.$el;
|
||||||
|
|
||||||
if (this.$refs.gridOuter.clientHeight) {
|
if (this.$refs.gridOuter.clientHeight) {
|
||||||
height = this.$refs.gridOuter.clientHeight;
|
height = this.$refs.gridOuter.clientHeight;
|
||||||
@@ -81,11 +79,6 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
parentElement() {
|
|
||||||
this.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -125,6 +125,18 @@ export default defineComponent({
|
|||||||
}, genEl(token.children));
|
}, genEl(token.children));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'twitch': {
|
||||||
|
return h('span', {
|
||||||
|
style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-twitch 0.5s ease infinite;' : 'display: inline-block;'
|
||||||
|
}, genEl(token.children));
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'shake': {
|
||||||
|
return h('span', {
|
||||||
|
style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-shake 0.5s ease infinite;' : 'display: inline-block;'
|
||||||
|
}, genEl(token.children));
|
||||||
|
}
|
||||||
|
|
||||||
case 'url': {
|
case 'url': {
|
||||||
return [h(MkUrl, {
|
return [h(MkUrl, {
|
||||||
key: Math.random(),
|
key: Math.random(),
|
||||||
|
@@ -41,7 +41,7 @@
|
|||||||
<div class="main">
|
<div class="main">
|
||||||
<XNoteHeader class="header" :note="appearNote" :mini="true"/>
|
<XNoteHeader class="header" :note="appearNote" :mini="true"/>
|
||||||
<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/>
|
<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/>
|
||||||
<div class="body" ref="noteBody">
|
<div class="body">
|
||||||
<p v-if="appearNote.cw != null" class="cw">
|
<p v-if="appearNote.cw != null" class="cw">
|
||||||
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/>
|
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/>
|
||||||
<XCwButton v-model:value="showContent" :note="appearNote"/>
|
<XCwButton v-model:value="showContent" :note="appearNote"/>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<a class="rp" v-if="appearNote.renote != null">RN:</a>
|
<a class="rp" v-if="appearNote.renote != null">RN:</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="files" v-if="appearNote.files.length > 0">
|
<div class="files" v-if="appearNote.files.length > 0">
|
||||||
<XMediaList :media-list="appearNote.files" :parent-element="noteBody"/>
|
<XMediaList :media-list="appearNote.files"/>
|
||||||
</div>
|
</div>
|
||||||
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
|
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
|
||||||
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="detail" class="url-preview"/>
|
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="detail" class="url-preview"/>
|
||||||
@@ -176,7 +176,6 @@ export default defineComponent({
|
|||||||
showContent: false,
|
showContent: false,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
muted: false,
|
muted: false,
|
||||||
noteBody: this.$refs.noteBody,
|
|
||||||
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
|
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -309,8 +308,6 @@ export default defineComponent({
|
|||||||
if (this.$store.getters.isSignedIn) {
|
if (this.$store.getters.isSignedIn) {
|
||||||
this.connection.on('_connected_', this.onStreamConnected);
|
this.connection.on('_connected_', this.onStreamConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.noteBody = this.$refs.noteBody;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns } from '@fortawesome/free-solid-svg-icons';
|
||||||
import XWindow from '@/components/ui/window.vue';
|
import XWindow from '@/components/ui/window.vue';
|
||||||
import XHeader from '@/ui/_common_/header.vue';
|
import XHeader from '@/ui/_common_/header.vue';
|
||||||
import { popout } from '@/scripts/popout';
|
import { popout } from '@/scripts/popout';
|
||||||
@@ -35,6 +35,12 @@ export default defineComponent({
|
|||||||
XHeader,
|
XHeader,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
inject: {
|
||||||
|
sideViewHook: {
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
navHook: (url) => {
|
navHook: (url) => {
|
||||||
@@ -81,7 +87,14 @@ export default defineComponent({
|
|||||||
icon: faExpandAlt,
|
icon: faExpandAlt,
|
||||||
text: this.$t('showInPage'),
|
text: this.$t('showInPage'),
|
||||||
action: this.expand
|
action: this.expand
|
||||||
}, {
|
}, this.sideViewHook ? {
|
||||||
|
icon: faColumns,
|
||||||
|
text: this.$t('openInSideView'),
|
||||||
|
action: () => {
|
||||||
|
this.sideViewHook(this.url);
|
||||||
|
this.$refs.window.close();
|
||||||
|
}
|
||||||
|
} : undefined, {
|
||||||
icon: faExternalLinkAlt,
|
icon: faExternalLinkAlt,
|
||||||
text: this.$t('popout'),
|
text: this.$t('popout'),
|
||||||
action: this.popout
|
action: this.popout
|
||||||
|
@@ -564,7 +564,7 @@ export default defineComponent({
|
|||||||
this.posting = false;
|
this.posting = false;
|
||||||
os.dialog({
|
os.dialog({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: err.message + '<br>' + (err as any).id,
|
text: err.message + '\n' + (err as any).id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@@ -422,9 +422,9 @@ export default defineComponent({
|
|||||||
> .item {
|
> .item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
padding-left: 32px;
|
padding-left: 24px;
|
||||||
font-size: $ui-font-size;
|
font-size: $ui-font-size;
|
||||||
line-height: 3.2rem;
|
line-height: 3rem;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@@ -7,9 +7,7 @@
|
|||||||
>
|
>
|
||||||
<template #header>{{ $t('login') }}</template>
|
<template #header>{{ $t('login') }}</template>
|
||||||
|
|
||||||
<div class="_section">
|
|
||||||
<MkSignin :auto-set="autoSet" @login="onLogin"/>
|
<MkSignin :auto-set="autoSet" @login="onLogin"/>
|
||||||
</div>
|
|
||||||
</XModalWindow>
|
</XModalWindow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
|
<form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
|
||||||
|
<div class="auth _section">
|
||||||
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
|
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
|
||||||
<div class="normal-signin" v-if="!totpLogin">
|
<div class="normal-signin" v-if="!totpLogin">
|
||||||
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
|
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
|
||||||
@@ -12,9 +13,6 @@
|
|||||||
<template #prefix><Fa :icon="faLock"/></template>
|
<template #prefix><Fa :icon="faLock"/></template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
||||||
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
|
|
||||||
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
|
|
||||||
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
|
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
|
||||||
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
|
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
|
||||||
@@ -39,6 +37,12 @@
|
|||||||
<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="social _section">
|
||||||
|
<a class="_borderButton _vMargin" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
|
||||||
|
<a class="_borderButton _vMargin" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
|
||||||
|
<a class="_borderButton _vMargin" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -203,6 +207,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.eppvobhk {
|
.eppvobhk {
|
||||||
|
> .auth {
|
||||||
> .avatar {
|
> .avatar {
|
||||||
margin: 0 auto 0 auto;
|
margin: 0 auto 0 auto;
|
||||||
width: 64px;
|
width: 64px;
|
||||||
@@ -212,5 +217,6 @@ export default defineComponent({
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="pxhvhrfw" v-size="{ max: [500] }">
|
<div class="pxhvhrfw" v-size="{ max: [500] }">
|
||||||
<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
|
<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :disabled="value === item.value" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -23,19 +23,26 @@ export default defineComponent({
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pxhvhrfw {
|
.pxhvhrfw {
|
||||||
display: flex;
|
display: flex;
|
||||||
max-width: var(--baseContentWidth);
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
> button {
|
> button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 15px 12px 12px 12px;
|
padding: 15px 12px 12px 12px;
|
||||||
border-bottom: solid 3px transparent;
|
border-bottom: solid 3px transparent;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 1 !important;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
border-bottom-color: var(--accent);
|
border-bottom-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.active):hover {
|
||||||
|
color: var(--fgHighlighted);
|
||||||
|
}
|
||||||
|
|
||||||
> .icon {
|
> .icon {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
70
src/client/components/taskmanager.api-window.vue
Normal file
70
src/client/components/taskmanager.api-window.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<XWindow ref="window"
|
||||||
|
:initial-width="370"
|
||||||
|
:initial-height="450"
|
||||||
|
:can-resize="true"
|
||||||
|
@close="$refs.window.close()"
|
||||||
|
@closed="$emit('closed')"
|
||||||
|
>
|
||||||
|
<template #header>Req Viewer</template>
|
||||||
|
|
||||||
|
<div class="rlkneywz">
|
||||||
|
<MkTab v-model:value="tab" :items="[{ label: 'Request', value: 'req', }, { label: 'Response', value: 'res', }]" style="border-bottom: solid 1px var(--divider);"/>
|
||||||
|
|
||||||
|
<code v-if="tab === 'req'">{{ reqStr }}</code>
|
||||||
|
<code v-if="tab === 'res'">{{ resStr }}</code>
|
||||||
|
</div>
|
||||||
|
</XWindow>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import * as JSON5 from 'json5';
|
||||||
|
import XWindow from '@/components/ui/window.vue';
|
||||||
|
import MkTab from '@/components/tab.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
XWindow,
|
||||||
|
MkTab,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
req: {
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['closed'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 'req',
|
||||||
|
reqStr: JSON5.stringify(this.req.req, null, '\t'),
|
||||||
|
resStr: JSON5.stringify(this.req.res, null, '\t'),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.rlkneywz {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> code {
|
||||||
|
display: block;
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 0.9em;
|
||||||
|
tab-size: 2;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
231
src/client/components/taskmanager.vue
Normal file
231
src/client/components/taskmanager.vue
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<template>
|
||||||
|
<XWindow ref="window" :initial-width="650" :initial-height="420" :can-resize="true" @closed="$emit('closed')">
|
||||||
|
<template #header>
|
||||||
|
<Fa :icon="faTerminal" style="margin-right: 0.5em;"/>Task Manager
|
||||||
|
</template>
|
||||||
|
<div class="qljqmnzj">
|
||||||
|
<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div v-if="tab === 'windows'" class="windows" v-follow>
|
||||||
|
<div class="header">
|
||||||
|
<div>#ID</div>
|
||||||
|
<div>Component</div>
|
||||||
|
<div>Action</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="p in popups">
|
||||||
|
<div>#{{ p.id }}</div>
|
||||||
|
<div>{{ p.component.name ? p.component.name : '<anonymous>' }}</div>
|
||||||
|
<div><button class="_textButton" @click="killPopup(p)">Kill</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="tab === 'stream'" class="stream" v-follow>
|
||||||
|
<div class="header">
|
||||||
|
<div>#ID</div>
|
||||||
|
<div>Ch</div>
|
||||||
|
<div>Handle</div>
|
||||||
|
<div>In</div>
|
||||||
|
<div>Out</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="c in connections">
|
||||||
|
<div>#{{ c.id }}</div>
|
||||||
|
<div>{{ c.channel }}</div>
|
||||||
|
<div v-if="c.users !== null">(shared)<span v-if="c.name">{{ ' ' + c.name }}</span></div>
|
||||||
|
<div v-else>{{ c.name ? c.name : '<anonymous>' }}</div>
|
||||||
|
<div>{{ c.in }}</div>
|
||||||
|
<div>{{ c.out }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="tab === 'streamPool'" class="streamPool" v-follow>
|
||||||
|
<div class="header">
|
||||||
|
<div>#ID</div>
|
||||||
|
<div>Ch</div>
|
||||||
|
<div>Users</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="p in pools">
|
||||||
|
<div>#{{ p.id }}</div>
|
||||||
|
<div>{{ p.channel }}</div>
|
||||||
|
<div>{{ p.users }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="tab === 'api'" class="api" v-follow>
|
||||||
|
<div class="header">
|
||||||
|
<div>#ID</div>
|
||||||
|
<div>Endpoint</div>
|
||||||
|
<div>State</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="req in apiRequests" @click="showReq(req)">
|
||||||
|
<div>#{{ req.id }}</div>
|
||||||
|
<div>{{ req.endpoint }}</div>
|
||||||
|
<div class="state" :class="req.state">{{ req.state }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div><span class="label">Windows</span>{{ popups.length }}</div>
|
||||||
|
<div><span class="label">Stream</span>{{ connections.length }}</div>
|
||||||
|
<div><span class="label">Stream (Pool)</span>{{ pools.length }}</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</XWindow>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, markRaw, onBeforeUnmount, ref, shallowRef } from 'vue';
|
||||||
|
import { faTerminal } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import XWindow from '@/components/ui/window.vue';
|
||||||
|
import MkTab from '@/components/tab.vue';
|
||||||
|
import MkButton from '@/components/ui/button.vue';
|
||||||
|
import follow from '@/directives/follow-append';
|
||||||
|
import * as os from '@/os';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
XWindow,
|
||||||
|
MkTab,
|
||||||
|
MkButton,
|
||||||
|
},
|
||||||
|
|
||||||
|
directives: {
|
||||||
|
follow
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ['closed'],
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const connections = shallowRef([]);
|
||||||
|
const pools = shallowRef([]);
|
||||||
|
const refreshStreamInfo = () => {
|
||||||
|
console.log(os.stream.sharedConnectionPools, os.stream.sharedConnections, os.stream.nonSharedConnections);
|
||||||
|
const conn = os.stream.sharedConnections.map(c => ({
|
||||||
|
id: c.id, name: c.name, channel: c.channel, users: c.pool.users, in: c.inCount, out: c.outCount,
|
||||||
|
})).concat(os.stream.nonSharedConnections.map(c => ({
|
||||||
|
id: c.id, name: c.name, channel: c.channel, users: null, in: c.inCount, out: c.outCount,
|
||||||
|
})));
|
||||||
|
conn.sort((a, b) => (a.id > b.id) ? 1 : -1);
|
||||||
|
connections.value = conn;
|
||||||
|
pools.value = os.stream.sharedConnectionPools;
|
||||||
|
};
|
||||||
|
const interval = setInterval(refreshStreamInfo, 1000);
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
|
||||||
|
const killPopup = p => {
|
||||||
|
os.popups.value = os.popups.value.filter(x => x !== p);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showReq = async req => {
|
||||||
|
os.popup(await import('./taskmanager.api-window.vue'), {
|
||||||
|
req: req
|
||||||
|
}, {
|
||||||
|
}, 'closed');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
tab: ref('stream'),
|
||||||
|
popups: os.popups,
|
||||||
|
apiRequests: os.apiRequests,
|
||||||
|
connections,
|
||||||
|
pools,
|
||||||
|
killPopup,
|
||||||
|
showReq,
|
||||||
|
faTerminal,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.qljqmnzj {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: table-row;
|
||||||
|
|
||||||
|
&:nth-child(even) {
|
||||||
|
//background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.header {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: table-cell;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.api {
|
||||||
|
> div {
|
||||||
|
&:not(.header) {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .state {
|
||||||
|
&.pending {
|
||||||
|
color: var(--warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.failed {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> footer {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-top: solid 1px var(--divider);
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
> .label {
|
||||||
|
opacity: 0.7;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: ":";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -10,7 +10,7 @@ import { faExpandAlt, faColumns, faExternalLinkAlt, faLink, faWindowMaximize } f
|
|||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||||
import { router } from '@/router';
|
import { router } from '@/router';
|
||||||
import { deckmode } from '@/config';
|
import { deckmode, url } from '@/config';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inject: {
|
inject: {
|
||||||
@@ -60,7 +60,7 @@ export default defineComponent({
|
|||||||
action: () => {
|
action: () => {
|
||||||
os.pageWindow(this.to);
|
os.pageWindow(this.to);
|
||||||
}
|
}
|
||||||
}, !this.navHook && this.sideViewHook ? {
|
}, this.sideViewHook ? {
|
||||||
icon: faColumns,
|
icon: faColumns,
|
||||||
text: this.$t('openInSideView'),
|
text: this.$t('openInSideView'),
|
||||||
action: () => {
|
action: () => {
|
||||||
@@ -82,7 +82,7 @@ export default defineComponent({
|
|||||||
icon: faLink,
|
icon: faLink,
|
||||||
text: this.$t('copyLink'),
|
text: this.$t('copyLink'),
|
||||||
action: () => {
|
action: () => {
|
||||||
copyToClipboard(this.to);
|
copyToClipboard(`${url}${this.to}`);
|
||||||
}
|
}
|
||||||
}], e);
|
}], e);
|
||||||
},
|
},
|
||||||
|
@@ -119,6 +119,9 @@ export default defineComponent({
|
|||||||
z: Number(document.defaultView.getComputedStyle(this.$el, null).zIndex)
|
z: Number(document.defaultView.getComputedStyle(this.$el, null).zIndex)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
|
||||||
|
this.top();
|
||||||
|
|
||||||
window.addEventListener('resize', this.onBrowserResize);
|
window.addEventListener('resize', this.onBrowserResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -14,3 +14,4 @@ export const getLocale = async () => Object.fromEntries((await entries(clientDb.
|
|||||||
export const version = _VERSION_;
|
export const version = _VERSION_;
|
||||||
export const instanceName = siteName === 'Misskey' ? host : siteName;
|
export const instanceName = siteName === 'Misskey' ? host : siteName;
|
||||||
export const deckmode = localStorage.getItem('deckmode') === 'true';
|
export const deckmode = localStorage.getItem('deckmode') === 'true';
|
||||||
|
export const debug = localStorage.getItem('debug') === 'true';
|
||||||
|
25
src/client/directives/follow-append.ts
Normal file
25
src/client/directives/follow-append.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Directive } from 'vue';
|
||||||
|
import { getScrollContainer, getScrollPosition } from '@/scripts/scroll';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mounted(src, binding, vn) {
|
||||||
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
|
const pos = getScrollPosition(src);
|
||||||
|
const container = getScrollContainer(src);
|
||||||
|
const viewHeight = container.clientHeight;
|
||||||
|
const height = container.scrollHeight;
|
||||||
|
if (pos + viewHeight > height - 32) {
|
||||||
|
container.scrollTop = height;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ro.observe(src);
|
||||||
|
|
||||||
|
// TODO: 新たにプロパティを作るのをやめMapを使う
|
||||||
|
src._ro_ = ro;
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted(src, binding, vn) {
|
||||||
|
src._ro_.unobserve(src);
|
||||||
|
}
|
||||||
|
} as Directive;
|
@@ -252,7 +252,7 @@ if (store.getters.isSignedIn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const main = stream.useSharedConnection('main');
|
const main = stream.useSharedConnection('main', 'System');
|
||||||
|
|
||||||
// 自分の情報が更新されたとき
|
// 自分の情報が更新されたとき
|
||||||
main.on('meUpdated', i => {
|
main.on('meUpdated', i => {
|
||||||
|
@@ -2,7 +2,7 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vu
|
|||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import Stream from '@/scripts/stream';
|
import Stream from '@/scripts/stream';
|
||||||
import { store } from '@/store';
|
import { store } from '@/store';
|
||||||
import { apiUrl } from '@/config';
|
import { apiUrl, debug } from '@/config';
|
||||||
import MkPostFormDialog from '@/components/post-form-dialog.vue';
|
import MkPostFormDialog from '@/components/post-form-dialog.vue';
|
||||||
import MkWaitingDialog from '@/components/waiting-dialog.vue';
|
import MkWaitingDialog from '@/components/waiting-dialog.vue';
|
||||||
import { resolve } from '@/router';
|
import { resolve } from '@/router';
|
||||||
@@ -13,28 +13,30 @@ export const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
|||||||
export const stream = markRaw(new Stream());
|
export const stream = markRaw(new Stream());
|
||||||
|
|
||||||
export const pendingApiRequestsCount = ref(0);
|
export const pendingApiRequestsCount = ref(0);
|
||||||
|
let apiRequestsCount = 0; // for debug
|
||||||
|
export const apiRequests = ref([]); // for debug
|
||||||
|
|
||||||
export const windows = new Map();
|
export const windows = new Map();
|
||||||
|
|
||||||
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
|
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
|
||||||
pendingApiRequestsCount.value++;
|
pendingApiRequestsCount.value++;
|
||||||
|
|
||||||
if (_DEV_) {
|
|
||||||
performance.mark(_PERF_PREFIX_ + 'api:begin');
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFinally = () => {
|
const onFinally = () => {
|
||||||
pendingApiRequestsCount.value--;
|
pendingApiRequestsCount.value--;
|
||||||
|
|
||||||
if (_DEV_) {
|
|
||||||
performance.mark(_PERF_PREFIX_ + 'api:end');
|
|
||||||
|
|
||||||
performance.measure(_PERF_PREFIX_ + 'api',
|
|
||||||
_PERF_PREFIX_ + 'api:begin',
|
|
||||||
_PERF_PREFIX_ + 'api:end');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const log = debug ? reactive({
|
||||||
|
id: ++apiRequestsCount,
|
||||||
|
endpoint,
|
||||||
|
req: markRaw(data),
|
||||||
|
res: null,
|
||||||
|
state: 'pending',
|
||||||
|
}) : null;
|
||||||
|
if (debug) {
|
||||||
|
apiRequests.value.push(log);
|
||||||
|
if (apiRequests.value.length > 128) apiRequests.value.shift();
|
||||||
|
}
|
||||||
|
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
// Append a credential
|
// Append a credential
|
||||||
if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
|
if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
|
||||||
@@ -51,10 +53,21 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
|
|||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
resolve(body);
|
resolve(body);
|
||||||
|
if (debug) {
|
||||||
|
log.res = markRaw(body);
|
||||||
|
log.state = 'success';
|
||||||
|
}
|
||||||
} else if (res.status === 204) {
|
} else if (res.status === 204) {
|
||||||
resolve();
|
resolve();
|
||||||
|
if (debug) {
|
||||||
|
log.state = 'success';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reject(body.error);
|
reject(body.error);
|
||||||
|
if (debug) {
|
||||||
|
log.res = markRaw(body.error);
|
||||||
|
log.state = 'failed';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).catch(reject);
|
}).catch(reject);
|
||||||
});
|
});
|
||||||
@@ -75,7 +88,7 @@ export function apiWithDialog(
|
|||||||
promiseDialog(promise, onSuccess, onFailure ? onFailure : (e) => {
|
promiseDialog(promise, onSuccess, onFailure ? onFailure : (e) => {
|
||||||
dialog({
|
dialog({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
text: e.message + '<br>' + (e as any).id,
|
text: e.message + '\n' + (e as any).id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,6 +140,7 @@ function isModule(x: any): x is typeof import('*.vue') {
|
|||||||
return x.default != null;
|
return x.default != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let popupIdCount = 0;
|
||||||
export const popups = ref([]) as Ref<{
|
export const popups = ref([]) as Ref<{
|
||||||
id: any;
|
id: any;
|
||||||
component: any;
|
component: any;
|
||||||
@@ -137,7 +151,7 @@ export function popup(component: Component | typeof import('*.vue'), props: Reco
|
|||||||
if (isModule(component)) component = component.default;
|
if (isModule(component)) component = component.default;
|
||||||
markRaw(component);
|
markRaw(component);
|
||||||
|
|
||||||
const id = Math.random().toString(); // TODO: uuidとか使う
|
const id = ++popupIdCount;
|
||||||
const dispose = () => {
|
const dispose = () => {
|
||||||
if (_DEV_) console.log('os:popup close', id, component, props, events);
|
if (_DEV_) console.log('os:popup close', id, component, props, events);
|
||||||
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
|
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ?
|
||||||
|
@@ -42,7 +42,8 @@
|
|||||||
<small style="opacity: 0.7;">{{ file.name }}</small>
|
<small style="opacity: 0.7;">{{ file.name }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<MkAcct :user="file.user"/>
|
<MkAcct v-if="file.user" :user="file.user"/>
|
||||||
|
<div v-else>{{ $t('system') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span style="margin-right: 1em;">{{ file.type }}</span>
|
<span style="margin-right: 1em;">{{ file.type }}</span>
|
||||||
|
@@ -231,7 +231,7 @@ export default defineComponent({
|
|||||||
set(pos) {
|
set(pos) {
|
||||||
if (this.game.isEnded) return;
|
if (this.game.isEnded) return;
|
||||||
if (!this.iAmPlayer) return;
|
if (!this.iAmPlayer) return;
|
||||||
if (!this.isMyTurn) return;
|
if (!this.isMyTurn()) return;
|
||||||
if (!this.o.canPut(this.myColor, pos)) return;
|
if (!this.o.canPut(this.myColor, pos)) return;
|
||||||
|
|
||||||
this.o.put(this.myColor, pos);
|
this.o.put(this.myColor, pos);
|
||||||
|
@@ -10,22 +10,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="_section">
|
<div class="_section">
|
||||||
|
<MkSwitch v-model:value="debug" @update:value="changeDebug">
|
||||||
|
DEBUG MODE
|
||||||
|
</MkSwitch>
|
||||||
|
<div v-if="debug">
|
||||||
<MkA to="/settings/regedit">RegEdit</MkA>
|
<MkA to="/settings/regedit">RegEdit</MkA>
|
||||||
|
<MkButton @click="taskmanager">Task Manager</MkButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||||
import { faEllipsisH } from '@fortawesome/free-solid-svg-icons';
|
import { faEllipsisH } from '@fortawesome/free-solid-svg-icons';
|
||||||
import MkSelect from '@/components/ui/select.vue';
|
import MkSelect from '@/components/ui/select.vue';
|
||||||
import MkSwitch from '@/components/ui/switch.vue';
|
import MkSwitch from '@/components/ui/switch.vue';
|
||||||
|
import MkButton from '@/components/ui/button.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
import { debug } from '@/config';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkSelect,
|
MkSelect,
|
||||||
MkSwitch,
|
MkSwitch,
|
||||||
|
MkButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
@@ -38,6 +47,7 @@ export default defineComponent({
|
|||||||
icon: faEllipsisH
|
icon: faEllipsisH
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
debug
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -46,11 +56,22 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
changeDebug(v) {
|
||||||
|
console.log(v);
|
||||||
|
localStorage.setItem('debug', v.toString());
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
|
||||||
onChangeInjectFeaturedNote(v) {
|
onChangeInjectFeaturedNote(v) {
|
||||||
os.api('i/update', {
|
os.api('i/update', {
|
||||||
injectFeaturedNote: v
|
injectFeaturedNote: v
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
taskmanager() {
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/taskmanager.vue')), {
|
||||||
|
}, {}, 'closed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -10,6 +10,10 @@
|
|||||||
<MkInput v-model:value="dialogBody">
|
<MkInput v-model:value="dialogBody">
|
||||||
<span>Body</span>
|
<span>Body</span>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
<MkRadio v-model="dialogType" value="info">Info</MkRadio>
|
||||||
|
<MkRadio v-model="dialogType" value="success">Success</MkRadio>
|
||||||
|
<MkRadio v-model="dialogType" value="warning">Warn</MkRadio>
|
||||||
|
<MkRadio v-model="dialogType" value="error">Error</MkRadio>
|
||||||
<MkSwitch v-model:value="dialogCancel">
|
<MkSwitch v-model:value="dialogCancel">
|
||||||
<span>With cancel button</span>
|
<span>With cancel button</span>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
@@ -133,6 +137,7 @@ import MkButton from '@/components/ui/button.vue';
|
|||||||
import MkInput from '@/components/ui/input.vue';
|
import MkInput from '@/components/ui/input.vue';
|
||||||
import MkSwitch from '@/components/ui/switch.vue';
|
import MkSwitch from '@/components/ui/switch.vue';
|
||||||
import MkTextarea from '@/components/ui/textarea.vue';
|
import MkTextarea from '@/components/ui/textarea.vue';
|
||||||
|
import MkRadio from '@/components/ui/radio.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -141,6 +146,7 @@ export default defineComponent({
|
|||||||
MkInput,
|
MkInput,
|
||||||
MkSwitch,
|
MkSwitch,
|
||||||
MkTextarea,
|
MkTextarea,
|
||||||
|
MkRadio,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -153,6 +159,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
dialogTitle: 'Hello',
|
dialogTitle: 'Hello',
|
||||||
dialogBody: 'World!',
|
dialogBody: 'World!',
|
||||||
|
dialogType: 'info',
|
||||||
dialogCancel: false,
|
dialogCancel: false,
|
||||||
dialogCancelByBgClick: true,
|
dialogCancelByBgClick: true,
|
||||||
dialogInput: false,
|
dialogInput: false,
|
||||||
@@ -192,6 +199,7 @@ export default defineComponent({
|
|||||||
async showDialog() {
|
async showDialog() {
|
||||||
this.dialogResult = null;
|
this.dialogResult = null;
|
||||||
this.dialogResult = await os.dialog({
|
this.dialogResult = await os.dialog({
|
||||||
|
type: this.dialogType,
|
||||||
title: this.dialogTitle,
|
title: this.dialogTitle,
|
||||||
text: this.dialogBody,
|
text: this.dialogBody,
|
||||||
showCancelButton: this.dialogCancel,
|
showCancelButton: this.dialogCancel,
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import ReconnectingWebsocket from 'reconnecting-websocket';
|
import ReconnectingWebsocket from 'reconnecting-websocket';
|
||||||
import { wsUrl } from '@/config';
|
import { markRaw } from 'vue';
|
||||||
|
import { debug, wsUrl } from '@/config';
|
||||||
import { query as urlQuery } from '../../prelude/url';
|
import { query as urlQuery } from '../../prelude/url';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +29,7 @@ export default class Stream extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public useSharedConnection(channel: string): SharedConnection {
|
public useSharedConnection(channel: string, name?: string): SharedConnection {
|
||||||
let pool = this.sharedConnectionPools.find(p => p.channel === channel);
|
let pool = this.sharedConnectionPools.find(p => p.channel === channel);
|
||||||
|
|
||||||
if (pool == null) {
|
if (pool == null) {
|
||||||
@@ -36,7 +37,7 @@ export default class Stream extends EventEmitter {
|
|||||||
this.sharedConnectionPools.push(pool);
|
this.sharedConnectionPools.push(pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = new SharedConnection(this, channel, pool);
|
const connection = markRaw(new SharedConnection(this, channel, pool, name));
|
||||||
this.sharedConnections.push(connection);
|
this.sharedConnections.push(connection);
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ export default class Stream extends EventEmitter {
|
|||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
public connectToChannel(channel: string, params?: any): NonSharedConnection {
|
public connectToChannel(channel: string, params?: any): NonSharedConnection {
|
||||||
const connection = new NonSharedConnection(this, channel, params);
|
const connection = markRaw(new NonSharedConnection(this, channel, params));
|
||||||
this.nonSharedConnections.push(connection);
|
this.nonSharedConnections.push(connection);
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
@@ -113,6 +114,7 @@ export default class Stream extends EventEmitter {
|
|||||||
|
|
||||||
for (const c of connections.filter(c => c != null)) {
|
for (const c of connections.filter(c => c != null)) {
|
||||||
c.emit(body.type, Object.freeze(body.body));
|
c.emit(body.type, Object.freeze(body.body));
|
||||||
|
if (debug) c.inCount++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.emit(type, Object.freeze(body));
|
this.emit(type, Object.freeze(body));
|
||||||
@@ -142,6 +144,8 @@ export default class Stream extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let idCounter = 0;
|
||||||
|
|
||||||
class Pool {
|
class Pool {
|
||||||
public channel: string;
|
public channel: string;
|
||||||
public id: string;
|
public id: string;
|
||||||
@@ -154,7 +158,7 @@ class Pool {
|
|||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
|
|
||||||
this.id = Math.random().toString().substr(2, 8);
|
this.id = (++idCounter).toString();
|
||||||
|
|
||||||
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
this.stream.on('_disconnected_', this.onStreamDisconnected);
|
||||||
}
|
}
|
||||||
@@ -216,11 +220,16 @@ abstract class Connection extends EventEmitter {
|
|||||||
protected stream: Stream;
|
protected stream: Stream;
|
||||||
public abstract id: string;
|
public abstract id: string;
|
||||||
|
|
||||||
constructor(stream: Stream, channel: string) {
|
public name?: string; // for debug
|
||||||
|
public inCount: number = 0; // for debug
|
||||||
|
public outCount: number = 0; // for debug
|
||||||
|
|
||||||
|
constructor(stream: Stream, channel: string, name?: string) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
@@ -233,6 +242,8 @@ abstract class Connection extends EventEmitter {
|
|||||||
type: type,
|
type: type,
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (debug) this.outCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract dispose(): void;
|
public abstract dispose(): void;
|
||||||
@@ -245,8 +256,8 @@ class SharedConnection extends Connection {
|
|||||||
return this.pool.id;
|
return this.pool.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(stream: Stream, channel: string, pool: Pool) {
|
constructor(stream: Stream, channel: string, pool: Pool, name?: string) {
|
||||||
super(stream, channel);
|
super(stream, channel, name);
|
||||||
|
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
this.pool.inc();
|
this.pool.inc();
|
||||||
@@ -273,7 +284,7 @@ class NonSharedConnection extends Connection {
|
|||||||
super(stream, channel);
|
super(stream, channel);
|
||||||
|
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.id = Math.random().toString().substr(2, 8);
|
this.id = (++idCounter).toString();
|
||||||
|
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
|
@@ -297,6 +297,21 @@ hr {
|
|||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
._borderButton {
|
||||||
|
@extend ._button;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-align: center;
|
||||||
|
border: solid 1px var(--divider);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
._popup {
|
._popup {
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
@@ -484,6 +499,60 @@ hr {
|
|||||||
100% { transform: translateY(0); }
|
100% { transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`;
|
||||||
|
// let css = '';
|
||||||
|
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
|
||||||
|
@keyframes anime-twitch {
|
||||||
|
0% { transform: translate(7px, -2px) }
|
||||||
|
5% { transform: translate(-3px, 1px) }
|
||||||
|
10% { transform: translate(-7px, -1px) }
|
||||||
|
15% { transform: translate(0px, -1px) }
|
||||||
|
20% { transform: translate(-8px, 6px) }
|
||||||
|
25% { transform: translate(-4px, -3px) }
|
||||||
|
30% { transform: translate(-4px, -6px) }
|
||||||
|
35% { transform: translate(-8px, -8px) }
|
||||||
|
40% { transform: translate(4px, 6px) }
|
||||||
|
45% { transform: translate(-3px, 1px) }
|
||||||
|
50% { transform: translate(2px, -10px) }
|
||||||
|
55% { transform: translate(-7px, 0px) }
|
||||||
|
60% { transform: translate(-2px, 4px) }
|
||||||
|
65% { transform: translate(3px, -8px) }
|
||||||
|
70% { transform: translate(6px, 7px) }
|
||||||
|
75% { transform: translate(-7px, -2px) }
|
||||||
|
80% { transform: translate(-7px, -8px) }
|
||||||
|
85% { transform: translate(9px, 3px) }
|
||||||
|
90% { transform: translate(-3px, -2px) }
|
||||||
|
95% { transform: translate(-10px, 2px) }
|
||||||
|
100% { transform: translate(-2px, -6px) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`;
|
||||||
|
// let css = '';
|
||||||
|
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
|
||||||
|
@keyframes anime-shake {
|
||||||
|
0% { transform: translate(-3px, -1px) rotate(-8deg) }
|
||||||
|
5% { transform: translate(0px, -1px) rotate(-10deg) }
|
||||||
|
10% { transform: translate(1px, -3px) rotate(0deg) }
|
||||||
|
15% { transform: translate(1px, 1px) rotate(11deg) }
|
||||||
|
20% { transform: translate(-2px, 1px) rotate(1deg) }
|
||||||
|
25% { transform: translate(-1px, -2px) rotate(-2deg) }
|
||||||
|
30% { transform: translate(-1px, 2px) rotate(-3deg) }
|
||||||
|
35% { transform: translate(2px, 1px) rotate(6deg) }
|
||||||
|
40% { transform: translate(-2px, -3px) rotate(-9deg) }
|
||||||
|
45% { transform: translate(0px, -1px) rotate(-12deg) }
|
||||||
|
50% { transform: translate(1px, 2px) rotate(10deg) }
|
||||||
|
55% { transform: translate(0px, -3px) rotate(8deg) }
|
||||||
|
60% { transform: translate(1px, -1px) rotate(8deg) }
|
||||||
|
65% { transform: translate(0px, -1px) rotate(-7deg) }
|
||||||
|
70% { transform: translate(-1px, -3px) rotate(6deg) }
|
||||||
|
75% { transform: translate(0px, -2px) rotate(4deg) }
|
||||||
|
80% { transform: translate(-2px, -1px) rotate(3deg) }
|
||||||
|
85% { transform: translate(1px, -3px) rotate(-10deg) }
|
||||||
|
90% { transform: translate(1px, 0px) rotate(3deg) }
|
||||||
|
95% { transform: translate(-2px, 0px) rotate(-3deg) }
|
||||||
|
100% { transform: translate(2px, 1px) rotate(2deg) }
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes anime-tada {
|
@keyframes anime-tada {
|
||||||
from {
|
from {
|
||||||
transform: scale3d(1, 1, 1);
|
transform: scale3d(1, 1, 1);
|
||||||
|
@@ -56,6 +56,9 @@
|
|||||||
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
|
||||||
badge: '#31b1ce',
|
badge: '#31b1ce',
|
||||||
messageBg: ':lighten<5<@bg',
|
messageBg: ':lighten<5<@bg',
|
||||||
|
success: '#86b300',
|
||||||
|
error: '#ec4137',
|
||||||
|
warn: '#ecb637',
|
||||||
htmlThemeColor: '@bg',
|
htmlThemeColor: '@bg',
|
||||||
X1: ':alpha<0<@bg',
|
X1: ':alpha<0<@bg',
|
||||||
X2: ':darken<2<@panel',
|
X2: ':darken<2<@panel',
|
||||||
|
@@ -56,6 +56,9 @@
|
|||||||
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
|
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
|
||||||
badge: '#31b1ce',
|
badge: '#31b1ce',
|
||||||
messageBg: '@panel',
|
messageBg: '@panel',
|
||||||
|
success: '#86b300',
|
||||||
|
error: '#ec4137',
|
||||||
|
warn: '#ecb637',
|
||||||
htmlThemeColor: '@bg',
|
htmlThemeColor: '@bg',
|
||||||
X1: ':alpha<0<@bg',
|
X1: ':alpha<0<@bg',
|
||||||
X2: ':darken<2<@panel',
|
X2: ':darken<2<@panel',
|
||||||
|
@@ -109,7 +109,7 @@ export default defineComponent({
|
|||||||
window.addEventListener('wheel', this.onWheel);
|
window.addEventListener('wheel', this.onWheel);
|
||||||
|
|
||||||
if (this.$store.getters.isSignedIn) {
|
if (this.$store.getters.isSignedIn) {
|
||||||
this.connection = os.stream.useSharedConnection('main');
|
this.connection = os.stream.useSharedConnection('main', 'UI');
|
||||||
this.connection.on('notification', this.onNotification);
|
this.connection.on('notification', this.onNotification);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -110,7 +110,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
'p': os.post,
|
'p': os.post,
|
||||||
'n': os.post,
|
'n': os.post,
|
||||||
's': search,
|
's': () => search(),
|
||||||
'h|/': this.help
|
'h|/': this.help
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -141,7 +141,7 @@ export default defineComponent({
|
|||||||
created() {
|
created() {
|
||||||
document.documentElement.style.overflowY = 'scroll';
|
document.documentElement.style.overflowY = 'scroll';
|
||||||
|
|
||||||
this.connection = os.stream.useSharedConnection('main');
|
this.connection = os.stream.useSharedConnection('main', 'UI');
|
||||||
this.connection.on('notification', this.onNotification);
|
this.connection.on('notification', this.onNotification);
|
||||||
|
|
||||||
if (this.$store.state.deviceUser.widgets.length === 0) {
|
if (this.$store.state.deviceUser.widgets.length === 0) {
|
||||||
|
@@ -71,7 +71,7 @@ export default defineComponent({
|
|||||||
created() {
|
created() {
|
||||||
document.documentElement.style.overflowY = 'scroll';
|
document.documentElement.style.overflowY = 'scroll';
|
||||||
|
|
||||||
this.connection = os.stream.useSharedConnection('main');
|
this.connection = os.stream.useSharedConnection('main', 'UI');
|
||||||
this.connection.on('notification', this.onNotification);
|
this.connection.on('notification', this.onNotification);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@@ -76,6 +76,8 @@ export const mfmLanguage = P.createLanguage({
|
|||||||
r.spin,
|
r.spin,
|
||||||
r.jump,
|
r.jump,
|
||||||
r.flip,
|
r.flip,
|
||||||
|
r.twitch,
|
||||||
|
r.shake,
|
||||||
r.inlineCode,
|
r.inlineCode,
|
||||||
r.mathInline,
|
r.mathInline,
|
||||||
r.mention,
|
r.mention,
|
||||||
@@ -122,6 +124,8 @@ export const mfmLanguage = P.createLanguage({
|
|||||||
},
|
},
|
||||||
jump: r => P.regexp(/<jump>(.+?)<\/jump>/, 1).map(x => createTree('jump', r.inline.atLeast(1).tryParse(x), {})),
|
jump: r => P.regexp(/<jump>(.+?)<\/jump>/, 1).map(x => createTree('jump', r.inline.atLeast(1).tryParse(x), {})),
|
||||||
flip: r => P.regexp(/<flip>(.+?)<\/flip>/, 1).map(x => createTree('flip', r.inline.atLeast(1).tryParse(x), {})),
|
flip: r => P.regexp(/<flip>(.+?)<\/flip>/, 1).map(x => createTree('flip', r.inline.atLeast(1).tryParse(x), {})),
|
||||||
|
twitch: r => P.regexp(/<twitch>(.+?)<\/twitch>/, 1).map(x => createTree('twitch', r.inline.atLeast(1).tryParse(x), {})),
|
||||||
|
shake: r => P.regexp(/<shake>(.+?)<\/shake>/, 1).map(x => createTree('shake', r.inline.atLeast(1).tryParse(x), {})),
|
||||||
center: r => r.startOfLine.then(P.regexp(/<center>([\s\S]+?)<\/center>/, 1).map(x => createTree('center', r.inline.atLeast(1).tryParse(x), {}))),
|
center: r => r.startOfLine.then(P.regexp(/<center>([\s\S]+?)<\/center>/, 1).map(x => createTree('center', r.inline.atLeast(1).tryParse(x), {}))),
|
||||||
inlineCode: () => P.regexp(/`([^´\n]+?)`/, 1).map(x => createLeaf('inlineCode', { code: x })),
|
inlineCode: () => P.regexp(/`([^´\n]+?)`/, 1).map(x => createLeaf('inlineCode', { code: x })),
|
||||||
mathBlock: r => r.startOfLine.then(P.regexp(/\\\[([\s\S]+?)\\\]/, 1).map(x => createLeaf('mathBlock', { formula: x.trim() }))),
|
mathBlock: r => r.startOfLine.then(P.regexp(/\\\[([\s\S]+?)\\\]/, 1).map(x => createLeaf('mathBlock', { formula: x.trim() }))),
|
||||||
|
@@ -67,6 +67,18 @@ export function toHtml(tokens: MfmForest | null, mentionedRemoteUsers: IMentione
|
|||||||
return el;
|
return el;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
twitch(token) {
|
||||||
|
const el = doc.createElement('i');
|
||||||
|
appendChildren(token.children, el);
|
||||||
|
return el;
|
||||||
|
},
|
||||||
|
|
||||||
|
shake(token) {
|
||||||
|
const el = doc.createElement('i');
|
||||||
|
appendChildren(token.children, el);
|
||||||
|
return el;
|
||||||
|
},
|
||||||
|
|
||||||
flip(token) {
|
flip(token) {
|
||||||
const el = doc.createElement('span');
|
const el = doc.createElement('span');
|
||||||
appendChildren(token.children, el);
|
appendChildren(token.children, el);
|
||||||
|
@@ -48,6 +48,14 @@ export function toString(tokens: MfmForest | null, opts?: RestoreOptions): strin
|
|||||||
return `<jump>${appendChildren(token.children, opts)}</jump>`;
|
return `<jump>${appendChildren(token.children, opts)}</jump>`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
twitch(token, opts) {
|
||||||
|
return `<twitch>${appendChildren(token.children, opts)}</twitch>`;
|
||||||
|
},
|
||||||
|
|
||||||
|
shake(token, opts) {
|
||||||
|
return `<shake>${appendChildren(token.children, opts)}</shake>`;
|
||||||
|
},
|
||||||
|
|
||||||
flip(token, opts) {
|
flip(token, opts) {
|
||||||
return `<flip>${appendChildren(token.children, opts)}</flip>`;
|
return `<flip>${appendChildren(token.children, opts)}</flip>`;
|
||||||
},
|
},
|
||||||
|
@@ -124,8 +124,8 @@ export class DriveFileRepository extends Repository<DriveFile> {
|
|||||||
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
|
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
|
||||||
detail: true
|
detail: true
|
||||||
}) : null,
|
}) : null,
|
||||||
userId: opts.withUser ? file.userId! : null,
|
userId: opts.withUser ? file.userId : null,
|
||||||
user: opts.withUser ? Users.pack(file.userId!) : null
|
user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,7 +2,8 @@ import config from '../../config';
|
|||||||
import { Note } from '../../models/entities/note';
|
import { Note } from '../../models/entities/note';
|
||||||
import { User, IRemoteUser } from '../../models/entities/user';
|
import { User, IRemoteUser } from '../../models/entities/user';
|
||||||
import { UserPublickey } from '../../models/entities/user-publickey';
|
import { UserPublickey } from '../../models/entities/user-publickey';
|
||||||
import { Notes, Users, UserPublickeys } from '../../models';
|
import { MessagingMessage } from '../../models/entities/messaging-message';
|
||||||
|
import { Notes, Users, UserPublickeys, MessagingMessages } from '../../models';
|
||||||
import { IObject, getApId } from './type';
|
import { IObject, getApId } from './type';
|
||||||
import { resolvePerson } from './models/person';
|
import { resolvePerson } from './models/person';
|
||||||
import { ensure } from '../../prelude/ensure';
|
import { ensure } from '../../prelude/ensure';
|
||||||
@@ -33,6 +34,24 @@ export default class DbResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> {
|
||||||
|
const parsed = this.parseUri(value);
|
||||||
|
|
||||||
|
if (parsed.id) {
|
||||||
|
return (await MessagingMessages.findOne({
|
||||||
|
id: parsed.id
|
||||||
|
})) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.uri) {
|
||||||
|
return (await MessagingMessages.findOne({
|
||||||
|
uri: parsed.uri
|
||||||
|
})) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AP Person => Misskey User in DB
|
* AP Person => Misskey User in DB
|
||||||
*/
|
*/
|
||||||
|
@@ -3,6 +3,7 @@ import deleteNode from '../../../../services/note/delete';
|
|||||||
import { apLogger } from '../../logger';
|
import { apLogger } from '../../logger';
|
||||||
import DbResolver from '../../db-resolver';
|
import DbResolver from '../../db-resolver';
|
||||||
import { getApLock } from '../../../../misc/app-lock';
|
import { getApLock } from '../../../../misc/app-lock';
|
||||||
|
import { deleteMessage } from '../../../../services/messages/delete';
|
||||||
|
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
@@ -16,7 +17,16 @@ export default async function(actor: IRemoteUser, uri: string): Promise<string>
|
|||||||
const note = await dbResolver.getNoteFromApId(uri);
|
const note = await dbResolver.getNoteFromApId(uri);
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
return 'note not found';
|
const message = await dbResolver.getMessageFromApId(uri);
|
||||||
|
if (message == null) return 'message not found';
|
||||||
|
|
||||||
|
if (message.userId !== actor.id) {
|
||||||
|
return '投稿を削除しようとしているユーザーは投稿の作成者ではありません';
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteMessage(message);
|
||||||
|
|
||||||
|
return 'ok: message deleted';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.userId !== actor.id) {
|
if (note.userId !== actor.id) {
|
||||||
@@ -24,7 +34,7 @@ export default async function(actor: IRemoteUser, uri: string): Promise<string>
|
|||||||
}
|
}
|
||||||
|
|
||||||
await deleteNode(actor, note);
|
await deleteNode(actor, note);
|
||||||
return 'ok: deleted';
|
return 'ok: note deleted';
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
@@ -87,5 +87,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
noteUserId: blockee.id
|
noteUserId: blockee.id
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Users.pack(blockee.id, user);
|
return await Users.pack(blockee.id, user, {
|
||||||
|
detail: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -82,5 +82,7 @@ export default define(meta, async (ps, user) => {
|
|||||||
// Delete blocking
|
// Delete blocking
|
||||||
await deleteBlocking(blocker, blockee);
|
await deleteBlocking(blocker, blockee);
|
||||||
|
|
||||||
return await Users.pack(blockee.id, user);
|
return await Users.pack(blockee.id, user, {
|
||||||
|
detail: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import { ID } from '../../../../../misc/cafy-id';
|
import { ID } from '../../../../../misc/cafy-id';
|
||||||
import define from '../../../define';
|
import define from '../../../define';
|
||||||
import { publishMessagingStream, publishGroupMessagingStream } from '../../../../../services/stream';
|
|
||||||
import * as ms from 'ms';
|
import * as ms from 'ms';
|
||||||
import { ApiError } from '../../../error';
|
import { ApiError } from '../../../error';
|
||||||
import { MessagingMessages } from '../../../../../models';
|
import { MessagingMessages } from '../../../../../models';
|
||||||
|
import { deleteMessage } from '../../../../../services/messages/delete';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@@ -53,12 +53,5 @@ export default define(meta, async (ps, user) => {
|
|||||||
throw new ApiError(meta.errors.noSuchMessage);
|
throw new ApiError(meta.errors.noSuchMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
await MessagingMessages.delete(message.id);
|
await deleteMessage(message);
|
||||||
|
|
||||||
if (message.recipientId) {
|
|
||||||
publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id);
|
|
||||||
publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id);
|
|
||||||
} else if (message.groupId) {
|
|
||||||
publishGroupMessagingStream(message.groupId, 'deleted', message.id);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
31
src/services/messages/delete.ts
Normal file
31
src/services/messages/delete.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import config from '../../config';
|
||||||
|
import { ensure } from '../../prelude/ensure';
|
||||||
|
import { MessagingMessages, Users } from '../../models';
|
||||||
|
import { MessagingMessage } from '../../models/entities/messaging-message';
|
||||||
|
import { publishGroupMessagingStream, publishMessagingStream } from '../stream';
|
||||||
|
import { renderActivity } from '../../remote/activitypub/renderer';
|
||||||
|
import renderDelete from '../../remote/activitypub/renderer/delete';
|
||||||
|
import renderTombstone from '../../remote/activitypub/renderer/tombstone';
|
||||||
|
import { deliver } from '../../queue';
|
||||||
|
|
||||||
|
export async function deleteMessage(message: MessagingMessage) {
|
||||||
|
await MessagingMessages.delete(message.id);
|
||||||
|
postDeleteMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postDeleteMessage(message: MessagingMessage) {
|
||||||
|
if (message.recipientId) {
|
||||||
|
const user = await Users.findOne(message.userId).then(ensure);
|
||||||
|
const recipient = await Users.findOne(message.recipientId).then(ensure);
|
||||||
|
|
||||||
|
if (Users.isLocalUser(user)) publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id);
|
||||||
|
if (Users.isLocalUser(recipient)) publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id);
|
||||||
|
|
||||||
|
if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) {
|
||||||
|
const activity = renderActivity(renderDelete(renderTombstone(`${config.url}/notes/${message.id}`), user));
|
||||||
|
deliver(user, activity, recipient.inbox);
|
||||||
|
}
|
||||||
|
} else if (message.groupId) {
|
||||||
|
publishGroupMessagingStream(message.groupId, 'deleted', message.id);
|
||||||
|
}
|
||||||
|
}
|
32
src/tools/demote-admin.ts
Normal file
32
src/tools/demote-admin.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { initDb } from '../db/postgre';
|
||||||
|
import { getRepository } from 'typeorm';
|
||||||
|
import { User } from '../models/entities/user';
|
||||||
|
|
||||||
|
async function main(username: string) {
|
||||||
|
if (!username) throw `username required`;
|
||||||
|
username = username.replace(/^@/, '');
|
||||||
|
|
||||||
|
await initDb();
|
||||||
|
const Users = getRepository(User);
|
||||||
|
|
||||||
|
const res = await Users.update({
|
||||||
|
usernameLower: username.toLowerCase(),
|
||||||
|
host: null
|
||||||
|
}, {
|
||||||
|
isAdmin: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.affected !== 1) {
|
||||||
|
throw 'Failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
main(args[0]).then(() => {
|
||||||
|
console.log('Success');
|
||||||
|
process.exit(0);
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(`Error: ${e.message || e}`);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
52
yarn.lock
52
yarn.lock
@@ -3476,7 +3476,7 @@ enhanced-resolve@^4.0.0:
|
|||||||
memory-fs "^0.5.0"
|
memory-fs "^0.5.0"
|
||||||
tapable "^1.0.0"
|
tapable "^1.0.0"
|
||||||
|
|
||||||
enhanced-resolve@^5.3.0:
|
enhanced-resolve@^5.3.1:
|
||||||
version "5.3.1"
|
version "5.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz#3f988d0d7775bdc2d96ede321dc81f8249492f57"
|
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz#3f988d0d7775bdc2d96ede321dc81f8249492f57"
|
||||||
integrity sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w==
|
integrity sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w==
|
||||||
@@ -5498,10 +5498,10 @@ iterate-value@^1.0.0:
|
|||||||
es-get-iterator "^1.0.2"
|
es-get-iterator "^1.0.2"
|
||||||
iterate-iterator "^1.0.1"
|
iterate-iterator "^1.0.1"
|
||||||
|
|
||||||
jest-worker@^26.5.0:
|
jest-worker@^26.6.1:
|
||||||
version "26.5.0"
|
version "26.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30"
|
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.1.tgz#c2ae8cde6802cc14056043f997469ec170d9c32a"
|
||||||
integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==
|
integrity sha512-R5IE3qSGz+QynJx8y+ICEkdI2OJ3RJjRQVEyCcFAd3yVhQSEtquziPO29Mlzgn07LOVE8u8jhJ1FqcwegiXWOw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
merge-stream "^2.0.0"
|
merge-stream "^2.0.0"
|
||||||
@@ -9713,17 +9713,17 @@ tar@^6.0.2:
|
|||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
terser-webpack-plugin@^5.0.0:
|
terser-webpack-plugin@^5.0.3:
|
||||||
version "5.0.0"
|
version "5.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.0.0.tgz#88f58d27d1c8244965c59540d3ccda1598fc958c"
|
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.0.3.tgz#ec60542db2421f45735c719d2e17dabfbb2e3e42"
|
||||||
integrity sha512-rf7l5a9xamIVX3enQeTl0MY2MNeZClo5yPX/tVPy22oY0nzu0b45h7JqyFi/bygqKWtzXMnml0u12mArhQPsBQ==
|
integrity sha512-zFdGk8Lh9ZJGPxxPE6jwysOlATWB8GMW8HcfGULWA/nPal+3VdATflQvSBSLQJRCmYZnfFJl6vkRTiwJGNgPiQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
jest-worker "^26.5.0"
|
jest-worker "^26.6.1"
|
||||||
p-limit "^3.0.2"
|
p-limit "^3.0.2"
|
||||||
schema-utils "^3.0.0"
|
schema-utils "^3.0.0"
|
||||||
serialize-javascript "^5.0.1"
|
serialize-javascript "^5.0.1"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
terser "^5.3.5"
|
terser "^5.3.8"
|
||||||
|
|
||||||
terser@>=4:
|
terser@>=4:
|
||||||
version "4.8.0"
|
version "4.8.0"
|
||||||
@@ -9734,10 +9734,10 @@ terser@>=4:
|
|||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
source-map-support "~0.5.12"
|
source-map-support "~0.5.12"
|
||||||
|
|
||||||
terser@^5.3.5:
|
terser@^5.3.8:
|
||||||
version "5.3.5"
|
version "5.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.5.tgz#9e080baa0568f96654621b20eb9effa440b1484e"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.8.tgz#991ae8ba21a3d990579b54aa9af11586197a75dd"
|
||||||
integrity sha512-Qw3CZAMmmfU824AoGKalx+riwocSI5Cs0PoGp9RdSLfmxkmJgyBxqLBP/isDNtFyhHnitikvRMZzyVgeq+U+Tg==
|
integrity sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map "~0.7.2"
|
source-map "~0.7.2"
|
||||||
@@ -10633,18 +10633,18 @@ webpack-sources@^1.0.1:
|
|||||||
source-list-map "^2.0.0"
|
source-list-map "^2.0.0"
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
webpack-sources@^2.0.1:
|
webpack-sources@^2.1.1:
|
||||||
version "2.0.1"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.0.1.tgz#1467f6e692ddce91e88b8044c44347b1087bbd4f"
|
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
|
||||||
integrity sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw==
|
integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==
|
||||||
dependencies:
|
dependencies:
|
||||||
source-list-map "^2.0.1"
|
source-list-map "^2.0.1"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
webpack@5.2.0:
|
webpack@5.3.2:
|
||||||
version "5.2.0"
|
version "5.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.2.0.tgz#02f22466b79751a80a50f20f027a716e296b3ef5"
|
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.3.2.tgz#f88f6f2c54eaa1f68c8f37d8984657eaf68b00f0"
|
||||||
integrity sha512-evtOjOJQq3zaHJIWsJjM4TGtNHtSrNVAIyQ+tdPW/fRd+4PLGbUG6S3xt+N4+QwDBOaCVd0xCWiHd4R6lWO5DQ==
|
integrity sha512-DXsfHoI6lQAR3KnQh7+FsRfs9fs+TEvzXCA35UbKv4kVuzslg7QCMAcpFRZNDMjdtm9N/PoO54XEzGN9TeacQg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/eslint-scope" "^3.7.0"
|
"@types/eslint-scope" "^3.7.0"
|
||||||
"@types/estree" "^0.0.45"
|
"@types/estree" "^0.0.45"
|
||||||
@@ -10655,7 +10655,7 @@ webpack@5.2.0:
|
|||||||
acorn "^8.0.4"
|
acorn "^8.0.4"
|
||||||
browserslist "^4.14.5"
|
browserslist "^4.14.5"
|
||||||
chrome-trace-event "^1.0.2"
|
chrome-trace-event "^1.0.2"
|
||||||
enhanced-resolve "^5.3.0"
|
enhanced-resolve "^5.3.1"
|
||||||
eslint-scope "^5.1.1"
|
eslint-scope "^5.1.1"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
glob-to-regexp "^0.4.1"
|
glob-to-regexp "^0.4.1"
|
||||||
@@ -10667,9 +10667,9 @@ webpack@5.2.0:
|
|||||||
pkg-dir "^4.2.0"
|
pkg-dir "^4.2.0"
|
||||||
schema-utils "^3.0.0"
|
schema-utils "^3.0.0"
|
||||||
tapable "^2.0.0"
|
tapable "^2.0.0"
|
||||||
terser-webpack-plugin "^5.0.0"
|
terser-webpack-plugin "^5.0.3"
|
||||||
watchpack "^2.0.0"
|
watchpack "^2.0.0"
|
||||||
webpack-sources "^2.0.1"
|
webpack-sources "^2.1.1"
|
||||||
|
|
||||||
websocket@1.0.32:
|
websocket@1.0.32:
|
||||||
version "1.0.32"
|
version "1.0.32"
|
||||||
|
Reference in New Issue
Block a user