Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0500600cdd | ||
![]() |
6b897e562a | ||
![]() |
f74bc309b1 | ||
![]() |
bc83963ac7 | ||
![]() |
94715e6153 | ||
![]() |
31a5247b9c | ||
![]() |
ac9f77752d | ||
![]() |
73ac2632c2 | ||
![]() |
20f11a6701 | ||
![]() |
9bdc67c1f6 | ||
![]() |
bb4ca7b31a | ||
![]() |
89f16668e6 | ||
![]() |
8409b59dea | ||
![]() |
1707002d43 | ||
![]() |
16b03fc157 | ||
![]() |
fa0023e541 | ||
![]() |
aec5911821 | ||
![]() |
26f4c038f3 | ||
![]() |
6136f6f33a | ||
![]() |
2b8d0225cc | ||
![]() |
18184441f1 | ||
![]() |
bd83939993 | ||
![]() |
2c1932faa7 | ||
![]() |
33d79420eb | ||
![]() |
80d8af84dd | ||
![]() |
aac519bf80 | ||
![]() |
d64dffbdda | ||
![]() |
4015ccef2f | ||
![]() |
b6f985abaf | ||
![]() |
2dac8d3d1f | ||
![]() |
4f284e1bc0 | ||
![]() |
b0280355e8 | ||
![]() |
048d88b784 | ||
![]() |
bb52ebdc3e | ||
![]() |
5f2fda85ba | ||
![]() |
1331f0b953 | ||
![]() |
736fdabc1d | ||
![]() |
7096c0ca49 | ||
![]() |
1a984de8e8 | ||
![]() |
285d0d13f9 | ||
![]() |
63c659bc8f | ||
![]() |
af60b45ee7 | ||
![]() |
e7effd606d | ||
![]() |
5042d23bc4 | ||
![]() |
b134467bd3 | ||
![]() |
5cc1aab530 | ||
![]() |
49d57ce049 | ||
![]() |
f1feceaf1a | ||
![]() |
1204e1c5e4 | ||
![]() |
96cf4c30cf | ||
![]() |
a3853611ad | ||
![]() |
ef7eaaabfa | ||
![]() |
a4a96710b0 | ||
![]() |
6d08909b2f | ||
![]() |
2615368b1e | ||
![]() |
2bd03ca725 | ||
![]() |
67dda01fcb | ||
![]() |
e9dc54c4d9 | ||
![]() |
fde9fc2891 | ||
![]() |
6e59798e82 | ||
![]() |
5e3cc0a3c6 | ||
![]() |
cfb35324d0 | ||
![]() |
9bfbc12afa | ||
![]() |
b41cddaf5a | ||
![]() |
1432a7193d | ||
![]() |
20630cb3a0 | ||
![]() |
2eb02ae581 | ||
![]() |
801f9027aa | ||
![]() |
802739a2df |
@@ -62,6 +62,9 @@ db:
|
||||
user: example-misskey-user
|
||||
pass: example-misskey-pass
|
||||
|
||||
# Whether disable Caching queries
|
||||
#disableCache: true
|
||||
|
||||
# Extra Connection options
|
||||
#extra:
|
||||
# ssl: true
|
||||
|
55
CHANGELOG.md
55
CHANGELOG.md
@@ -17,6 +17,61 @@ npm i -g ts-node
|
||||
npm run migrate
|
||||
```
|
||||
|
||||
11.23.1 (2019/06/25)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* クライアントの設定がリセットされることがある問題を修正
|
||||
|
||||
11.23.0 (2019/06/23)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* ホーム/デッキのカスタマイズ情報を複数のデバイスで同期できるように
|
||||
* ホーム/デッキのカスタマイズ情報の複数プリセット切り替え
|
||||
* サーバー情報にRedisのバージョンを追加
|
||||
* ServiceWorker有効化
|
||||
* MisskeyPagesでリストの要素数を取得する関数を追加
|
||||
* URLプレビューでハッシュだけ違うプレビューカードは表示しないように
|
||||
* URLプレビューをユーザーロケールで出し分けるように
|
||||
* リモートの凍結されたアカウントからのアクティビティはすべて無視するように
|
||||
|
||||
### 🐛Fixes
|
||||
* フォロー解除してもフォローボタンがフォロー中のままになる問題を修正
|
||||
|
||||
11.22.0 (2019/06/18)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 管理画面でデータベースの各テーブルのレコード数やサイズを確認できるように
|
||||
* サーバー情報にPostgreSQLのバージョンを追加
|
||||
|
||||
### 🐛Fixes
|
||||
* リモートファイルのダウンロードに失敗することがある問題を修正
|
||||
* アンケートの期間を日時指定で選択すると日時がUTCになってしまう問題を修正
|
||||
* MFMのパースを修正
|
||||
|
||||
11.21.0 (2019/06/16)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* Unicode 12.0 の絵文字に対応
|
||||
* 閉鎖しているホストにはAP deliverしないように
|
||||
* image以外はproxyしないように
|
||||
|
||||
### 🐛Fixes
|
||||
* サムネイル生成でエラーになるとファイルのアップロードに失敗する問題を修正
|
||||
* オートコンプリートにアニメーション停止が効かない問題を修正
|
||||
|
||||
11.20.4 (2019/06/13)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 検索結果がループする問題を修正
|
||||
* 設定でPostgreSQLのクエリー結果のキャッシュを無効できるように
|
||||
* 「投稿内の動きのあるテキストを無効にする」だけ反応しない問題を修正
|
||||
* 特定の操作のデータベースのパフォーマンス調整
|
||||
|
||||
11.20.3 (2019/06/10)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 絵文字サジェストが動作しなくなっていた問題を修正
|
||||
|
||||
11.20.2 (2019/06/10)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
|
@@ -124,7 +124,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<table><tr>
|
||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1.png?token-time=2145916800&token-hash=FMV7cPKBD1TU2WTbl1jg6AcdKSvTb2BSFcDhgc-EO8w%3D" alt="gutfuckllc" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
|
||||
@@ -135,7 +134,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
|
||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
@@ -179,7 +177,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Tue, 04 Jun 2019 04:21:06 UTC
|
||||
**Last updated:** Tue, 11 Jun 2019 01:46:06 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
@@ -94,9 +94,20 @@ common:
|
||||
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
|
||||
explore: "Najít uživatele"
|
||||
post-form:
|
||||
attach-location-information: "Přidat informace o lokaci"
|
||||
hide-contents: "Schovat obsah"
|
||||
reply-placeholder: "Odpovědět na tento příspěvek"
|
||||
quote-placeholder: "Citovat tento příspěvek"
|
||||
submit: "Odeslat"
|
||||
reply: "Odpovědět"
|
||||
renote: "Renotovat"
|
||||
attach-media-from-local: "Uplodovat soubor z vašeho zařízení"
|
||||
insert-a-kao: "v('ω')v"
|
||||
create-poll: "Vytvořit anketu"
|
||||
text-remain: "zbývá ještě {} znaků"
|
||||
recent-tags: "Nejnovější"
|
||||
visibility: "Viditelnost"
|
||||
geolocation-alert: "Vaše zařízení nedalo k dispozici lokaci"
|
||||
error: "Chyba"
|
||||
enter-username: "Zadejte své uživatelské jméno"
|
||||
add-visible-user: "Přidat uživatele"
|
||||
@@ -316,7 +327,10 @@ auth/views/form.vue:
|
||||
accept: "Povolit přístup"
|
||||
auth/views/index.vue:
|
||||
loading: "Načítám..."
|
||||
denied-paragraph: "Tato aplikace nebude mít přístup k Vašemu účtu."
|
||||
already-authorized: "Tato aplikace byla již autorizována."
|
||||
callback-url: "Zpátky do aplikace."
|
||||
please-go-back: "Prosím vraťte se zpátky do aplikace."
|
||||
error: "Taková relace neexistuje."
|
||||
sign-in: "Prosím přihlaste se."
|
||||
common/views/pages/explore.vue:
|
||||
@@ -366,20 +380,25 @@ common/views/components/games/reversi/reversi.room.vue:
|
||||
waiting-for-both: "Připravuji"
|
||||
cancel: "Zrušit"
|
||||
ready: "Připraveno"
|
||||
cancel-ready: "Pokračovat v přípravě"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "Nelze se připojit k serveru"
|
||||
description: "Nastal problém s Vaším připojením k internetu, nebo server není dostupný nebo zrovna probíhá údržba. Prosím {zkuste to znova} za pár minut."
|
||||
thanks: "Děkujeme že jste použili Misskey."
|
||||
common/views/components/connect-failed.troubleshooter.vue:
|
||||
title: "Poradce při potížích"
|
||||
network: "Síťové připojení"
|
||||
checking-network: "Prověřit síťové připojení"
|
||||
internet: "Připojení k internetu"
|
||||
checking-internet: "Ověřuji připojení k internetu."
|
||||
server: "Připojení k serveru"
|
||||
checking-server: "Spojuji se se serverem"
|
||||
no-network-desc: "Ujistěte se že jste připojeni k Internetu."
|
||||
no-internet: "Nejste připojeni k internetu"
|
||||
no-internet-desc: "Jste připojen k síti, ale zdá se že stále chybí připojení k Internetu. Prosím zkontrolujte Vaše připojení k Internetu."
|
||||
no-server: "Nelze se připojit k serveru Misskey"
|
||||
success: "Úspěšně se podařilo spojit s Misskey serverem"
|
||||
flush: "Vyčistit mezipaměť"
|
||||
common/views/components/media-banner.vue:
|
||||
sensitive: "Choulostivý obsah"
|
||||
click-to-show: "Klikněte pro zobrazení"
|
||||
@@ -403,8 +422,10 @@ common/views/components/theme.vue:
|
||||
find-more-theme: "Najít další vzhledy"
|
||||
theme-name: "Jméno vzhledu"
|
||||
preview-created-theme: "Náhled"
|
||||
invalid-theme: "Vzhled není validní"
|
||||
already-installed: "Tento vzhled je již nainstalován."
|
||||
saved: "Uloženo"
|
||||
manage-themes: "Správa vzhledů"
|
||||
builtin-themes: "Standardní vzhledy"
|
||||
my-themes: "Moje vzhledy"
|
||||
installed-themes: "Nainstalované vzhledy"
|
||||
@@ -415,6 +436,7 @@ common/views/components/theme.vue:
|
||||
desc: "Popis"
|
||||
export: "Exportovat"
|
||||
import: "Importovat"
|
||||
import-by-code: "nebo zkopírujte kód"
|
||||
theme-name-required: "Jméno vzhledu je povinné"
|
||||
common/views/components/cw-button.vue:
|
||||
hide: "Skrýt"
|
||||
@@ -487,10 +509,13 @@ common/views/components/poll-editor.vue:
|
||||
remove: "Odstranit tuto možnost"
|
||||
add: "+ Přidat možnost"
|
||||
destroy: "Zahodit dotazník"
|
||||
multiple: "Více odpovědí je povoleno"
|
||||
expiration: "Termín"
|
||||
infinite: "Nekonečne"
|
||||
at: "Výběr data a času"
|
||||
no-more: "Více už přidat nemůžete"
|
||||
deadline-date: "Termín ukončení"
|
||||
deadline-time: "Doba trvání"
|
||||
interval: "Trvání"
|
||||
second: "Sekunda"
|
||||
minute: "Minuta"
|
||||
@@ -498,6 +523,7 @@ common/views/components/poll-editor.vue:
|
||||
day: "Ne"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Vyberte svoji reakci"
|
||||
input-reaction-placeholder: "nebo vložte Emoji"
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "Emoji"
|
||||
people: "Lidé"
|
||||
@@ -511,6 +537,7 @@ common/views/components/emoji-picker.vue:
|
||||
common/views/components/signin.vue:
|
||||
username: "Přezdívka"
|
||||
password: "Heslo"
|
||||
token: "Token"
|
||||
signing-in: "Přihlašování..."
|
||||
or: "Nebo"
|
||||
signin-with-twitter: "Přihlásit se pomocí účtu Twitter"
|
||||
@@ -538,6 +565,7 @@ common/views/components/signup.vue:
|
||||
password-matched: "OK"
|
||||
password-not-matched: "Neshodují se"
|
||||
recaptcha: "Potvrzení"
|
||||
tos: "Podmínky užívání"
|
||||
create: "Vytvořit účet"
|
||||
some-error: "Pokus o vytvoření účtu selhal. Prosím zkuste to znovu."
|
||||
common/views/components/special-message.vue:
|
||||
@@ -576,6 +604,7 @@ common/views/components/visibility-chooser.vue:
|
||||
home: "Domů"
|
||||
specified-desc: "Poslat pouze zmíněným uživatelům"
|
||||
local-public: "Veřejná (pouze místní)"
|
||||
local-public-desc: "Nepublikovat na vzdálených serverech"
|
||||
local-home: "Domovská (pouze místní)"
|
||||
local-followers: "Pro sledující (pouze místní)"
|
||||
common/views/components/trends.vue:
|
||||
@@ -585,6 +614,8 @@ common/views/components/language-settings.vue:
|
||||
title: "Zobrazit jazyky"
|
||||
pick-language: "Zvolte jazyk"
|
||||
recommended: "Doporučené"
|
||||
auto: "Automaticky"
|
||||
specify-language: "Vyberte jazyk"
|
||||
info: "Pro aktivování změn musíte znovu načíst stránky."
|
||||
common/views/components/profile-editor.vue:
|
||||
title: "Profil"
|
||||
@@ -611,6 +642,7 @@ common/views/components/profile-editor.vue:
|
||||
email-not-verified: "Váš email není potvrzen. Prosím zkontrolujte si svou schránku."
|
||||
export: "Exportovat"
|
||||
import: "Importovat"
|
||||
export-and-import: "Import / Export"
|
||||
export-targets:
|
||||
following-list: "Seznam sledujících"
|
||||
mute-list: "Seznam ztlumených uživatelů"
|
||||
@@ -630,19 +662,31 @@ common/views/components/user-list-editor.vue:
|
||||
add-user: "Přidat uživatele"
|
||||
common/views/components/user-group-editor.vue:
|
||||
users: "Členové"
|
||||
rename: "Přejmenovat skupinu"
|
||||
delete: "Odstranit skupinu"
|
||||
transfer: "Přesunout skupinu"
|
||||
transfer-are-you-sure: "Jste si jistí že chcete přidat @$2 do skupiny: $1?"
|
||||
transferred: "Skupina přesunuta"
|
||||
remove-user: "Odebrat uživatele z této skupiny"
|
||||
delete-are-you-sure: "Jste si jistí že chcete smazat skupinu \"$1\"?"
|
||||
deleted: "Smazáno"
|
||||
invite: "Pozvat"
|
||||
invited: "Pozvánka byla úspěšně odeslána"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Seznamy"
|
||||
create-list: "Vytvořit seznam"
|
||||
list-name: "Název seznamu"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "Skupiny"
|
||||
create-group: "Vytvořit skupinu"
|
||||
group-name: "Název skupiny"
|
||||
owned-groups: "Moje skupiny"
|
||||
invites: "Pozvat"
|
||||
reject-invite: "Odmítnout"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Načítám"
|
||||
no-broadcasts: "Žádná nová oznámení"
|
||||
have-a-nice-day: "Přejeme Vám příjemný den!"
|
||||
next: "Další"
|
||||
common/views/widgets/calendar.vue:
|
||||
year: "Rok {}"
|
||||
@@ -800,6 +844,7 @@ desktop/views/components/renote-form-window.vue:
|
||||
desktop/views/components/settings.2fa.vue:
|
||||
detail: "Více…"
|
||||
url: "https://www.google.cz/landing/2step/"
|
||||
token: "Token"
|
||||
common/views/components/media-image.vue:
|
||||
click-to-show: "Klikněte pro zobrazení"
|
||||
common/views/components/api-settings.vue:
|
||||
@@ -1253,5 +1298,7 @@ pages:
|
||||
arg2: "Seznamy"
|
||||
_pick:
|
||||
arg1: "Seznamy"
|
||||
_listLen:
|
||||
arg1: "Seznamy"
|
||||
types:
|
||||
array: "Seznamy"
|
||||
|
@@ -1761,8 +1761,7 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ Tomo"
|
||||
_remind:
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A og B er ens"
|
||||
@@ -1845,6 +1844,8 @@ pages:
|
||||
_pick:
|
||||
arg1: "Lister"
|
||||
arg2: "Position"
|
||||
_listLen:
|
||||
arg1: "Lister"
|
||||
number: "Tal"
|
||||
stringToNumber: "Tekst til tal"
|
||||
_stringToNumber:
|
||||
|
@@ -929,5 +929,7 @@ pages:
|
||||
arg2: "Listen"
|
||||
_pick:
|
||||
arg1: "Listen"
|
||||
_listLen:
|
||||
arg1: "Listen"
|
||||
types:
|
||||
array: "Listen"
|
||||
|
@@ -270,6 +270,9 @@ common:
|
||||
disable-via-mobile: "Don't mark the post as 'from mobile'"
|
||||
load-raw-images: "Show attached images in original quality"
|
||||
load-remote-media: "Show media from a remote server"
|
||||
sync: "Sync"
|
||||
home-profile: "Home profile"
|
||||
deck-profile: "Deck profile"
|
||||
search: "Search"
|
||||
delete: "Delete"
|
||||
loading: "Loading"
|
||||
@@ -1103,7 +1106,10 @@ admin/views/index.vue:
|
||||
abuse: "Abuse"
|
||||
queue: "Job Queue"
|
||||
logs: "Logs"
|
||||
db: "Database"
|
||||
back-to-misskey: "Back to Misskey"
|
||||
admin/views/db.vue:
|
||||
tables: "Tables"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Dashboard"
|
||||
accounts: "Accounts"
|
||||
@@ -1857,8 +1863,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ Remaindering"
|
||||
_remind:
|
||||
mod: "÷ Remaindering"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A and B are equal"
|
||||
@@ -1941,6 +1947,8 @@ pages:
|
||||
_pick:
|
||||
arg1: "Lists"
|
||||
arg2: "Position"
|
||||
_listLen:
|
||||
arg1: "Lists"
|
||||
number: "Number"
|
||||
stringToNumber: "Text to number"
|
||||
_stringToNumber:
|
||||
|
@@ -1109,5 +1109,7 @@ pages:
|
||||
arg2: "Listas"
|
||||
_pick:
|
||||
arg1: "Listas"
|
||||
_listLen:
|
||||
arg1: "Listas"
|
||||
types:
|
||||
array: "Listas"
|
||||
|
@@ -12,7 +12,7 @@ common:
|
||||
rich-contents: "Notes"
|
||||
rich-contents-desc: "Partagez vos idées, les événements et les sujets qui vous tiennent à cœur ainsi que tout autre chose que vous souhaitez partager avec les autres. Si vous le désirez, vous pouvez décorer vos messages en utilisant une syntaxe différente ou en y joignant des sondages et des fichiers, tels que les photos ou les vidéos que vous aimez."
|
||||
reaction: "Réactions"
|
||||
reaction-desc: "Une manière simple d'exprimer vos émotions. Misskey peut attacher diverses réactions aux publications des autres utilisateurs. Si vous essayez les réactions sur Misskey, vous ne pourrez plus retourner sur une autre plateforme de réseaux sociaux n'offrant que des « J'aime »."
|
||||
reaction-desc: "Une manière simple d'exprimer vos émotions. Misskey peut attacher diverses réactions aux publications des autres utilisateur·rice·s. Si vous essayez les réactions sur Misskey, vous ne pourrez plus retourner sur une autre plateforme de réseaux sociaux n'offrant que des « J'aime »."
|
||||
ui: "Interface"
|
||||
ui-desc: "Aucune interface graphique ne peut plaire à tout le monde. Par conséquent, Misskey possède une interface utilisateur hautement personnalisable selon vos goûts. Vous pouvez rendre votre page d'accueil originale en modifiant la mise en page de votre fil et en déplaçant les widgets que vous pouvez facilement ajuster pour vous approprier cet espace."
|
||||
drive: "Drive"
|
||||
@@ -89,8 +89,8 @@ common:
|
||||
"write:reactions": "Gérer vos réactions"
|
||||
"write:votes": "Vote"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil."
|
||||
explore: "Trouver des utilisateurs"
|
||||
follow-users-to-make-your-timeline: "Les utilisateur·rice·s suivant·e·s afficheront leurs publications sur votre fil."
|
||||
explore: "Trouver des utilisateur·rice·s"
|
||||
post-form:
|
||||
attach-location-information: "Joindre des informations de localisation"
|
||||
hide-contents: "Masquer les contenus"
|
||||
@@ -148,13 +148,13 @@ common:
|
||||
public: "Public"
|
||||
home: "Principal"
|
||||
home-desc: "Publier sur le fil principal uniquement"
|
||||
followers: "Abonnés"
|
||||
followers-desc: "Publier à vos abonnés uniquement"
|
||||
followers: "Abonné·e·s"
|
||||
followers-desc: "Publier à vos abonné·e·s uniquement"
|
||||
specified: "Direct"
|
||||
specified-desc: "Publier uniquement aux utilisateurs mentionnés"
|
||||
specified-desc: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
|
||||
local-public: "Local (Public)"
|
||||
local-home: "Accueil (local uniquement)"
|
||||
local-followers: "Local (Abonnés)"
|
||||
local-followers: "Abonné·e·s (Local uniquement)"
|
||||
note-placeholders:
|
||||
a: "Que faites-vous maintenant ?"
|
||||
b: "Quoi de neuf ?"
|
||||
@@ -265,6 +265,7 @@ common:
|
||||
disable-via-mobile: "Enlever la mention publié via 'mobile'"
|
||||
load-raw-images: "Afficher les photos jointes dans leur qualité originale"
|
||||
load-remote-media: "Afficher les médias depuis le serveur distant"
|
||||
sync: "Synchroniser"
|
||||
search: "Recherche"
|
||||
delete: "Supprimer"
|
||||
loading: "Chargement en cours …"
|
||||
@@ -314,7 +315,7 @@ common:
|
||||
version: "Version"
|
||||
broadcast: "Diffusion"
|
||||
notifications: "Notifications"
|
||||
users: "Utilisateurs recommandés"
|
||||
users: "Utilisateur·rice·s recommandé·e·s"
|
||||
polls: "Sondages"
|
||||
post-form: "Champs de publication"
|
||||
server: "Infos sur le serveur"
|
||||
@@ -342,13 +343,13 @@ auth/views/index.vue:
|
||||
sign-in: "Veuillez vous connecter"
|
||||
common/views/pages/explore.vue:
|
||||
pinned-users: "Utilisateur·rice·s épinglé·e·s"
|
||||
popular-users: "Utilisateurs populaires"
|
||||
recently-updated-users: "Utilisateurs actifs récemment"
|
||||
popular-users: "Utilisateur·rice·s populaires"
|
||||
recently-updated-users: "Utilisateur·rice·s actif·ve·s récemment"
|
||||
recently-registered-users: "Les nouveaux inscrits"
|
||||
popular-tags: "Mots-clés populaires"
|
||||
federated: "Du Fédiverse"
|
||||
explore: "Explorer {host}"
|
||||
users-info: "Actuellement, {users} utilisateurs se sont inscrit ici"
|
||||
users-info: "Actuellement, {users} utilisateur·rice·s se sont inscrit ici"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "Activer la lecture"
|
||||
disable-player: "Fermer le lecteur"
|
||||
@@ -653,13 +654,13 @@ common/views/components/visibility-chooser.vue:
|
||||
followers: "Abonné·e·s"
|
||||
followers-desc: "Publier à vos abonné·e·s uniquement"
|
||||
specified: "Direct"
|
||||
specified-desc: "Publier uniquement aux utilisateurs mentionnés"
|
||||
specified-desc: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
|
||||
local-public: "Local (Public)"
|
||||
local-public-desc: "Ne pas publier pour les distants"
|
||||
local-home: "Accueil (local uniquement)"
|
||||
local-followers: "Local (Abonnés)"
|
||||
local-followers: "Abonné·e·s (Local uniquement)"
|
||||
common/views/components/trends.vue:
|
||||
count: "{} utilisateurs mentionnés"
|
||||
count: "{} utilisateur·rice·s mentionné·e·s"
|
||||
empty: "Aucune tendance"
|
||||
common/views/components/language-settings.vue:
|
||||
title: "Langue "
|
||||
@@ -724,6 +725,7 @@ common/views/components/user-group-editor.vue:
|
||||
transfer: "Transférer de groupe"
|
||||
transferred: "Groupe transféré"
|
||||
remove-user: "Enlever un utilisateur de ce groupe"
|
||||
delete-are-you-sure: "Désirez-vous vraiment supprimer le groupe $1 ?"
|
||||
deleted: "Supprimé"
|
||||
invite: "Inviter"
|
||||
invited: "Invitation envoyée avec succès"
|
||||
@@ -897,7 +899,7 @@ desktop/views/components/media-video.vue:
|
||||
sensitive: "Le contenu est NSFW"
|
||||
click-to-show: "Cliquer pour afficher"
|
||||
desktop/views/components/followers-window.vue:
|
||||
followers: "{} abonné·e·s"
|
||||
followers: "Abonné·e·s de {}"
|
||||
desktop/views/components/followers.vue:
|
||||
empty: "Il semble que vous n’avez pas encore d’abonné·e·s."
|
||||
desktop/views/components/following-window.vue:
|
||||
@@ -1088,7 +1090,10 @@ admin/views/index.vue:
|
||||
abuse: "Abus"
|
||||
queue: "File d’attente"
|
||||
logs: "Journaux"
|
||||
db: "Base de données"
|
||||
back-to-misskey: "Retour vers Misskey"
|
||||
admin/views/db.vue:
|
||||
tables: "Tables"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Tableau de bord"
|
||||
accounts: "Comptes"
|
||||
@@ -1224,7 +1229,7 @@ admin/views/charts.vue:
|
||||
per-hour: "par heure"
|
||||
federation: "Fédération"
|
||||
notes: "Publications"
|
||||
users: "Utilisateurs"
|
||||
users: "Utilisateur·rice·s"
|
||||
drive: "Lecteur"
|
||||
network: "Réseau"
|
||||
charts:
|
||||
@@ -1234,8 +1239,8 @@ admin/views/charts.vue:
|
||||
local-notes: "Nombre des publications : augmentation/diminution (Local)"
|
||||
remote-notes: "Nombre de publications : augmentation/diminution (distants)"
|
||||
notes-total: "Total des notes"
|
||||
users: "Nombre d’utilisateurs : augmentation/diminution"
|
||||
users-total: "Nombre total des utilisateurs"
|
||||
users: "Nombre d’utilisateur·rice·s : augmentation/diminution"
|
||||
users-total: "Nombre total des utilisateur·rice·s"
|
||||
active-users: "Utilisateur·rice·s actif·ve·s"
|
||||
drive: "Capacité utilisée comme stockage : augmentation/diminution"
|
||||
drive-total: "Utilisation totale du lecteur"
|
||||
@@ -1266,6 +1271,7 @@ admin/views/drive.vue:
|
||||
unmark-as-sensitive: "Ne pas marquer comme sensible"
|
||||
marked-as-sensitive: "Marqué comme sensible"
|
||||
unmarked-as-sensitive: "Marqué comme non sensible"
|
||||
clean-remote-files: "Nettoyer le cache des fichiers distants"
|
||||
clean-up: "Nettoyage"
|
||||
admin/views/users.vue:
|
||||
operation: "Actions"
|
||||
@@ -1374,7 +1380,7 @@ admin/views/federation.vue:
|
||||
lastCommunicatedAtAsc: "La date et l'heure des interactions plus anciennes"
|
||||
lastCommunicatedAtDesc: "La date et l'heure des nouvelles interactions"
|
||||
notesDesc: "Description des notes"
|
||||
usersAsc: "Peu d'abonnés"
|
||||
usersAsc: "Peu d'abonné·e·s"
|
||||
followingAsc: "Les moins suivies"
|
||||
followingDesc: "Ayant le plus d'abonné·e·s"
|
||||
followersAsc: "Ayant le moins d'abonné·e·s"
|
||||
@@ -1389,7 +1395,7 @@ admin/views/federation.vue:
|
||||
charts: "Graphs"
|
||||
chart-srcs:
|
||||
requests: "Requêtes"
|
||||
users: "Nombre d’utilisateurs·trices : augmentation/diminution"
|
||||
users: "Nombre d’utilisateur·trice·s : augmentation/diminution"
|
||||
users-total: "Nombre total des utilisateur·rice·s"
|
||||
notes: "Augmentation/diminution du nombre des notes"
|
||||
notes-total: "Nombre total des notes"
|
||||
@@ -1431,7 +1437,7 @@ desktop/views/pages/user-list.users.vue:
|
||||
desktop/views/pages/user/user.followers-you-know.vue:
|
||||
title: "Abonné·e·s que vous connaissez"
|
||||
loading: "Chargement en cours"
|
||||
no-users: "Aucun abonné connu"
|
||||
no-users: "Aucun·e abonné·e connu·e"
|
||||
desktop/views/pages/user/user.friends.vue:
|
||||
title: "Mentions fréquentes"
|
||||
loading: "Chargement en cours"
|
||||
@@ -1646,7 +1652,7 @@ deck/deck.user-column.vue:
|
||||
follows-you: "Vous suit"
|
||||
posts: "Notes"
|
||||
following: "Suit"
|
||||
followers: "Abonnés"
|
||||
followers: "Abonné·e·s"
|
||||
images: "Images"
|
||||
activity: "Activité"
|
||||
timeline: "Fil d’actualité"
|
||||
@@ -1688,6 +1694,7 @@ pages:
|
||||
view-source: "Afficher la source"
|
||||
view-page: "Afficher la page"
|
||||
like: "Bien"
|
||||
liked-pages: "Pages favorites"
|
||||
my-pages: "Mes pages"
|
||||
inspector: "Inspecteur"
|
||||
content: "Bloc de page"
|
||||
@@ -1801,7 +1808,7 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_remind:
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A et B sont équivalents"
|
||||
@@ -1873,6 +1880,8 @@ pages:
|
||||
_pick:
|
||||
arg1: "Listes"
|
||||
arg2: "Position"
|
||||
_listLen:
|
||||
arg1: "Listes"
|
||||
number: "Numérique"
|
||||
stringToNumber: "Chaîne en chiffres"
|
||||
_stringToNumber:
|
||||
|
@@ -282,6 +282,9 @@ common:
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
sync: "同期"
|
||||
home-profile: "ホームのプロファイル"
|
||||
deck-profile: "デッキのプロファイル"
|
||||
|
||||
search: "検索"
|
||||
delete: "削除"
|
||||
@@ -1226,8 +1229,12 @@ admin/views/index.vue:
|
||||
abuse: "スパム報告"
|
||||
queue: "ジョブキュー"
|
||||
logs: "ログ"
|
||||
db: "データベース"
|
||||
back-to-misskey: "Misskeyに戻る"
|
||||
|
||||
admin/views/db.vue:
|
||||
tables: "テーブル"
|
||||
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "ダッシュボード"
|
||||
accounts: "アカウント"
|
||||
@@ -2060,8 +2067,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 割った余り"
|
||||
_remind:
|
||||
mod: "÷ 割った余り"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "AとBが同じ"
|
||||
@@ -2144,6 +2151,9 @@ pages:
|
||||
_pick:
|
||||
arg1: "リスト"
|
||||
arg2: "位置"
|
||||
listLen: "リストの長さを取得"
|
||||
_listLen:
|
||||
arg1: "リスト"
|
||||
number: "数値"
|
||||
stringToNumber: "テキストを数値に"
|
||||
_stringToNumber:
|
||||
|
@@ -1254,5 +1254,7 @@ pages:
|
||||
arg2: "リスト"
|
||||
_pick:
|
||||
arg1: "リスト"
|
||||
_listLen:
|
||||
arg1: "リスト"
|
||||
types:
|
||||
array: "リスト"
|
||||
|
@@ -116,7 +116,7 @@ common:
|
||||
local-only-message: "이 글은 로컬에만 공개되어 있습니다"
|
||||
click-to-tagging: "클릭하여 태그 넣기"
|
||||
visibility: "공개 범위"
|
||||
geolocation-alert: "사용 중인 장치에서는 위치 정보를 사용할 수 없습니다"
|
||||
geolocation-alert: "사용 중이신 장치에서는 위치 정보를 사용할 수 없습니다"
|
||||
error: "오류"
|
||||
enter-username: "사용자명을 입력해주세요"
|
||||
add-visible-user: "사용자 추가"
|
||||
@@ -270,6 +270,9 @@ common:
|
||||
disable-via-mobile: "작성하는 글에 \"모바일에서 작성함\" 을 붙이지 않음"
|
||||
load-raw-images: "첨부 이미지를 고품질로 표시"
|
||||
load-remote-media: "원격 서버의 미디어를 표시"
|
||||
sync: "동기화"
|
||||
home-profile: "홈 프로필"
|
||||
deck-profile: "덱 프로필"
|
||||
search: "검색"
|
||||
delete: "삭제"
|
||||
loading: "로드 중"
|
||||
@@ -286,7 +289,7 @@ common:
|
||||
is-remote-user: "이 사용자 정보는 정확하지 않을 수 있습니다."
|
||||
is-remote-post: "이 글 정보는 복사본입니다."
|
||||
view-on-remote: "정확한 정보 보기"
|
||||
renoted-by: "{user}이(가) 리노트"
|
||||
renoted-by: "{user}님이 리노트"
|
||||
no-notes: "글이 없습니다"
|
||||
turn-on-darkmode: "어둠에 삼켜져라"
|
||||
turn-off-darkmode: "빛이 있으라"
|
||||
@@ -1103,7 +1106,10 @@ admin/views/index.vue:
|
||||
abuse: "스팸 신고"
|
||||
queue: "작업 대기열"
|
||||
logs: "로그"
|
||||
db: "데이터베이스"
|
||||
back-to-misskey: "Misskey로 돌아가기"
|
||||
admin/views/db.vue:
|
||||
tables: "테이블"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "대시보드"
|
||||
accounts: "계정"
|
||||
@@ -1126,7 +1132,7 @@ admin/views/queue.vue:
|
||||
states:
|
||||
active: "처리중"
|
||||
delayed: "지연됨"
|
||||
waiting: "대기열에 들어감"
|
||||
waiting: "대기열에 있음"
|
||||
result-is-truncated: "결과는 생략되었습니다"
|
||||
other-queues: "기타 큐"
|
||||
admin/views/logs.vue:
|
||||
@@ -1857,8 +1863,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 나눈 나머지"
|
||||
_remind:
|
||||
mod: "÷ 나눈 나머지"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A와 B가 동일"
|
||||
@@ -1941,6 +1947,8 @@ pages:
|
||||
_pick:
|
||||
arg1: "리스트"
|
||||
arg2: "위치"
|
||||
_listLen:
|
||||
arg1: "리스트"
|
||||
number: "수치"
|
||||
stringToNumber: "텍스트를 수치로"
|
||||
_stringToNumber:
|
||||
|
@@ -623,5 +623,7 @@ pages:
|
||||
arg2: "Lijsten"
|
||||
_pick:
|
||||
arg1: "Lijsten"
|
||||
_listLen:
|
||||
arg1: "Lijsten"
|
||||
types:
|
||||
array: "Lijsten"
|
||||
|
@@ -503,5 +503,7 @@ pages:
|
||||
arg2: "Lister"
|
||||
_pick:
|
||||
arg1: "Lister"
|
||||
_listLen:
|
||||
arg1: "Lister"
|
||||
types:
|
||||
array: "Lister"
|
||||
|
@@ -1219,5 +1219,7 @@ pages:
|
||||
arg2: "Listy"
|
||||
_pick:
|
||||
arg1: "Listy"
|
||||
_listLen:
|
||||
arg1: "Listy"
|
||||
types:
|
||||
array: "Listy"
|
||||
|
@@ -26,6 +26,13 @@ common:
|
||||
do-not-copy-paste: "Пожалуйста, не вводите и не вставляйте сюда код. Аккаунту может угрожать опасность."
|
||||
load-more: "Загрузить больше"
|
||||
enter-password: "Пожалуйста, введите ваш пароль"
|
||||
2fa: "Двухфакторная аутентификация"
|
||||
customize-home: "Настройка домашней страницы"
|
||||
dark-mode: "Тёмная тема"
|
||||
signin: "Войти"
|
||||
signup: "Регистрация"
|
||||
signout: "Выйти"
|
||||
reload-to-apply-the-setting: "Вам необходимо перезагрузить страницу, чтобы применить настройки. Вы хотите перезагрузить сейчас?"
|
||||
customization-tips:
|
||||
title: "Советы по настройке"
|
||||
gotit: "Понятно!"
|
||||
@@ -51,7 +58,14 @@ common:
|
||||
month-and-day: "{day}.{month}"
|
||||
trash: "Мусорное ведро"
|
||||
drive: "Drive"
|
||||
pages: "Страницы"
|
||||
messaging: "Чат"
|
||||
timeline: "Лента"
|
||||
followers: "Подписчики"
|
||||
favorites: "Избранное"
|
||||
post-form:
|
||||
reply: "Ответить"
|
||||
create-poll: "Создать опрос"
|
||||
weekday-short:
|
||||
sunday: "Вс"
|
||||
monday: "Пн"
|
||||
|
@@ -270,6 +270,9 @@ common:
|
||||
disable-via-mobile: "不要将帖子标记为“来自手机”"
|
||||
load-raw-images: "以原始质量显示附加图像"
|
||||
load-remote-media: "显示来自远程服务器的媒体"
|
||||
sync: "同步"
|
||||
home-profile: "定制首页数据"
|
||||
deck-profile: "定制Deck数据"
|
||||
search: "搜索"
|
||||
delete: "删除"
|
||||
loading: "正在加载中"
|
||||
@@ -859,7 +862,7 @@ desktop/views/components/crop-window.vue:
|
||||
cancel: "取消"
|
||||
ok: "确定"
|
||||
desktop/views/components/drive-window.vue:
|
||||
used: "使用中"
|
||||
used: "已使用"
|
||||
desktop/views/components/drive.file.vue:
|
||||
avatar: "头像"
|
||||
banner: "背景"
|
||||
@@ -1016,7 +1019,7 @@ desktop/views/components/settings.apps.vue:
|
||||
no-apps: "没有已连接的应用程序"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "正在使用"
|
||||
in-use: "已使用"
|
||||
stats: "统计"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "屏蔽/拉黑"
|
||||
@@ -1103,7 +1106,10 @@ admin/views/index.vue:
|
||||
abuse: "举报垃圾信息"
|
||||
queue: "作业队列"
|
||||
logs: "登录"
|
||||
db: "数据库"
|
||||
back-to-misskey: "返回 Misskey"
|
||||
admin/views/db.vue:
|
||||
tables: "表格"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Dashboard"
|
||||
accounts: "账户"
|
||||
@@ -1518,7 +1524,7 @@ desktop/views/widgets/users.vue:
|
||||
refresh: "更多"
|
||||
no-one: "不是!"
|
||||
mobile/views/components/drive.vue:
|
||||
used: "使用中"
|
||||
used: "已使用"
|
||||
folder-count: "文件夹"
|
||||
count-separator: ","
|
||||
file-count: "文件"
|
||||
@@ -1857,8 +1863,8 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 取模"
|
||||
_remind:
|
||||
mod: "÷ 取模"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A和B相等"
|
||||
@@ -1941,6 +1947,9 @@ pages:
|
||||
_pick:
|
||||
arg1: "列表"
|
||||
arg2: "位置"
|
||||
listLen: "获取列表长度"
|
||||
_listLen:
|
||||
arg1: "列表"
|
||||
number: "数值"
|
||||
stringToNumber: "文本到数字"
|
||||
_stringToNumber:
|
||||
|
16
package.json
16
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.20.2",
|
||||
"version": "11.23.1",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -99,7 +99,7 @@
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "3.0.1",
|
||||
"apexcharts": "3.7.1",
|
||||
"apexcharts": "3.8.0",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
@@ -165,11 +165,11 @@
|
||||
"loader-utils": "1.2.3",
|
||||
"lolex": "3.1.0",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"minio": "7.0.8",
|
||||
"minio": "7.0.10",
|
||||
"mocha": "6.1.4",
|
||||
"moji": "0.5.1",
|
||||
"moment": "2.24.0",
|
||||
"ms": "2.1.1",
|
||||
"ms": "2.1.2",
|
||||
"nested-property": "0.0.7",
|
||||
"node-fetch": "2.6.0",
|
||||
"nodemailer": "6.2.1",
|
||||
@@ -214,8 +214,8 @@
|
||||
"style-loader": "0.23.1",
|
||||
"stylus": "0.54.5",
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.2.0",
|
||||
"systeminformation": "4.1.6",
|
||||
"summaly": "2.3.0",
|
||||
"systeminformation": "4.11.1",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.3.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
@@ -255,8 +255,8 @@
|
||||
"vuex": "3.1.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.5",
|
||||
"webpack": "4.33.0",
|
||||
"webpack-cli": "3.3.2",
|
||||
"webpack": "4.34.0",
|
||||
"webpack-cli": "3.3.4",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "7.0.0",
|
||||
"xev": "2.0.1"
|
||||
|
@@ -124,7 +124,7 @@ export default Vue.extend({
|
||||
this.connection = this.$root.stream.useSharedConnection('serverStats');
|
||||
|
||||
this.updateStats();
|
||||
this.clock = setInterval(this.updateStats, 1000);
|
||||
this.clock = setInterval(this.updateStats, 3000);
|
||||
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.meta = meta;
|
||||
|
39
src/client/app/admin/views/db.vue
Normal file
39
src/client/app/admin/views/db.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faDatabase"/> {{ $t('tables') }}</template>
|
||||
<section v-if="tables">
|
||||
<div v-for="table in Object.keys(tables)"><b>{{ table }}</b> {{ tables[table].count | number }} {{ tables[table].size | bytes }}</div>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/db.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
tables: null,
|
||||
faDatabase
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
this.$root.api('admin/get-table-stats').then(tables => {
|
||||
this.tables = tables;
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
@@ -22,6 +22,7 @@
|
||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
|
||||
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
|
||||
<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
|
||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
||||
@@ -43,6 +44,7 @@
|
||||
<div v-if="page == 'instance'"><x-instance/></div>
|
||||
<div v-if="page == 'queue'"><x-queue/></div>
|
||||
<div v-if="page == 'logs'"><x-logs/></div>
|
||||
<div v-if="page == 'db'"><x-db/></div>
|
||||
<div v-if="page == 'moderators'"><x-moderators/></div>
|
||||
<div v-if="page == 'users'"><x-users/></div>
|
||||
<div v-if="page == 'emoji'"><x-emoji/></div>
|
||||
@@ -59,19 +61,20 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { version } from '../../config';
|
||||
import XDashboard from "./dashboard.vue";
|
||||
import XInstance from "./instance.vue";
|
||||
import XQueue from "./queue.vue";
|
||||
import XLogs from "./logs.vue";
|
||||
import XModerators from "./moderators.vue";
|
||||
import XEmoji from "./emoji.vue";
|
||||
import XAnnouncements from "./announcements.vue";
|
||||
import XUsers from "./users.vue";
|
||||
import XDrive from "./drive.vue";
|
||||
import XAbuse from "./abuse.vue";
|
||||
import XFederation from "./federation.vue";
|
||||
import XDashboard from './dashboard.vue';
|
||||
import XInstance from './instance.vue';
|
||||
import XQueue from './queue.vue';
|
||||
import XLogs from './logs.vue';
|
||||
import XDb from './db.vue';
|
||||
import XModerators from './moderators.vue';
|
||||
import XEmoji from './emoji.vue';
|
||||
import XAnnouncements from './announcements.vue';
|
||||
import XUsers from './users.vue';
|
||||
import XDrive from './drive.vue';
|
||||
import XAbuse from './abuse.vue';
|
||||
import XFederation from './federation.vue';
|
||||
|
||||
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeadset, faArrowLeft, faGlobe, faExclamationCircle, faTasks, faStream, faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
// Detect the user agent
|
||||
@@ -85,6 +88,7 @@ export default Vue.extend({
|
||||
XInstance,
|
||||
XQueue,
|
||||
XLogs,
|
||||
XDb,
|
||||
XModerators,
|
||||
XEmoji,
|
||||
XAnnouncements,
|
||||
@@ -108,7 +112,8 @@ export default Vue.extend({
|
||||
faGlobe,
|
||||
faExclamationCircle,
|
||||
faTasks,
|
||||
faStream
|
||||
faStream,
|
||||
faDatabase,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@@ -60,9 +60,9 @@ export default function <T extends object>(data: {
|
||||
|
||||
save() {
|
||||
if (this.platform == 'deck') {
|
||||
this.$store.commit('device/updateDeckColumn', this.column);
|
||||
this.$store.commit('updateDeckColumn', this.column);
|
||||
} else {
|
||||
this.$store.commit('device/updateWidget', this.widget);
|
||||
this.$store.commit('updateWidget', this.widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -83,9 +83,19 @@ export default (opts: Opts = {}) => ({
|
||||
if (this.appearNote.text) {
|
||||
const ast = parse(this.appearNote.text);
|
||||
// TODO: 再帰的にURL要素がないか調べる
|
||||
return unique(ast
|
||||
const urls = unique(ast
|
||||
.filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent))
|
||||
.map(t => t.node.props.url));
|
||||
|
||||
// unique without hash
|
||||
// [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
|
||||
const removeHash = x => x.replace(/#[^#]*$/, '');
|
||||
|
||||
return urls.reduce((array, url) => {
|
||||
const removed = removeHash(url);
|
||||
if (!array.map(x => removeHash(x)).includes(removed)) array.push(url);
|
||||
return array;
|
||||
}, []);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ export default (opts) => ({
|
||||
return {
|
||||
items: [],
|
||||
queue: [],
|
||||
offset: 0,
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
inited: false,
|
||||
@@ -80,6 +81,7 @@ export default (opts) => ({
|
||||
this.items = x;
|
||||
this.more = false;
|
||||
}
|
||||
this.offset = x.length;
|
||||
this.inited = true;
|
||||
this.fetching = false;
|
||||
if (opts.onInited) opts.onInited(this);
|
||||
@@ -96,7 +98,11 @@ export default (opts) => ({
|
||||
if (params && params.then) params = await params;
|
||||
await this.$root.api(this.pagination.endpoint, {
|
||||
limit: (this.pagination.limit || 10) + 1,
|
||||
untilId: this.items[this.items.length - 1].id,
|
||||
...(this.pagination.endpoint === 'notes/search' ? {
|
||||
offset: this.offset,
|
||||
} : {
|
||||
untilId: this.items[this.items.length - 1].id,
|
||||
}),
|
||||
...params
|
||||
}).then(x => {
|
||||
if (x.length == (this.pagination.limit || 10) + 1) {
|
||||
@@ -107,6 +113,7 @@ export default (opts) => ({
|
||||
this.items = this.items.concat(x);
|
||||
this.more = false;
|
||||
}
|
||||
this.offset += x.length;
|
||||
this.moreFetching = false;
|
||||
}, e => {
|
||||
this.moreFetching = false;
|
||||
|
@@ -16,10 +16,10 @@
|
||||
</ol>
|
||||
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
|
||||
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
|
||||
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else-if="!useOsDefaultEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else>{{ emoji.emoji }}</span>
|
||||
<span class="name">{{ beforeQ }}<b>{{ q }}</b>{{ afterQ }}</span>
|
||||
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
|
||||
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -31,6 +31,7 @@ import Vue from 'vue';
|
||||
import * as emojilib from 'emojilib';
|
||||
import contains from '../../../common/scripts/contains';
|
||||
import { twemojiBase } from '../../../../../misc/twemoji-base';
|
||||
import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
|
||||
|
||||
type EmojiDef = {
|
||||
emoji: string;
|
||||
@@ -78,6 +79,7 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
getStaticImageUrl,
|
||||
fetching: true,
|
||||
users: [],
|
||||
hashtags: [],
|
||||
@@ -89,14 +91,6 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
computed: {
|
||||
beforeQ(): string {
|
||||
return this.emoji.name.split(this.q)[0];
|
||||
},
|
||||
|
||||
afterQ(): string {
|
||||
return this.emoji.name.split(this.q)[1] || '';
|
||||
},
|
||||
|
||||
items(): HTMLCollection {
|
||||
return (this.$refs.suggests as Element).children;
|
||||
},
|
||||
|
@@ -89,9 +89,7 @@ export default Vue.extend({
|
||||
|
||||
get() {
|
||||
const at = () => {
|
||||
const [date] = moment(this.atDate).toISOString().split('T');
|
||||
const [hour, minute] = this.atTime.split(':');
|
||||
return moment(`${date}T${hour}:${minute}Z`).valueOf();
|
||||
return moment(`${this.atDate} ${this.atTime}`).valueOf();
|
||||
};
|
||||
|
||||
const after = () => {
|
||||
|
@@ -131,6 +131,13 @@
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('@._settings.sync') }}</header>
|
||||
<ui-input v-if="$root.isMobile" v-model="mobileHomeProfile" :datalist="Object.keys($store.state.settings.mobileHomeProfiles)">{{ $t('@._settings.home-profile') }}</ui-input>
|
||||
<ui-input v-else v-model="homeProfile" :datalist="Object.keys($store.state.settings.homeProfiles)">{{ $t('@._settings.home-profile') }}</ui-input>
|
||||
<ui-input v-model="deckProfile" :datalist="Object.keys($store.state.settings.deckProfiles)">{{ $t('@._settings.deck-profile') }}</ui-input>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('@._settings.web-search-engine') }}</header>
|
||||
<ui-input v-model="webSearchEngine">{{ $t('@._settings.web-search-engine') }}<template #desc>{{ $t('@._settings.web-search-engine-desc') }}</template></ui-input>
|
||||
@@ -500,6 +507,21 @@ export default Vue.extend({
|
||||
get() { return this.$store.state.device.mobileNotificationPosition; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); }
|
||||
},
|
||||
|
||||
homeProfile: {
|
||||
get() { return this.$store.state.device.homeProfile; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'homeProfile', value }); }
|
||||
},
|
||||
|
||||
mobileHomeProfile: {
|
||||
get() { return this.$store.state.device.mobileHomeProfile; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'mobileHomeProfile', value }); }
|
||||
},
|
||||
|
||||
deckProfile: {
|
||||
get() { return this.$store.state.device.deckProfile; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'deckProfile', value }); }
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$root.getMeta().then(meta => {
|
||||
|
@@ -30,7 +30,7 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { url as local } from '../../../config';
|
||||
import { url as local, lang } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/url-preview.vue'),
|
||||
@@ -89,10 +89,10 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
const url = new URL(this.url);
|
||||
const requestUrl = new URL(this.url);
|
||||
|
||||
if (this.detail && url.hostname == 'twitter.com' && /^\/.+\/status(es)?\/\d+/.test(url.pathname)) {
|
||||
this.tweetUrl = url;
|
||||
if (this.detail && requestUrl.hostname == 'twitter.com' && /^\/.+\/status(es)?\/\d+/.test(requestUrl.pathname)) {
|
||||
this.tweetUrl = requestUrl;
|
||||
const twttr = (window as any).twttr || {};
|
||||
const loadTweet = () => twttr.widgets.load(this.$refs.tweet);
|
||||
|
||||
@@ -113,10 +113,15 @@ export default Vue.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.hostname === 'music.youtube.com')
|
||||
url.hostname = 'youtube.com';
|
||||
if (requestUrl.hostname === 'music.youtube.com') {
|
||||
requestUrl.hostname = 'youtube.com';
|
||||
}
|
||||
|
||||
fetch(`/url?url=${encodeURIComponent(this.url)}`).then(res => {
|
||||
const requestLang = (lang || 'ja-JP').replace('ja-KS', 'ja-JP');
|
||||
|
||||
requestUrl.hash = '';
|
||||
|
||||
fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).then(res => {
|
||||
res.json().then(info => {
|
||||
if (info.url == null) return;
|
||||
this.title = info.title;
|
||||
|
@@ -146,7 +146,8 @@ export default Vue.extend({
|
||||
|
||||
toggleActive() {
|
||||
if (!this.isStacked) return;
|
||||
const vms = this.$store.state.device.deck.layout.find(ids => ids.indexOf(this.column.id) != -1).map(id => this.getColumnVm(id));
|
||||
const deck = this.$store.state.device.deckProfile ? this.$store.state.settings.deckProfiles[this.$store.state.device.deckProfile] : this.$store.state.device.deck;
|
||||
const vms = deck.layout.find(ids => ids.indexOf(this.column.id) != -1).map(id => this.getColumnVm(id));
|
||||
if (this.active && countIf(vm => vm.$el.classList.contains('active'), vms) == 1) return;
|
||||
this.active = !this.active;
|
||||
},
|
||||
@@ -179,50 +180,50 @@ export default Vue.extend({
|
||||
}
|
||||
}).then(({ canceled, result: name }) => {
|
||||
if (canceled) return;
|
||||
this.$store.commit('device/renameDeckColumn', { id: this.column.id, name });
|
||||
this.$store.commit('renameDeckColumn', { id: this.column.id, name });
|
||||
});
|
||||
}
|
||||
}, null, {
|
||||
icon: 'arrow-left',
|
||||
text: this.$t('swap-left'),
|
||||
action: () => {
|
||||
this.$store.commit('device/swapLeftDeckColumn', this.column.id);
|
||||
this.$store.commit('swapLeftDeckColumn', this.column.id);
|
||||
}
|
||||
}, {
|
||||
icon: 'arrow-right',
|
||||
text: this.$t('swap-right'),
|
||||
action: () => {
|
||||
this.$store.commit('device/swapRightDeckColumn', this.column.id);
|
||||
this.$store.commit('swapRightDeckColumn', this.column.id);
|
||||
}
|
||||
}, this.isStacked ? {
|
||||
icon: faArrowUp,
|
||||
text: this.$t('swap-up'),
|
||||
action: () => {
|
||||
this.$store.commit('device/swapUpDeckColumn', this.column.id);
|
||||
this.$store.commit('swapUpDeckColumn', this.column.id);
|
||||
}
|
||||
} : undefined, this.isStacked ? {
|
||||
icon: faArrowDown,
|
||||
text: this.$t('swap-down'),
|
||||
action: () => {
|
||||
this.$store.commit('device/swapDownDeckColumn', this.column.id);
|
||||
this.$store.commit('swapDownDeckColumn', this.column.id);
|
||||
}
|
||||
} : undefined, null, {
|
||||
icon: ['far', 'window-restore'],
|
||||
text: this.$t('stack-left'),
|
||||
action: () => {
|
||||
this.$store.commit('device/stackLeftDeckColumn', this.column.id);
|
||||
this.$store.commit('stackLeftDeckColumn', this.column.id);
|
||||
}
|
||||
}, this.isStacked ? {
|
||||
icon: faWindowMaximize,
|
||||
text: this.$t('pop-right'),
|
||||
action: () => {
|
||||
this.$store.commit('device/popRightDeckColumn', this.column.id);
|
||||
this.$store.commit('popRightDeckColumn', this.column.id);
|
||||
}
|
||||
} : undefined, null, {
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: this.$t('remove'),
|
||||
action: () => {
|
||||
this.$store.commit('device/removeDeckColumn', this.column.id);
|
||||
this.$store.commit('removeDeckColumn', this.column.id);
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -306,7 +307,7 @@ export default Vue.extend({
|
||||
|
||||
const id = e.dataTransfer.getData('mk-deck-column');
|
||||
if (id != null && id != '') {
|
||||
this.$store.commit('device/swapDeckColumn', {
|
||||
this.$store.commit('swapDeckColumn', {
|
||||
a: this.column.id,
|
||||
b: id
|
||||
});
|
||||
|
@@ -4,8 +4,8 @@
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div>
|
||||
<header>
|
||||
<mk-reaction-icon :reaction="notification.reaction"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<mk-reaction-icon :reaction="notification.reaction" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
@@ -22,8 +22,8 @@
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div>
|
||||
<header>
|
||||
<fa icon="retweet"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<fa icon="retweet" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
@@ -40,8 +40,8 @@
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div>
|
||||
<header>
|
||||
<fa icon="user-plus"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<fa icon="user-plus" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
@@ -53,9 +53,9 @@
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div>
|
||||
<header>
|
||||
<fa icon="user-clock"/>
|
||||
<fa icon="user-clock" class="icon"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
<mk-user-name :user="notification.user" class="name"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
@@ -66,9 +66,9 @@
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div>
|
||||
<header>
|
||||
<fa icon="chart-pie"/>
|
||||
<fa icon="chart-pie" class="icon"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
<mk-user-name :user="notification.user" class="name"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
@@ -137,9 +137,13 @@ export default Vue.extend({
|
||||
align-items baseline
|
||||
white-space nowrap
|
||||
|
||||
[data-icon], .mk-reaction-icon
|
||||
> .icon
|
||||
margin-right 4px
|
||||
|
||||
> .name
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
|
||||
> .mk-time
|
||||
margin-left auto
|
||||
color var(--noteHeaderInfo)
|
||||
|
@@ -90,7 +90,7 @@ export default Vue.extend({
|
||||
|
||||
methods: {
|
||||
onChangeSettings(v) {
|
||||
this.$store.commit('device/updateDeckColumn', this.column);
|
||||
this.$store.commit('updateDeckColumn', this.column);
|
||||
},
|
||||
|
||||
focus() {
|
||||
|
@@ -25,20 +25,25 @@ import * as uuid from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('deck'),
|
||||
|
||||
components: {
|
||||
XColumnCore
|
||||
},
|
||||
|
||||
computed: {
|
||||
deck() {
|
||||
return this.$store.getters.deck;
|
||||
},
|
||||
|
||||
columns(): any[] {
|
||||
if (this.$store.state.device.deck == null) return [];
|
||||
return this.$store.state.device.deck.columns;
|
||||
if (this.deck == null) return [];
|
||||
return this.deck.columns;
|
||||
},
|
||||
|
||||
layout(): any[] {
|
||||
if (this.$store.state.device.deck == null) return [];
|
||||
if (this.$store.state.device.deck.layout == null) return this.$store.state.device.deck.columns.map(c => [c.id]);
|
||||
return this.$store.state.device.deck.layout;
|
||||
if (this.deck == null) return [];
|
||||
if (this.deck.layout == null) return this.deck.columns.map(c => [c.id]);
|
||||
return this.deck.layout;
|
||||
},
|
||||
|
||||
style(): any {
|
||||
@@ -75,7 +80,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.state.device.deck == null) {
|
||||
if (this.deck == null) {
|
||||
const deck = {
|
||||
columns: [/*{
|
||||
type: 'widgets',
|
||||
@@ -101,10 +106,7 @@ export default Vue.extend({
|
||||
|
||||
deck.layout = deck.columns.map(c => [c.id]);
|
||||
|
||||
this.$store.commit('device/set', {
|
||||
key: 'deck',
|
||||
value: deck
|
||||
});
|
||||
this.$store.commit('setDeck', deck);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -129,7 +131,7 @@ export default Vue.extend({
|
||||
icon: 'home',
|
||||
text: this.$t('@deck.home'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'home'
|
||||
});
|
||||
@@ -138,7 +140,7 @@ export default Vue.extend({
|
||||
icon: ['far', 'comments'],
|
||||
text: this.$t('@deck.local'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'local'
|
||||
});
|
||||
@@ -147,7 +149,7 @@ export default Vue.extend({
|
||||
icon: 'share-alt',
|
||||
text: this.$t('@deck.hybrid'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'hybrid'
|
||||
});
|
||||
@@ -156,7 +158,7 @@ export default Vue.extend({
|
||||
icon: 'globe',
|
||||
text: this.$t('@deck.global'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'global'
|
||||
});
|
||||
@@ -165,7 +167,7 @@ export default Vue.extend({
|
||||
icon: 'at',
|
||||
text: this.$t('@deck.mentions'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'mentions'
|
||||
});
|
||||
@@ -174,7 +176,7 @@ export default Vue.extend({
|
||||
icon: ['far', 'envelope'],
|
||||
text: this.$t('@deck.direct'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'direct'
|
||||
});
|
||||
@@ -195,7 +197,7 @@ export default Vue.extend({
|
||||
showCancelButton: true
|
||||
});
|
||||
if (canceled) return;
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'list',
|
||||
list: lists.find(l => l.id === listId)
|
||||
@@ -210,7 +212,7 @@ export default Vue.extend({
|
||||
input: true
|
||||
}).then(({ canceled, result: title }) => {
|
||||
if (canceled) return;
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'hashtag',
|
||||
tagTlId: this.$store.state.settings.tagTimelines.find(x => x.title == title).id
|
||||
@@ -221,7 +223,7 @@ export default Vue.extend({
|
||||
icon: ['far', 'bell'],
|
||||
text: this.$t('@deck.notifications'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'notifications'
|
||||
});
|
||||
@@ -230,7 +232,7 @@ export default Vue.extend({
|
||||
icon: 'calculator',
|
||||
text: this.$t('@deck.widgets'),
|
||||
action: () => {
|
||||
this.$store.commit('device/addDeckColumn', {
|
||||
this.$store.commit('addDeckColumn', {
|
||||
id: uuid(),
|
||||
type: 'widgets',
|
||||
widgets: []
|
||||
|
@@ -110,7 +110,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
addWidget() {
|
||||
this.$store.commit('device/addDeckWidget', {
|
||||
this.$store.commit('addDeckWidget', {
|
||||
id: this.column.id,
|
||||
widget: {
|
||||
name: this.widgetAdderSelected,
|
||||
@@ -123,14 +123,14 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
removeWidget(widget) {
|
||||
this.$store.commit('device/removeDeckWidget', {
|
||||
this.$store.commit('removeDeckWidget', {
|
||||
id: this.column.id,
|
||||
widget
|
||||
});
|
||||
},
|
||||
|
||||
saveWidgets() {
|
||||
this.$store.commit('device/updateDeckColumn', this.column);
|
||||
this.$store.commit('updateDeckColumn', this.column);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -3,6 +3,8 @@
|
||||
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainerEmail" target="_blank">{{ meta.maintainerName }}</a></b></p>
|
||||
<p>Machine: {{ meta.machine }}</p>
|
||||
<p>Node: {{ meta.node }}</p>
|
||||
<p>PSQL: {{ meta.psql }}</p>
|
||||
<p>Redis: {{ meta.redis }}</p>
|
||||
<p>Version: {{ meta.version }} </p>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -84,6 +84,7 @@ import XWelcome from '../pages/welcome.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('desktop/views/components/home.vue'),
|
||||
|
||||
components: {
|
||||
XDraggable,
|
||||
XWelcome
|
||||
@@ -102,7 +103,7 @@ export default Vue.extend({
|
||||
computed: {
|
||||
home(): any[] {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
return this.$store.state.device.home || [];
|
||||
return this.$store.getters.home || [];
|
||||
} else {
|
||||
return [{
|
||||
name: 'instance',
|
||||
@@ -138,7 +139,9 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
if (!this.$store.getters.isSignedIn) return;
|
||||
|
||||
if (this.$store.getters.home == null) {
|
||||
const defaultDesktopHomeWidgets = {
|
||||
left: [
|
||||
'profile',
|
||||
@@ -183,9 +186,7 @@ export default Vue.extend({
|
||||
}
|
||||
//#endregion
|
||||
|
||||
if (this.$store.state.device.home == null) {
|
||||
this.$store.commit('device/setHome', _defaultDesktopHomeWidgets);
|
||||
}
|
||||
this.$store.commit('setHome', _defaultDesktopHomeWidgets);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -223,7 +224,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
addWidget() {
|
||||
this.$store.commit('device/addHomeWidget', {
|
||||
this.$store.commit('addHomeWidget', {
|
||||
name: this.widgetAdderSelected,
|
||||
id: uuid(),
|
||||
place: 'left',
|
||||
@@ -234,7 +235,7 @@ export default Vue.extend({
|
||||
saveHome() {
|
||||
const left = this.widgets.left;
|
||||
const right = this.widgets.right;
|
||||
this.$store.commit('device/setHome', left.concat(right));
|
||||
this.$store.commit('setHome', left.concat(right));
|
||||
for (const w of left) w.place = 'left';
|
||||
for (const w of right) w.place = 'right';
|
||||
},
|
||||
@@ -245,7 +246,7 @@ export default Vue.extend({
|
||||
|
||||
focus() {
|
||||
(this.$refs.content as any).focus();
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -173,10 +173,9 @@ export default class MiOS extends EventEmitter {
|
||||
|
||||
// Init service worker
|
||||
if (this.shouldRegisterSw) {
|
||||
// #4813
|
||||
//this.getMeta().then(data => {
|
||||
// this.registerSw(data.swPublickey);
|
||||
//});
|
||||
this.getMeta().then(data => {
|
||||
this.registerSw(data.swPublickey);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -72,13 +72,13 @@ export default Vue.extend({
|
||||
|
||||
computed: {
|
||||
widgets(): any[] {
|
||||
return this.$store.state.device.mobileHome;
|
||||
return this.$store.getters.mobileHome || [];
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.widgets.length == 0) {
|
||||
this.$store.commit('device/setMobileHome', [{
|
||||
this.$store.commit('setMobileHome', [{
|
||||
name: 'calendar',
|
||||
id: 'a', data: {}
|
||||
}, {
|
||||
@@ -98,6 +98,12 @@ export default Vue.extend({
|
||||
id: 'g', data: {}
|
||||
}]);
|
||||
}
|
||||
|
||||
this.$watch('$store.getters.mobileHome', () => {
|
||||
this.$store.dispatch('settings/updateMobileHomeProfile');
|
||||
}, {
|
||||
deep: true
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@@ -122,7 +128,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
addWidget() {
|
||||
this.$store.commit('device/addMobileHomeWidget', {
|
||||
this.$store.commit('addMobileHomeWidget', {
|
||||
name: this.widgetAdderSelected,
|
||||
id: uuid(),
|
||||
data: {}
|
||||
@@ -130,11 +136,11 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
removeWidget(widget) {
|
||||
this.$store.commit('device/removeMobileHomeWidget', widget);
|
||||
this.$store.commit('removeMobileHomeWidget', widget);
|
||||
},
|
||||
|
||||
saveHome() {
|
||||
this.$store.commit('device/setMobileHome', this.widgets);
|
||||
this.$store.commit('setMobileHome', this.widgets);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import createPersistedState from 'vuex-persistedstate';
|
||||
import * as nestedProperty from 'nested-property';
|
||||
@@ -33,12 +34,16 @@ const defaultSettings = {
|
||||
mutedWords: [],
|
||||
gamesReversiShowBoardLabels: false,
|
||||
gamesReversiUseAvatarStones: true,
|
||||
disableAnimatedMfm: false,
|
||||
homeProfiles: {},
|
||||
mobileHomeProfiles: {},
|
||||
deckProfiles: {},
|
||||
};
|
||||
|
||||
const defaultDeviceSettings = {
|
||||
home: null,
|
||||
mobileHome: [],
|
||||
deck: null,
|
||||
homeProfile: 'Default',
|
||||
mobileHomeProfile: 'Default',
|
||||
deckProfile: 'Default',
|
||||
deckMode: false,
|
||||
deckColumnAlign: 'center',
|
||||
deckColumnWidth: 'normal',
|
||||
@@ -81,7 +86,13 @@ export default (os: MiOS) => new Vuex.Store({
|
||||
},
|
||||
|
||||
getters: {
|
||||
isSignedIn: state => state.i != null
|
||||
isSignedIn: state => state.i != null,
|
||||
|
||||
home: state => state.settings.homeProfiles[state.device.homeProfile],
|
||||
|
||||
mobileHome: state => state.settings.mobileHomeProfiles[state.device.mobileHomeProfile],
|
||||
|
||||
deck: state => state.settings.deckProfiles[state.device.deckProfile],
|
||||
},
|
||||
|
||||
mutations: {
|
||||
@@ -111,6 +122,216 @@ export default (os: MiOS) => new Vuex.Store({
|
||||
clearBehindNotes(state) {
|
||||
state.behindNotes = [];
|
||||
document.title = os.instanceName;
|
||||
},
|
||||
|
||||
setHome(state, data) {
|
||||
Vue.set(state.settings.homeProfiles, state.device.homeProfile, data);
|
||||
os.store.dispatch('settings/updateHomeProfile');
|
||||
},
|
||||
|
||||
setDeck(state, data) {
|
||||
Vue.set(state.settings.deckProfiles, state.device.deckProfile, data);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
addHomeWidget(state, widget) {
|
||||
state.settings.homeProfiles[state.device.homeProfile].unshift(widget);
|
||||
os.store.dispatch('settings/updateHomeProfile');
|
||||
},
|
||||
|
||||
setMobileHome(state, data) {
|
||||
Vue.set(state.settings.mobileHomeProfiles, state.device.mobileHomeProfile, data);
|
||||
os.store.dispatch('settings/updateMobileHomeProfile');
|
||||
},
|
||||
|
||||
updateWidget(state, x) {
|
||||
let w;
|
||||
|
||||
//#region Desktop home
|
||||
const home = state.settings.homeProfiles[state.device.homeProfile];
|
||||
if (home) {
|
||||
w = home.find(w => w.id == x.id);
|
||||
if (w) {
|
||||
w.data = x.data;
|
||||
os.store.dispatch('settings/updateHomeProfile');
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Mobile home
|
||||
const mobileHome = state.settings.mobileHomeProfiles[state.device.mobileHomeProfile];
|
||||
if (mobileHome) {
|
||||
w = mobileHome.find(w => w.id == x.id);
|
||||
if (w) {
|
||||
w.data = x.data;
|
||||
os.store.dispatch('settings/updateMobileHomeProfile');
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
},
|
||||
|
||||
addMobileHomeWidget(state, widget) {
|
||||
state.settings.mobileHomeProfiles[state.device.mobileHomeProfile].unshift(widget);
|
||||
os.store.dispatch('settings/updateMobileHomeProfile');
|
||||
},
|
||||
|
||||
removeMobileHomeWidget(state, widget) {
|
||||
Vue.set('state.settings.mobileHomeProfiles', state.device.mobileHomeProfile, state.settings.mobileHomeProfiles[state.device.mobileHomeProfile].filter(w => w.id != widget.id));
|
||||
os.store.dispatch('settings/updateMobileHomeProfile');
|
||||
},
|
||||
|
||||
addDeckColumn(state, column) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
if (column.name == undefined) column.name = null;
|
||||
deck.columns.push(column);
|
||||
deck.layout.push([column.id]);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
removeDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
deck.columns = deck.columns.filter(c => c.id != id);
|
||||
deck.layout = deck.layout.map(ids => erase(id, ids));
|
||||
deck.layout = deck.layout.filter(ids => ids.length > 0);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
swapDeckColumn(state, x) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const a = x.a;
|
||||
const b = x.b;
|
||||
const aX = deck.layout.findIndex(ids => ids.indexOf(a) != -1);
|
||||
const aY = deck.layout[aX].findIndex(id => id == a);
|
||||
const bX = deck.layout.findIndex(ids => ids.indexOf(b) != -1);
|
||||
const bY = deck.layout[bX].findIndex(id => id == b);
|
||||
deck.layout[aX][aY] = b;
|
||||
deck.layout[bX][bY] = a;
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
swapLeftDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
deck.layout.some((ids, i) => {
|
||||
if (ids.indexOf(id) != -1) {
|
||||
const left = deck.layout[i - 1];
|
||||
if (left) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//state.deck.layout[i - 1] = state.deck.layout[i];
|
||||
//state.deck.layout[i] = left;
|
||||
deck.layout.splice(i - 1, 1, deck.layout[i]);
|
||||
deck.layout.splice(i, 1, left);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
swapRightDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
deck.layout.some((ids, i) => {
|
||||
if (ids.indexOf(id) != -1) {
|
||||
const right = deck.layout[i + 1];
|
||||
if (right) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//state.deck.layout[i + 1] = state.deck.layout[i];
|
||||
//state.deck.layout[i] = right;
|
||||
deck.layout.splice(i + 1, 1, deck.layout[i]);
|
||||
deck.layout.splice(i, 1, right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
swapUpDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const ids = deck.layout.find(ids => ids.indexOf(id) != -1);
|
||||
ids.some((x, i) => {
|
||||
if (x == id) {
|
||||
const up = ids[i - 1];
|
||||
if (up) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//ids[i - 1] = id;
|
||||
//ids[i] = up;
|
||||
ids.splice(i - 1, 1, id);
|
||||
ids.splice(i, 1, up);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
swapDownDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const ids = deck.layout.find(ids => ids.indexOf(id) != -1);
|
||||
ids.some((x, i) => {
|
||||
if (x == id) {
|
||||
const down = ids[i + 1];
|
||||
if (down) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//ids[i + 1] = id;
|
||||
//ids[i] = down;
|
||||
ids.splice(i + 1, 1, id);
|
||||
ids.splice(i, 1, down);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
stackLeftDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const i = deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
||||
deck.layout = deck.layout.map(ids => erase(id, ids));
|
||||
const left = deck.layout[i - 1];
|
||||
if (left) deck.layout[i - 1].push(id);
|
||||
deck.layout = deck.layout.filter(ids => ids.length > 0);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
popRightDeckColumn(state, id) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const i = deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
||||
deck.layout = deck.layout.map(ids => erase(id, ids));
|
||||
deck.layout.splice(i + 1, 0, [id]);
|
||||
deck.layout = deck.layout.filter(ids => ids.length > 0);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
addDeckWidget(state, x) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const column = deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column.widgets.unshift(x.widget);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
removeDeckWidget(state, x) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const column = deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column.widgets = column.widgets.filter(w => w.id != x.widget.id);
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
renameDeckColumn(state, x) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
const column = deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column.name = x.name;
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
},
|
||||
|
||||
updateDeckColumn(state, x) {
|
||||
const deck = state.settings.deckProfiles[state.device.deckProfile];
|
||||
let column = deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column = x;
|
||||
os.store.dispatch('settings/updateDeckProfile');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -158,176 +379,6 @@ export default (os: MiOS) => new Vuex.Store({
|
||||
setVisibility(state, visibility) {
|
||||
state.visibility = visibility;
|
||||
},
|
||||
|
||||
setHome(state, data) {
|
||||
state.home = data;
|
||||
},
|
||||
|
||||
addHomeWidget(state, widget) {
|
||||
state.home.unshift(widget);
|
||||
},
|
||||
|
||||
setMobileHome(state, data) {
|
||||
state.mobileHome = data;
|
||||
},
|
||||
|
||||
updateWidget(state, x) {
|
||||
let w;
|
||||
|
||||
//#region Desktop home
|
||||
if (state.home) {
|
||||
w = state.home.find(w => w.id == x.id);
|
||||
if (w) {
|
||||
w.data = x.data;
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Mobile home
|
||||
if (state.mobileHome) {
|
||||
w = state.mobileHome.find(w => w.id == x.id);
|
||||
if (w) {
|
||||
w.data = x.data;
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
},
|
||||
|
||||
addMobileHomeWidget(state, widget) {
|
||||
state.mobileHome.unshift(widget);
|
||||
},
|
||||
|
||||
removeMobileHomeWidget(state, widget) {
|
||||
state.mobileHome = state.mobileHome.filter(w => w.id != widget.id);
|
||||
},
|
||||
|
||||
addDeckColumn(state, column) {
|
||||
if (column.name == undefined) column.name = null;
|
||||
state.deck.columns.push(column);
|
||||
state.deck.layout.push([column.id]);
|
||||
},
|
||||
|
||||
removeDeckColumn(state, id) {
|
||||
state.deck.columns = state.deck.columns.filter(c => c.id != id);
|
||||
state.deck.layout = state.deck.layout.map(ids => erase(id, ids));
|
||||
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
||||
},
|
||||
|
||||
swapDeckColumn(state, x) {
|
||||
const a = x.a;
|
||||
const b = x.b;
|
||||
const aX = state.deck.layout.findIndex(ids => ids.indexOf(a) != -1);
|
||||
const aY = state.deck.layout[aX].findIndex(id => id == a);
|
||||
const bX = state.deck.layout.findIndex(ids => ids.indexOf(b) != -1);
|
||||
const bY = state.deck.layout[bX].findIndex(id => id == b);
|
||||
state.deck.layout[aX][aY] = b;
|
||||
state.deck.layout[bX][bY] = a;
|
||||
},
|
||||
|
||||
swapLeftDeckColumn(state, id) {
|
||||
state.deck.layout.some((ids, i) => {
|
||||
if (ids.indexOf(id) != -1) {
|
||||
const left = state.deck.layout[i - 1];
|
||||
if (left) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//state.deck.layout[i - 1] = state.deck.layout[i];
|
||||
//state.deck.layout[i] = left;
|
||||
state.deck.layout.splice(i - 1, 1, state.deck.layout[i]);
|
||||
state.deck.layout.splice(i, 1, left);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
swapRightDeckColumn(state, id) {
|
||||
state.deck.layout.some((ids, i) => {
|
||||
if (ids.indexOf(id) != -1) {
|
||||
const right = state.deck.layout[i + 1];
|
||||
if (right) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//state.deck.layout[i + 1] = state.deck.layout[i];
|
||||
//state.deck.layout[i] = right;
|
||||
state.deck.layout.splice(i + 1, 1, state.deck.layout[i]);
|
||||
state.deck.layout.splice(i, 1, right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
swapUpDeckColumn(state, id) {
|
||||
const ids = state.deck.layout.find(ids => ids.indexOf(id) != -1);
|
||||
ids.some((x, i) => {
|
||||
if (x == id) {
|
||||
const up = ids[i - 1];
|
||||
if (up) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//ids[i - 1] = id;
|
||||
//ids[i] = up;
|
||||
ids.splice(i - 1, 1, id);
|
||||
ids.splice(i, 1, up);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
swapDownDeckColumn(state, id) {
|
||||
const ids = state.deck.layout.find(ids => ids.indexOf(id) != -1);
|
||||
ids.some((x, i) => {
|
||||
if (x == id) {
|
||||
const down = ids[i + 1];
|
||||
if (down) {
|
||||
// https://vuejs.org/v2/guide/list.html#Caveats
|
||||
//ids[i + 1] = id;
|
||||
//ids[i] = down;
|
||||
ids.splice(i + 1, 1, id);
|
||||
ids.splice(i, 1, down);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stackLeftDeckColumn(state, id) {
|
||||
const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
||||
state.deck.layout = state.deck.layout.map(ids => erase(id, ids));
|
||||
const left = state.deck.layout[i - 1];
|
||||
if (left) state.deck.layout[i - 1].push(id);
|
||||
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
||||
},
|
||||
|
||||
popRightDeckColumn(state, id) {
|
||||
const i = state.deck.layout.findIndex(ids => ids.indexOf(id) != -1);
|
||||
state.deck.layout = state.deck.layout.map(ids => erase(id, ids));
|
||||
state.deck.layout.splice(i + 1, 0, [id]);
|
||||
state.deck.layout = state.deck.layout.filter(ids => ids.length > 0);
|
||||
},
|
||||
|
||||
addDeckWidget(state, x) {
|
||||
const column = state.deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column.widgets.unshift(x.widget);
|
||||
},
|
||||
|
||||
removeDeckWidget(state, x) {
|
||||
const column = state.deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column.widgets = column.widgets.filter(w => w.id != x.widget.id);
|
||||
},
|
||||
|
||||
renameDeckColumn(state, x) {
|
||||
const column = state.deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column.name = x.name;
|
||||
},
|
||||
|
||||
updateDeckColumn(state, x) {
|
||||
let column = state.deck.columns.find(c => c.id == x.id);
|
||||
if (column == null) return;
|
||||
column = x;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -360,6 +411,42 @@ export default (os: MiOS) => new Vuex.Store({
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
updateHomeProfile(ctx) {
|
||||
const profiles = ctx.state.homeProfiles;
|
||||
ctx.commit('set', {
|
||||
key: 'homeProfiles',
|
||||
value: profiles
|
||||
});
|
||||
os.api('i/update-client-setting', {
|
||||
name: 'homeProfiles',
|
||||
value: profiles
|
||||
});
|
||||
},
|
||||
|
||||
updateMobileHomeProfile(ctx) {
|
||||
const profiles = ctx.state.mobileHomeProfiles;
|
||||
ctx.commit('set', {
|
||||
key: 'mobileHomeProfiles',
|
||||
value: profiles
|
||||
});
|
||||
os.api('i/update-client-setting', {
|
||||
name: 'mobileHomeProfiles',
|
||||
value: profiles
|
||||
});
|
||||
},
|
||||
|
||||
updateDeckProfile(ctx) {
|
||||
const profiles = ctx.state.deckProfiles;
|
||||
ctx.commit('set', {
|
||||
key: 'deckProfiles',
|
||||
value: profiles
|
||||
});
|
||||
os.api('i/update-client-setting', {
|
||||
name: 'deckProfiles',
|
||||
value: profiles
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { URL } from 'url';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { Source, Mixin } from './types';
|
||||
import * as pkg from '../../package.json';
|
||||
|
@@ -14,6 +14,7 @@ export type Source = {
|
||||
db: string;
|
||||
user: string;
|
||||
pass: string;
|
||||
disableCache?: boolean;
|
||||
extra?: { [x: string]: string };
|
||||
};
|
||||
redis: {
|
||||
|
@@ -96,7 +96,7 @@ export function initDb(justBorrow = false, sync = false, log = false) {
|
||||
extra: config.db.extra,
|
||||
synchronize: process.env.NODE_ENV === 'test' || sync,
|
||||
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
|
||||
cache: {
|
||||
cache: !config.db.disableCache ? {
|
||||
type: 'redis',
|
||||
options: {
|
||||
host: config.redis.host,
|
||||
@@ -107,7 +107,7 @@ export function initDb(justBorrow = false, sync = false, log = false) {
|
||||
db: config.redis.db || 0
|
||||
}
|
||||
}
|
||||
},
|
||||
} : false,
|
||||
logging: log,
|
||||
logger: log ? new MyCustomLogger() : undefined,
|
||||
entities: [
|
||||
|
@@ -257,7 +257,7 @@ export default class Reversi {
|
||||
public get winner(): Color | null {
|
||||
return this.isEnded ?
|
||||
this.blackCount == this.whiteCount ? null :
|
||||
this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK :
|
||||
this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK :
|
||||
undefined as never;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
|
||||
import { URL } from 'url';
|
||||
import { urlRegex } from './prelude';
|
||||
|
||||
export function fromHtml(html: string): string {
|
||||
|
@@ -98,13 +98,13 @@ export const mfmLanguage = P.createLanguage({
|
||||
const text = input.substr(i);
|
||||
const match = text.match(/^(\*|_)([a-zA-Z0-9]+?[\s\S]*?)\1/);
|
||||
if (!match) return P.makeFailure(i, 'not a italic');
|
||||
if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a italic');
|
||||
if (input[i - 1] != null && input[i - 1] != ' ' && input[i - 1] != '\n') return P.makeFailure(i, 'not a italic');
|
||||
return P.makeSuccess(i + match[0].length, match[2]);
|
||||
});
|
||||
|
||||
return P.alt(xml, underscore).map(x => createTree('italic', r.inline.atLeast(1).tryParse(x), {}));
|
||||
},
|
||||
strike: r => P.regexp(/~~(.+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
|
||||
strike: r => P.regexp(/~~([^\n~]+?)~~/, 1).map(x => createTree('strike', r.inline.atLeast(1).tryParse(x), {})),
|
||||
motion: r => {
|
||||
const paren = P.regexp(/\(\(\(([\s\S]+?)\)\)\)/, 1);
|
||||
const xml = P.regexp(/<motion>(.+?)<\/motion>/, 1);
|
||||
@@ -157,15 +157,19 @@ export const mfmLanguage = P.createLanguage({
|
||||
let url: string;
|
||||
if (!match) {
|
||||
const match = text.match(/^<(https?:\/\/.*?)>/);
|
||||
if (!match)
|
||||
if (!match) {
|
||||
return P.makeFailure(i, 'not a url');
|
||||
}
|
||||
url = match[1];
|
||||
i += 2;
|
||||
} else
|
||||
} else {
|
||||
url = match[0];
|
||||
}
|
||||
url = removeOrphanedBrackets(url);
|
||||
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||
while (url.endsWith('.') || url.endsWith(',')) {
|
||||
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||
}
|
||||
return P.makeSuccess(i + url.length, url);
|
||||
}).map(x => createLeaf('url', { url: x }));
|
||||
},
|
||||
|
@@ -161,7 +161,7 @@ export class ASEvaluator {
|
||||
subtract: (a: number, b: number) => a - b,
|
||||
multiply: (a: number, b: number) => a * b,
|
||||
divide: (a: number, b: number) => a / b,
|
||||
remind: (a: number, b: number) => a % b,
|
||||
mod: (a: number, b: number) => a % b,
|
||||
strLen: (a: string) => a.length,
|
||||
strPick: (a: string, b: number) => a[b - 1],
|
||||
strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
|
||||
@@ -171,6 +171,7 @@ export class ASEvaluator {
|
||||
numberToString: (a: number) => a.toString(),
|
||||
splitStrByLine: (a: string) => a.split('\n'),
|
||||
pick: (list: any[], i: number) => list[i - 1],
|
||||
listLen: (list: any[]) => list.length,
|
||||
random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
|
||||
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
|
||||
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
|
||||
|
@@ -58,7 +58,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
|
||||
subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, },
|
||||
multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, },
|
||||
divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
remind: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, },
|
||||
notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, },
|
||||
gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, },
|
||||
@@ -74,6 +74,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
|
||||
numberToString: { in: ['number'], out: 'string', category: 'convert', icon: faExchangeAlt, },
|
||||
splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: faExchangeAlt, },
|
||||
pick: { in: [null, 'number'], out: null, category: 'list', icon: faIndent, },
|
||||
listLen: { in: [null], out: 'number', category: 'list', icon: faIndent, },
|
||||
rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
|
||||
dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
|
||||
seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: faDice, },
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import config from '../config';
|
||||
import { toASCII } from 'punycode';
|
||||
import { URL } from 'url';
|
||||
|
||||
export function getFullApAccount(username: string, host: string | null) {
|
||||
return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`;
|
||||
|
@@ -25,10 +25,8 @@ export async function downloadUrl(url: string, path: string) {
|
||||
rej(error);
|
||||
});
|
||||
|
||||
const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url;
|
||||
|
||||
const req = request({
|
||||
url: requestUrl,
|
||||
url: new URL(url).href, // https://github.com/syuilo/misskey/issues/2637
|
||||
proxy: config.proxy,
|
||||
timeout: 10 * 1000,
|
||||
headers: {
|
||||
|
File diff suppressed because one or more lines are too long
@@ -2,6 +2,8 @@ export function nyaize(text: string): string {
|
||||
return text
|
||||
// ja-JP
|
||||
.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ')
|
||||
// en-US
|
||||
.replace(/morning/gi, 'mornyan').replace(/everyone/gi, 'everynyan')
|
||||
// ko-KR
|
||||
.replace(/[나-낳]/g, match => String.fromCharCode(
|
||||
match.codePointAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export const twemojiBase = 'https://cdn.jsdelivr.net/npm/twemoji@11.3.0';
|
||||
// https://cdn.jsdelivr.net/npm/twemoji@11.3.0
|
||||
// https://cdnjs.cloudflare.com/ajax/libs/twemoji/11.3.0
|
||||
export const twemojiBase = 'https://cdn.jsdelivr.net/npm/twemoji@12.0.1';
|
||||
// https://cdn.jsdelivr.net/npm/twemoji@12.0.1
|
||||
// https://cdnjs.cloudflare.com/ajax/libs/twemoji/12.0.1
|
||||
// https://twemoji.maxcdn.com
|
||||
|
@@ -72,6 +72,7 @@ export class DriveFile {
|
||||
})
|
||||
public properties: Record<string, any>;
|
||||
|
||||
@Index()
|
||||
@Column('boolean')
|
||||
public storedInternal: boolean;
|
||||
|
||||
@@ -146,6 +147,7 @@ export class DriveFile {
|
||||
/**
|
||||
* 外部の(信頼されていない)URLへの直リンクか否か
|
||||
*/
|
||||
@Index()
|
||||
@Column('boolean', {
|
||||
default: false,
|
||||
comment: 'Whether the DriveFile is direct link to remote server.'
|
||||
|
@@ -8,7 +8,7 @@ export type PackedMessagingMessage = SchemaType<typeof packedMessagingMessageSch
|
||||
|
||||
@EntityRepository(MessagingMessage)
|
||||
export class MessagingMessageRepository extends Repository<MessagingMessage> {
|
||||
public isValidText(text: string): boolean {
|
||||
public validateText(text: string): boolean {
|
||||
return text.trim().length <= 1000 && text.trim() != '';
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import $ from 'cafy';
|
||||
import { EntityRepository, Repository, In } from 'typeorm';
|
||||
import { User, ILocalUser, IRemoteUser } from '../entities/user';
|
||||
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserGroupJoinings } from '..';
|
||||
@@ -231,29 +232,13 @@ export class UserRepository extends Repository<User> {
|
||||
}
|
||||
|
||||
//#region Validators
|
||||
public validateUsername(username: string, remote = false): boolean {
|
||||
return typeof username == 'string' && (remote ? /^\w([\w-]*\w)?$/ : /^\w{1,20}$/).test(username);
|
||||
}
|
||||
|
||||
public validatePassword(password: string): boolean {
|
||||
return typeof password == 'string' && password != '';
|
||||
}
|
||||
|
||||
public isValidName(name?: string): boolean {
|
||||
return name === null || (typeof name == 'string' && name.length < 50 && name.trim() != '');
|
||||
}
|
||||
|
||||
public isValidDescription(description: string): boolean {
|
||||
return typeof description == 'string' && description.length < 500 && description.trim() != '';
|
||||
}
|
||||
|
||||
public isValidLocation(location: string): boolean {
|
||||
return typeof location == 'string' && location.length < 50 && location.trim() != '';
|
||||
}
|
||||
|
||||
public isValidBirthday(birthday: string): boolean {
|
||||
return typeof birthday == 'string' && /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.test(birthday);
|
||||
}
|
||||
public validateLocalUsername = $.str.match(/^\w{1,20}$/);
|
||||
public validateRemoteUsername = $.str.match(/^\w([\w-]*\w)?$/);
|
||||
public validatePassword = $.str.min(1);
|
||||
public validateName = $.str.min(1).max(50);
|
||||
public validateDescription = $.str.min(1).max(500);
|
||||
public validateLocation = $.str.min(1).max(50);
|
||||
public validateBirthday = $.str.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/);
|
||||
//#endregion
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,6 @@ import * as httpSignature from 'http-signature';
|
||||
import { IRemoteUser } from '../../models/entities/user';
|
||||
import perform from '../../remote/activitypub/perform';
|
||||
import { resolvePerson, updatePerson } from '../../remote/activitypub/models/person';
|
||||
import { URL } from 'url';
|
||||
import { publishApLogStream } from '../../services/stream';
|
||||
import Logger from '../../services/logger';
|
||||
import { registerOrFetchInstanceDoc } from '../../services/register-or-fetch-instance-doc';
|
||||
|
@@ -15,6 +15,8 @@ import block from './block';
|
||||
import { apLogger } from '../logger';
|
||||
|
||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||
if (actor.isSuspended) return;
|
||||
|
||||
switch (activity.type) {
|
||||
case 'Create':
|
||||
await create(actor, activity);
|
||||
|
@@ -6,7 +6,6 @@ import { resolveImage } from './image';
|
||||
import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
|
||||
import { DriveFile } from '../../../models/entities/drive-file';
|
||||
import { fromHtml } from '../../../mfm/fromHtml';
|
||||
import { URL } from 'url';
|
||||
import { resolveNote, extractEmojis } from './note';
|
||||
import { registerOrFetchInstanceDoc } from '../../../services/register-or-fetch-instance-doc';
|
||||
import { ITag, extractHashtags } from './tag';
|
||||
@@ -53,12 +52,14 @@ function validatePerson(x: any, uri: string) {
|
||||
return new Error('invalid person: inbox is not a string');
|
||||
}
|
||||
|
||||
if (!Users.validateUsername(x.preferredUsername, true)) {
|
||||
if (!Users.validateRemoteUsername.ok(x.preferredUsername)) {
|
||||
return new Error('invalid person: invalid username');
|
||||
}
|
||||
|
||||
if (!Users.isValidName(x.name == '' ? null : x.name)) {
|
||||
return new Error('invalid person: invalid name');
|
||||
if (x.name != null && x.name != '') {
|
||||
if (!Users.validateName.ok(x.name)) {
|
||||
return new Error('invalid person: invalid name');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof x.id !== 'string') {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { request } from 'https';
|
||||
import { sign } from 'http-signature';
|
||||
import { URL } from 'url';
|
||||
import * as crypto from 'crypto';
|
||||
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
||||
import * as promiseAny from 'promise-any';
|
||||
@@ -9,7 +8,7 @@ import config from '../../config';
|
||||
import { ILocalUser } from '../../models/entities/user';
|
||||
import { publishApLogStream } from '../../services/stream';
|
||||
import { apLogger } from './logger';
|
||||
import { UserKeypairs } from '../../models';
|
||||
import { UserKeypairs, Instances } from '../../models';
|
||||
import { fetchMeta } from '../../misc/fetch-meta';
|
||||
import { toPuny } from '../../misc/convert-host';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
@@ -17,15 +16,30 @@ import { ensure } from '../../prelude/ensure';
|
||||
export const logger = apLogger.createSubLogger('deliver');
|
||||
|
||||
export default async (user: ILocalUser, url: string, object: any) => {
|
||||
logger.info(`--> ${url}`);
|
||||
|
||||
const timeout = 10 * 1000;
|
||||
|
||||
const { protocol, host, hostname, port, pathname, search } = new URL(url);
|
||||
|
||||
// ブロックしてたら中断
|
||||
const meta = await fetchMeta();
|
||||
if (meta.blockedHosts.includes(toPuny(host))) return;
|
||||
if (meta.blockedHosts.includes(toPuny(host))) {
|
||||
logger.info(`skip (blocked) ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// closedなら中断
|
||||
const closedHosts = await Instances.find({
|
||||
where: {
|
||||
isMarkedAsClosed: true
|
||||
},
|
||||
cache: 60 * 1000
|
||||
});
|
||||
if (closedHosts.map(x => x.host).includes(toPuny(host))) {
|
||||
logger.info(`skip (closed) ${url}`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`--> ${url}`);
|
||||
|
||||
const data = JSON.stringify(object);
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import webFinger from './webfinger';
|
||||
import config from '../config';
|
||||
import { createPerson, updatePerson } from './activitypub/models/person';
|
||||
import { URL } from 'url';
|
||||
import { remoteLogger } from './logger';
|
||||
import chalk from 'chalk';
|
||||
import { User, IRemoteUser } from '../models/entities/user';
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import config from '../config';
|
||||
import * as request from 'request-promise-native';
|
||||
import { URL } from 'url';
|
||||
import { query as urlQuery } from '../prelude/url';
|
||||
|
||||
type ILink = {
|
||||
|
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
37
src/server/api/endpoints/admin/get-table-stats.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import define from '../../define';
|
||||
import { getConnection } from 'typeorm';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: false,
|
||||
|
||||
desc: {
|
||||
'en-US': 'Get table stats'
|
||||
},
|
||||
|
||||
tags: ['meta'],
|
||||
|
||||
params: {
|
||||
},
|
||||
};
|
||||
|
||||
export default define(meta, async () => {
|
||||
const sizes = await
|
||||
getConnection().query(`
|
||||
SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size"
|
||||
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
|
||||
AND C.relkind <> 'i'
|
||||
AND nspname !~ '^pg_toast';`)
|
||||
.then(recs => {
|
||||
const res = {} as Record<string, { count: number; size: number; }>;
|
||||
for (const rec of recs) {
|
||||
res[rec.table] = {
|
||||
count: parseInt(rec.count, 10),
|
||||
size: parseInt(rec.size, 10),
|
||||
};
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
return sizes;
|
||||
});
|
@@ -2,6 +2,7 @@ import $ from 'cafy';
|
||||
import { publishMainStream } from '../../../../services/stream';
|
||||
import define from '../../define';
|
||||
import { UserProfiles } from '../../../../models';
|
||||
import { ensure } from '../../../../prelude/ensure';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@@ -20,11 +21,13 @@ export const meta = {
|
||||
};
|
||||
|
||||
export default define(meta, async (ps, user) => {
|
||||
const profile = await UserProfiles.findOne(user.id).then(ensure);
|
||||
|
||||
await UserProfiles.createQueryBuilder().update()
|
||||
.set({
|
||||
clientData: {
|
||||
clientData: Object.assign(profile.clientData, {
|
||||
[ps.name]: ps.value
|
||||
},
|
||||
}),
|
||||
})
|
||||
.where('userId = :id', { id: user.id })
|
||||
.execute();
|
||||
|
@@ -8,6 +8,7 @@ import * as bcrypt from 'bcryptjs';
|
||||
import { Users, UserProfiles } from '../../../../models';
|
||||
import { ensure } from '../../../../prelude/ensure';
|
||||
import { sendEmail } from '../../../../services/send-email';
|
||||
import { ApiError } from '../../error';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
@@ -27,6 +28,14 @@ export const meta = {
|
||||
email: {
|
||||
validator: $.optional.nullable.str
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
incorrectPassword: {
|
||||
message: 'Incorrect password.',
|
||||
code: 'INCORRECT_PASSWORD',
|
||||
id: 'e54c1d7e-e7d6-4103-86b6-0a95069b4ad3'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,7 +46,7 @@ export default define(meta, async (ps, user) => {
|
||||
const same = await bcrypt.compare(ps.password, profile.password!);
|
||||
|
||||
if (!same) {
|
||||
throw new Error('incorrect password');
|
||||
throw new ApiError(meta.errors.incorrectPassword);
|
||||
}
|
||||
|
||||
await UserProfiles.update({ userId: user.id }, {
|
||||
|
@@ -29,14 +29,14 @@ export const meta = {
|
||||
|
||||
params: {
|
||||
name: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidName),
|
||||
validator: $.optional.nullable.use(Users.validateName),
|
||||
desc: {
|
||||
'ja-JP': '名前(ハンドルネームやニックネーム)'
|
||||
}
|
||||
},
|
||||
|
||||
description: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidDescription),
|
||||
validator: $.optional.nullable.use(Users.validateDescription),
|
||||
desc: {
|
||||
'ja-JP': 'アカウントの説明や自己紹介'
|
||||
}
|
||||
@@ -50,14 +50,14 @@ export const meta = {
|
||||
},
|
||||
|
||||
location: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidLocation),
|
||||
validator: $.optional.nullable.use(Users.validateLocation),
|
||||
desc: {
|
||||
'ja-JP': '住んでいる地域、所在'
|
||||
}
|
||||
},
|
||||
|
||||
birthday: {
|
||||
validator: $.optional.nullable.str.pipe(Users.isValidBirthday),
|
||||
validator: $.optional.nullable.use(Users.validateBirthday),
|
||||
desc: {
|
||||
'ja-JP': '誕生日 (YYYY-MM-DD形式)'
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ export const meta = {
|
||||
},
|
||||
|
||||
text: {
|
||||
validator: $.optional.str.pipe(MessagingMessages.isValidText)
|
||||
validator: $.optional.str.pipe(MessagingMessages.validateText)
|
||||
},
|
||||
|
||||
fileId: {
|
||||
|
@@ -6,6 +6,8 @@ import { fetchMeta } from '../../../misc/fetch-meta';
|
||||
import * as pkg from '../../../../package.json';
|
||||
import { Emojis } from '../../../models';
|
||||
import { types, bool } from '../../../misc/schema';
|
||||
import { getConnection } from 'typeorm';
|
||||
import redis from '../../../db/redis';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
@@ -114,6 +116,8 @@ export default define(meta, async (ps, me) => {
|
||||
machine: os.hostname(),
|
||||
os: os.platform(),
|
||||
node: process.version,
|
||||
psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version),
|
||||
redis: redis.server_info.redis_version,
|
||||
|
||||
cpu: {
|
||||
model: os.cpus()[0].model,
|
||||
|
@@ -9,7 +9,7 @@ export const meta = {
|
||||
|
||||
params: {
|
||||
username: {
|
||||
validator: $.str.pipe(Users.validateUsername)
|
||||
validator: $.use(Users.validateLocalUsername)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -66,7 +66,7 @@ export const meta = {
|
||||
};
|
||||
|
||||
export default define(meta, async (ps, me) => {
|
||||
const isUsername = Users.validateUsername(ps.query.replace('@', ''), !ps.localOnly);
|
||||
const isUsername = ps.localOnly ? Users.validateLocalUsername.ok(ps.query.replace('@', '')) : Users.validateRemoteUsername.ok(ps.query.replace('@', ''));
|
||||
|
||||
let users: User[] = [];
|
||||
|
||||
|
@@ -58,13 +58,13 @@ export default async (ctx: Koa.BaseContext) => {
|
||||
}
|
||||
|
||||
// Validate username
|
||||
if (!Users.validateUsername(username)) {
|
||||
if (!Users.validateLocalUsername.ok(username)) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate password
|
||||
if (!Users.validatePassword(password)) {
|
||||
if (!Users.validatePassword.ok(password)) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@ export async function proxyMedia(ctx: Koa.BaseContext) {
|
||||
|
||||
const [type, ext] = await detectMine(path);
|
||||
|
||||
if (!type.startsWith('image/')) throw 403;
|
||||
|
||||
let image: IImage;
|
||||
|
||||
if ('static' in ctx.query && ['image/png', 'image/gif'].includes(type)) {
|
||||
|
@@ -20,6 +20,8 @@ import { Users, Notes, Emojis, UserProfiles, Pages } from '../../models';
|
||||
import parseAcct from '../../misc/acct/parse';
|
||||
import getNoteSummary from '../../misc/get-note-summary';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
import { getConnection } from 'typeorm';
|
||||
import redis from '../../db/redis';
|
||||
|
||||
const client = `${__dirname}/../../client/`;
|
||||
|
||||
@@ -250,6 +252,8 @@ router.get('/info', async ctx => {
|
||||
machine: os.hostname(),
|
||||
os: os.platform(),
|
||||
node: process.version,
|
||||
psql: await getConnection().query('SHOW server_version').then(x => x[0].server_version),
|
||||
redis: redis.server_info.redis_version,
|
||||
cpu: {
|
||||
model: os.cpus()[0].model,
|
||||
cores: os.cpus().length
|
||||
|
@@ -12,18 +12,20 @@ module.exports = async (ctx: Koa.BaseContext) => {
|
||||
const meta = await fetchMeta();
|
||||
|
||||
logger.info(meta.summalyProxy
|
||||
? `(Proxy) Getting preview of ${ctx.query.url} ...`
|
||||
: `Getting preview of ${ctx.query.url} ...`);
|
||||
? `(Proxy) Getting preview of ${ctx.query.url}@${ctx.query.lang} ...`
|
||||
: `Getting preview of ${ctx.query.url}@${ctx.query.lang} ...`);
|
||||
|
||||
try {
|
||||
const summary = meta.summalyProxy ? await request.get({
|
||||
url: meta.summalyProxy,
|
||||
qs: {
|
||||
url: ctx.query.url
|
||||
url: ctx.query.url,
|
||||
lang: ctx.query.lang || 'ja-JP'
|
||||
},
|
||||
json: true
|
||||
}) : await summaly(ctx.query.url, {
|
||||
followRedirects: false
|
||||
followRedirects: false,
|
||||
lang: ctx.query.lang || 'ja-JP'
|
||||
});
|
||||
|
||||
logger.succ(`Got preview of ${ctx.query.url}: ${summary.title}`);
|
||||
@@ -36,7 +38,7 @@ module.exports = async (ctx: Koa.BaseContext) => {
|
||||
|
||||
ctx.body = summary;
|
||||
} catch (e) {
|
||||
logger.error(`Failed to get preview of ${ctx.query.url}: ${e}`);
|
||||
logger.warn(`Failed to get preview of ${ctx.query.url}: ${e}`);
|
||||
ctx.status = 200;
|
||||
ctx.set('Cache-Control', 'max-age=86400, immutable');
|
||||
ctx.body = '{}';
|
||||
|
@@ -85,6 +85,12 @@ html
|
||||
tr
|
||||
th Node version
|
||||
td= node
|
||||
tr
|
||||
th PSQL version
|
||||
td= psql
|
||||
tr
|
||||
th Redis version
|
||||
td= redis
|
||||
tr
|
||||
th Machine
|
||||
td= machine
|
||||
|
@@ -163,6 +163,19 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||
},
|
||||
...Chart.convertSchemaToFlatColumnDefinitions(schema)
|
||||
},
|
||||
indices: [{
|
||||
columns: ['date']
|
||||
}, {
|
||||
columns: ['span']
|
||||
}, {
|
||||
columns: ['group']
|
||||
}, {
|
||||
columns: ['span', 'date']
|
||||
}, {
|
||||
columns: ['date', 'group']
|
||||
}, {
|
||||
columns: ['span', 'date', 'group']
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -149,18 +149,22 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||
if (generateWeb) {
|
||||
logger.info(`creating web image`);
|
||||
|
||||
if (['image/jpeg'].includes(type)) {
|
||||
webpublic = await convertToJpeg(path, 2048, 2048);
|
||||
} else if (['image/webp'].includes(type)) {
|
||||
webpublic = await convertToWebp(path, 2048, 2048);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
webpublic = await convertToPng(path, 2048, 2048);
|
||||
} else if (['image/apng', 'image/vnd.mozilla.apng'].includes(type)) {
|
||||
webpublic = await convertToApng(path);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
webpublic = await convertToGif(path);
|
||||
} else {
|
||||
logger.info(`web image not created (not an image)`);
|
||||
try {
|
||||
if (['image/jpeg'].includes(type)) {
|
||||
webpublic = await convertToJpeg(path, 2048, 2048);
|
||||
} else if (['image/webp'].includes(type)) {
|
||||
webpublic = await convertToWebp(path, 2048, 2048);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
webpublic = await convertToPng(path, 2048, 2048);
|
||||
} else if (['image/apng', 'image/vnd.mozilla.apng'].includes(type)) {
|
||||
webpublic = await convertToApng(path);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
webpublic = await convertToGif(path);
|
||||
} else {
|
||||
logger.info(`web image not created (not an image)`);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn(`web image not created (an error occured)`, e);
|
||||
}
|
||||
} else {
|
||||
logger.info(`web image not created (from remote)`);
|
||||
@@ -170,18 +174,22 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
|
||||
// #region thumbnail
|
||||
let thumbnail: IImage | null = null;
|
||||
|
||||
if (['image/jpeg', 'image/webp'].includes(type)) {
|
||||
thumbnail = await convertToJpeg(path, 498, 280);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
thumbnail = await convertToPng(path, 498, 280);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
thumbnail = await convertToGif(path);
|
||||
} else if (type.startsWith('video/')) {
|
||||
try {
|
||||
thumbnail = await GenerateVideoThumbnail(path);
|
||||
} catch (e) {
|
||||
logger.error(`GenerateVideoThumbnail failed: ${e}`);
|
||||
try {
|
||||
if (['image/jpeg', 'image/webp'].includes(type)) {
|
||||
thumbnail = await convertToJpeg(path, 498, 280);
|
||||
} else if (['image/png'].includes(type)) {
|
||||
thumbnail = await convertToPng(path, 498, 280);
|
||||
} else if (['image/gif'].includes(type)) {
|
||||
thumbnail = await convertToGif(path);
|
||||
} else if (type.startsWith('video/')) {
|
||||
try {
|
||||
thumbnail = await GenerateVideoThumbnail(path);
|
||||
} catch (e) {
|
||||
logger.error(`GenerateVideoThumbnail failed: ${e}`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn(`thumbnail not created (an error occured)`, e);
|
||||
}
|
||||
// #endregion thumbnail
|
||||
|
||||
|
@@ -22,7 +22,7 @@ export default async function(follower: User, followee: User, silent = false) {
|
||||
return;
|
||||
}
|
||||
|
||||
Followings.delete(following.id);
|
||||
await Followings.delete(following.id);
|
||||
|
||||
//#region Decrement following count
|
||||
Users.decrement({ id: follower.id }, 'followingCount', 1);
|
||||
|
@@ -90,7 +90,7 @@ export default class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
public warn(message: string, data?: Record<string, any> | null, important = false): void { // 実行を継続できるが改善すべき状況で使う
|
||||
public warn(message: string, data?: Record<string, any> | null, important = false): void { // 実行を継続できるが改善すべき状況で使う
|
||||
this.log('warning', message, data, important);
|
||||
}
|
||||
|
||||
|
34
test/mfm.ts
34
test/mfm.ts
@@ -804,6 +804,14 @@ describe('MFM', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignore trailing periods', () => {
|
||||
const tokens = parse('https://example.com...');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
leaf('url', { url: 'https://example.com' }),
|
||||
text('...')
|
||||
]);
|
||||
});
|
||||
|
||||
it('with comma', () => {
|
||||
const tokens = parse('https://example.com/foo?bar=a,b');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
@@ -1116,6 +1124,14 @@ describe('MFM', () => {
|
||||
], {}),
|
||||
]);
|
||||
});
|
||||
|
||||
// https://misskey.io/notes/7u1kv5dmia
|
||||
it('ignore internal tilde', () => {
|
||||
const tokens = parse('~~~~~');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
text('~~~~~')
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic', () => {
|
||||
@@ -1173,6 +1189,24 @@ describe('MFM', () => {
|
||||
text('foo_bar_baz'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('require spaces', () => {
|
||||
const tokens = parse('4日目_L38b a_b');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
text('4日目_L38b a_b'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('newline sandwich', () => {
|
||||
const tokens = parse('foo\n_bar_\nbaz');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
text('foo\n'),
|
||||
tree('italic', [
|
||||
text('bar')
|
||||
], {}),
|
||||
text('\nbaz'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user