Compare commits

...

289 Commits

Author SHA1 Message Date
44d7652171 12.14.0 2020-02-18 21:35:16 +09:00
c9ed15b682 add missing image (#5967)
fix for explore banner
2020-02-18 21:33:51 +09:00
8faad646ae New Crowdin translations (#5971)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Kannada)
2020-02-18 21:33:38 +09:00
1d50bc3382 Fix bug 2020-02-18 21:27:47 +09:00
da4af041af ログビューア実装 2020-02-18 21:27:43 +09:00
e2ff408f2f Implement object storage settings 2020-02-18 21:12:05 +09:00
50d1500dfc 12.13.0 2020-02-18 19:50:04 +09:00
94441f93a5 New Crowdin translations (#5969)
* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-02-18 19:48:14 +09:00
5f712fbf3c Implement photo widget 2020-02-18 19:47:30 +09:00
1c757f10e0 Update CHANGELOG.md 2020-02-18 19:36:20 +09:00
0508d5f643 Add activity widget 2020-02-18 19:31:11 +09:00
d9986b7a2f Implement featured note injection 2020-02-18 19:05:11 +09:00
3d79e7a136 Improve paging 2020-02-18 18:19:11 +09:00
52fb1237ec Imprement promo read 2020-02-18 18:14:38 +09:00
8a7197726e Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-18 17:54:13 +09:00
b7f5458684 Fix bug 2020-02-18 17:53:56 +09:00
52710f3810 管理者はモデレーターに変更できないように (#5970)
* 管理者をモデレーターに変更できないように

* Change error message
2020-02-18 17:53:52 +09:00
a54de07260 Resolve #5963 2020-02-18 08:41:32 +09:00
aa2c8d101e Fix type 2020-02-18 08:13:47 +09:00
1441fd93b9 Clean up 2020-02-18 08:05:27 +09:00
4a585e8920 Improve chart logging 2020-02-18 03:03:34 +09:00
8c4245a09d Update core.ts 2020-02-18 02:27:18 +09:00
e4af16989a Fix bug 2020-02-18 01:25:02 +09:00
5dc0944fe8 Resolve #5949 2020-02-18 01:12:35 +09:00
b4d24f4377 12.12.0 2020-02-17 07:24:16 +09:00
67be47b8db New Crowdin translations (#5961)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)
2020-02-17 07:23:58 +09:00
e382982d32 Fix bug 2020-02-17 07:23:39 +09:00
b09b74b5da 🎨 2020-02-17 07:14:03 +09:00
c628bdb7a6 Fix glitch 2020-02-17 06:48:04 +09:00
2fcf6fb0fd UI tweak 2020-02-17 06:43:52 +09:00
4f3fc9ffd0 🎨 2020-02-17 06:39:41 +09:00
15839a7399 🎨 2020-02-17 06:37:39 +09:00
26b3a14a63 Clean up 2020-02-17 06:23:18 +09:00
f2f0799df1 Update app.vue 2020-02-17 05:38:00 +09:00
6c99c32100 i18n 2020-02-17 03:19:27 +09:00
93d25a2a34 ユーザー設定とクライアント設定を分離 2020-02-17 03:10:51 +09:00
88f5ec59d7 🎨 2020-02-17 02:41:03 +09:00
586d3c4db7 Better instance page 2020-02-17 02:27:14 +09:00
f45fb56e15 Improve instance info page 2020-02-17 02:21:27 +09:00
8fe153c7c1 12.11.0 2020-02-16 22:53:35 +09:00
36a8720fbb New Crowdin translations (#5948)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)
2020-02-16 22:53:16 +09:00
9cbfdc94d9 Clean up 2020-02-16 22:46:51 +09:00
091923764d Implement image dialog 2020-02-16 22:46:18 +09:00
dc39caed1e Resolve #5942 2020-02-16 22:15:49 +09:00
bcd7d1f007 Update CHANGELOG.md 2020-02-16 21:11:44 +09:00
40d4dc0474 Refactor 2020-02-16 21:11:27 +09:00
02ac30c0d0 Resolve #5958 2020-02-16 21:10:52 +09:00
518bc92673 Clean up 2020-02-16 21:05:17 +09:00
a5b92e316c Refactor: Extract scroll utility functions 2020-02-16 20:58:41 +09:00
828c7b66a0 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-16 20:54:33 +09:00
93474eaa06 2回目以降の読み込みは30個までフェッチするように 2020-02-16 20:54:25 +09:00
237f366aa2 Update notification (#5956)
* Add icon for messaging

This will add icon within messaging

* Update messaging-room.message.vue

Link to missing icon

* Update notification.vue

fix renote icon in notification

https://github.com/syuilo/misskey/issues/5955
2020-02-16 20:21:05 +09:00
714bcf28d5 テーマの切り替え時に時計の色が変わるように (#5959)
* テーマの切り替え時に時計の色が変わるように

* ディレイを追加
2020-02-16 20:20:02 +09:00
420eeb4d68 Clean up 2020-02-15 23:42:52 +09:00
bc6daf4a2e Update CHANGELOG.md 2020-02-15 23:16:50 +09:00
6f7832c09b API doc 2020-02-15 23:13:59 +09:00
bef67fa275 Update trend.ts 2020-02-15 23:01:41 +09:00
05d7198667 🎨 2020-02-15 22:51:46 +09:00
df0bfc14e5 🎨 2020-02-15 22:21:35 +09:00
3f28f7451f Prefetch aicons 2020-02-15 21:56:21 +09:00
dbb9199d6f Fix widget bg (#5952) 2020-02-15 21:42:45 +09:00
72cb3b03af Update CHANGELOG.md 2020-02-15 21:39:59 +09:00
d0085f00ed Fix #5950 2020-02-15 21:39:38 +09:00
43734f027b Refactoring 2020-02-15 21:33:32 +09:00
bb903cab40 🎨 2020-02-15 18:47:50 +09:00
92f765bc47 Update sequential-entrance.vue 2020-02-15 18:42:43 +09:00
742889a035 Update sequential-entrance.vue 2020-02-15 18:39:45 +09:00
24453ebcc3 Improve banner animation performance 2020-02-15 17:44:26 +09:00
8b8ab1bf5c Update CHANGELOG.md 2020-02-15 17:33:51 +09:00
e9bc9b8675 Fix bug 2020-02-15 17:31:45 +09:00
eeaa27c7ca Improve usability 2020-02-15 09:22:16 +09:00
ccea1755fc なんか 2020-02-15 09:10:49 +09:00
c32a5d602b 🎨 2020-02-15 08:52:21 +09:00
2a04f2ca4d Improve follow-requests page 2020-02-15 08:42:21 +09:00
37c80e8ef5 Improve wallpaper feature 2020-02-15 08:29:59 +09:00
1dce62e42a 🎨 2020-02-15 07:54:20 +09:00
ec222378c4 12.10.0 2020-02-15 04:32:48 +09:00
ac930a1c6a New Crowdin translations (#5947)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-02-15 04:30:54 +09:00
5ccd5ad56e Add driveFiles index 2020-02-15 04:29:57 +09:00
67da90530b Use em 2020-02-15 04:02:32 +09:00
b7f3753615 Use em 2020-02-15 03:57:43 +09:00
d21d38509c Font size setting 2020-02-15 03:44:40 +09:00
a59e1c0345 Use em 2020-02-15 03:25:54 +09:00
7ab613b394 Use em 2020-02-15 03:24:09 +09:00
c00ab0fbe2 Api key setting 2020-02-15 03:18:33 +09:00
87451b1223 Add header clock 2020-02-15 02:54:42 +09:00
d2b61229a3 Improve usability 2020-02-15 02:39:27 +09:00
980584020a 🎨 2020-02-15 02:26:45 +09:00
a43d0dafa5 🎨 2020-02-15 02:20:10 +09:00
d5c1e7e579 Resolve #5928 2020-02-15 02:16:11 +09:00
55bdf0d618 🎨 2020-02-15 01:40:38 +09:00
44f7c13ad4 ✌️ 2020-02-15 01:39:14 +09:00
7bd1a3c8ac Update CHANGELOG.md 2020-02-15 01:33:58 +09:00
4f1981df03 サイドバーメニューからアカウントを作成できるように (#5910)
* メニューからアカウントを作成できるようにした

* i18n

* Update signup.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-02-15 01:33:08 +09:00
8689a998aa New Crowdin translations (#5938)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)
2020-02-15 01:19:53 +09:00
079bb8d722 🎨 2020-02-15 01:18:48 +09:00
65c0b6c7da Resolve #5939 2020-02-15 01:03:59 +09:00
84958af4ce Improve doc page 2020-02-15 00:27:53 +09:00
c53b59914b doc 2020-02-15 00:17:21 +09:00
8ffd9ab2d9 Create reaction.ja-JP.md 2020-02-15 00:11:01 +09:00
0305caf504 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-15 00:02:10 +09:00
3012e4ffe0 Update CHANGELOG.md 2020-02-15 00:00:53 +09:00
585f3c3d3e Fix Oops! (#5945) 2020-02-14 23:59:54 +09:00
f1d07dfbed Fix favicon provide 2020-02-14 23:55:31 +09:00
cddfc55110 ✌️ 2020-02-14 23:55:13 +09:00
a2ce072ae7 アナログ時計ウィジェットを移植 (#5944)
* アナログ時計ウィジェットを移植

* analog-clock -> clock
2020-02-14 23:31:24 +09:00
439563c5d6 サムネイルをJPEGで生成するように (#5941) 2020-02-14 11:40:45 +09:00
962617b4f4 Update CHANGELOG.md 2020-02-14 01:28:15 +09:00
4a202f0f7e 12.9.0 2020-02-14 01:26:28 +09:00
6e6b12519a Refactor 2020-02-14 01:24:05 +09:00
f5f7654f4b Improve custom emoji managemant 2020-02-14 01:09:39 +09:00
5ac4c48ad1 動きのあるMFMを無効にするオプション 2020-02-13 23:20:12 +09:00
7e18fd18b0 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-13 23:11:14 +09:00
fb30c479ea Update CHANGELOG.md 2020-02-13 23:11:11 +09:00
DW
f40dcbfe13 Fix for CASCADE DELETE not being federated (#5812)
* Fix for CASCADE DELETE not being federated

* Use JOIN to get user

* fix typo
2020-02-13 23:08:33 +09:00
8755b5f353 Fix bug 2020-02-13 22:48:56 +09:00
691482bb28 Update dependencies 🚀 2020-02-13 22:39:23 +09:00
4248bb8ce0 New Crowdin translations (#5931)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)
2020-02-13 22:35:22 +09:00
a5653e33d3 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-13 22:13:35 +09:00
072dc1c7e6 Clean up 2020-02-13 22:13:31 +09:00
d76fceae85 Fix #5935 2020-02-13 22:13:24 +09:00
86107b2710 Fix For Messaging Icon (#5933)
* Add icon for messaging

This will add icon within messaging

* Update messaging-room.message.vue

Link to missing icon
2020-02-13 15:53:02 +09:00
a473768bef 🎨 2020-02-13 11:54:12 +09:00
f7fe13a177 i18n 2020-02-13 11:46:02 +09:00
acd29d22eb Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-13 11:45:02 +09:00
c228155514 Fix #5930 2020-02-13 11:44:58 +09:00
b601b98d5c New Crowdin translations (#5929)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)
2020-02-13 06:02:36 +09:00
1cd3419688 Refactor 2020-02-13 05:08:01 +09:00
6f5f233bb5 12.8.0 2020-02-13 03:13:30 +09:00
d33492cd49 New Crowdin translations (#5926)
* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)
2020-02-13 03:12:58 +09:00
83ad9f369f OSネイティブの絵文字を使用オプション 2020-02-13 03:11:37 +09:00
3a78b62520 Update about-misskey.vue 2020-02-13 03:01:39 +09:00
2479f75d8a インスタンス情報ページとMisskey情報ページを分離するなど 2020-02-13 02:48:52 +09:00
f0d187f71e Add crowdin link 2020-02-13 02:35:50 +09:00
056942391a Update CHANGELOG.md 2020-02-13 02:18:33 +09:00
2feef81516 グループ招待の通知とか
Resolve #5880
Resolve #5927
2020-02-13 02:17:54 +09:00
037d4b581b フランス語と関西弁を有効に (#5925) 2020-02-12 22:40:35 +09:00
da4cf6fdb4 New Crowdin translations (#5924)
* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)
2020-02-12 22:40:18 +09:00
848bcd5a63 Update CHANGELOG.md 2020-02-12 08:05:26 +09:00
7b60b6c6dc New Crowdin translations (#5921)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Kannada)
2020-02-12 08:00:07 +09:00
fbc801d1da 言語切り替え 2020-02-12 07:12:58 +09:00
2f18f82e3d 🎨 2020-02-12 06:43:22 +09:00
c2d5a96bb6 翻訳の抜けを修正その2 (#5893)
* Missing translation

* use npx

* ✌️

* Update ja-JP.yml

* Update signup.vue

* Update ja-JP.yml

* Update messaging-room.vue

* Update ja-JP.yml

* Update signup.vue

* Update ja-JP.yml

* Update signin.vue

* Update ja-JP.yml

* Update index.vue

* Update ja-JP.yml

* Update signup.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-02-12 06:27:11 +09:00
46575d4f04 Fix #5919 2020-02-12 06:19:31 +09:00
5e7be93980 Resolve #5907 2020-02-12 06:01:59 +09:00
3f749c6540 良い感じに 2020-02-12 05:21:08 +09:00
2a7d4ee866 Update sequential-entrance.vue 2020-02-12 03:31:46 +09:00
083095ded6 Update sequential-entrance.vue 2020-02-12 03:31:20 +09:00
7207663f37 🎨 2020-02-12 02:56:42 +09:00
66b49c909a 🎨 2020-02-12 02:55:35 +09:00
ab469aa243 Update CHANGELOG.md 2020-02-12 02:52:45 +09:00
afa4563e1e Update paging.ts 2020-02-12 02:52:43 +09:00
320b3d8617 🎨 2020-02-12 02:52:37 +09:00
7493429b4d 🎨 2020-02-12 02:35:03 +09:00
d70f7a717b Fix #5918 2020-02-12 01:01:17 +09:00
4ab38b7894 タイムラインを放置すると先頭の投稿が見えなくなるのを修正 Fix #5903 (#5913)
* seqent fix

* comment

* ✌️
2020-02-12 00:38:29 +09:00
2068407be0 Update app.vue 2020-02-12 00:31:48 +09:00
10a7369fec サーバーから切断されましたのダイアログは時間をおいて表示するように (#5916)
* timeout disconnect dialog

* 70ms

* 150ms
2020-02-11 22:57:09 +09:00
9fb5579701 12.7.1 2020-02-11 20:08:47 +09:00
b3cf883a44 New Crowdin translations (#5908)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)
2020-02-11 20:08:11 +09:00
f65e27c07c Fix #5914 2020-02-11 20:05:47 +09:00
b587fefe44 12.7.0 2020-02-10 23:45:31 +09:00
2511114c28 なんかもうめっちゃ変えた
Resolve #5846
2020-02-10 23:17:42 +09:00
9cd267fee5 🎨 2020-02-10 21:41:35 +09:00
aeac96a4f7 New Crowdin translations (#5901)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)
2020-02-10 21:40:30 +09:00
88a75e2a99 🎨 2020-02-10 21:37:37 +09:00
e14760775b 🎨 2020-02-10 21:30:59 +09:00
9e7e464ba5 Clean up 2020-02-10 21:28:29 +09:00
060d4fd27f 🎨 2020-02-10 21:27:17 +09:00
940578d062 🎨 2020-02-10 21:16:04 +09:00
9cf42d8b33 Better self link detection 2020-02-10 20:51:17 +09:00
1d62d2924e 🎨 2020-02-10 20:47:02 +09:00
e23bac47ba 🎨 2020-02-10 20:44:59 +09:00
b5d38adfcc 🎨 2020-02-10 20:32:57 +09:00
f30513b20b Fix #5902 2020-02-10 20:29:44 +09:00
ade1e40395 12.6.0 2020-02-10 07:25:49 +09:00
c93b8677e4 New Crowdin translations (#5899)
* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-02-10 07:25:32 +09:00
62683d8878 なんかもうめっちゃ変えた
Resolve #5900
2020-02-10 07:23:43 +09:00
3d1239c1b4 Better initial widgets 2020-02-10 05:44:47 +09:00
5268bade66 Better widgets 2020-02-10 05:42:03 +09:00
514eb39a14 ユーザーページからグループに招待できるように 2020-02-10 05:03:01 +09:00
18628b821e Better title adjust logic 2020-02-10 04:04:10 +09:00
344fbe6bcd Note page title 2020-02-10 03:55:33 +09:00
afb8cd2dc1 Update CHANGELOG.md 2020-02-10 03:51:58 +09:00
1a5f385eb5 Improve mfm link 2020-02-10 03:48:45 +09:00
8df7864064 Clean up 2020-02-10 03:16:34 +09:00
9ca60bad7f Update url-preview-popup.vue 2020-02-10 03:13:24 +09:00
bd828bb072 Better resize observe 2020-02-10 03:13:22 +09:00
892cb44d84 Resolve #3644 2020-02-10 02:59:00 +09:00
517ea6a119 Refactor 2020-02-10 02:42:06 +09:00
ba8ffda32a New Crowdin translations (#5884)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)
2020-02-09 22:47:54 +09:00
90a9cf376e 12.5.0 2020-02-09 22:44:07 +09:00
16d6c55407 🎨 2020-02-09 22:25:36 +09:00
f3508d15a3 Refactor 2020-02-09 22:25:32 +09:00
0add490097 Update ja-JP.yml 2020-02-09 22:05:56 +09:00
2d2d1bd58d Refactor 2020-02-09 22:00:45 +09:00
7813c8a942 Fix #5896 2020-02-09 22:00:38 +09:00
ac5453232f お知らせの固定表示 (#5887)
* お知らせの固定

* ✌️

* Update index.home.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-02-09 21:31:17 +09:00
e78e5274d3 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-09 20:53:08 +09:00
982520bcef fix typo #5890 2020-02-09 20:53:00 +09:00
7abd91f031 いくつかのスタイルを調整 (#5894)
* Chrome(Android)で誕生日欄が崩れていたのを修正

* 入力欄のフォントを親要素から継承するように変更
2020-02-09 19:59:28 +09:00
b93bfb7e5c 🎨 2020-02-09 19:40:15 +09:00
2b20c34c1e Add search shortcut 2020-02-09 19:34:26 +09:00
0f63acea5b Update messaging-room.vue 2020-02-09 19:31:23 +09:00
e600fb7096 Fix #5888 2020-02-09 19:29:49 +09:00
b63fc71865 i18n 2020-02-09 19:18:06 +09:00
23e7650983 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-09 18:53:31 +09:00
01b5ccfdc6 Fix #5891 2020-02-09 18:52:53 +09:00
cc72f91465 Fix cannot update (#5890) 2020-02-09 12:47:50 +09:00
45cb5ff4ef 12.4.1 2020-02-09 03:56:39 +09:00
390279a4a8 Fix #5885 2020-02-09 03:49:18 +09:00
851dececab 非ログイン時に検索欄がズレていたのを修正 (#5883)
* 非ログイン時に検索欄がズレていたのを修正

* flexboxを用いてセンタリングを行うように変更
2020-02-09 03:42:18 +09:00
482afa93a2 Update avatars.vue 2020-02-09 03:41:11 +09:00
25bdbd7ae0 Fix #5886 2020-02-09 03:40:09 +09:00
527a639242 Update ja-JP.yml 2020-02-09 03:33:17 +09:00
0d5e000ad3 12.4.0 2020-02-09 00:01:01 +09:00
f4cb467e7a New Crowdin translations (#5882)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)
2020-02-08 23:59:47 +09:00
2f6b0b142a nanka iroiro 2020-02-08 23:52:40 +09:00
78c2535c3c ✌️ 2020-02-08 22:42:35 +09:00
26260392a8 Fix bug 2020-02-08 22:23:44 +09:00
aa573c0063 Create ActivityでattributedToの補完とaudienceのコピーを行うように (#5873)
* attributedTo

* Create

* copy audiences between activity <=> object

* やっぱり匿名GETのpublicは必要

* fix
2020-02-08 21:40:06 +09:00
b2859bcd2a Fix defalut note visibility setting (#5881)
* Fix default note visibility setting

* refactor

* missing translation

* fix
2020-02-08 20:02:15 +09:00
b58dd8c704 12.3.0 2020-02-08 18:37:00 +09:00
09c96286f9 🎨 2020-02-08 18:35:42 +09:00
f2d2089c21 New Crowdin translations (#5849)
* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-02-08 18:03:56 +09:00
79c366d1f2 Update container.vue 2020-02-08 18:02:28 +09:00
c97ce5255f i18n 2020-02-08 17:52:36 +09:00
1fb66254a4 🎨 2020-02-08 17:49:54 +09:00
2d74f0507b 🎨 2020-02-08 17:36:23 +09:00
9c06544c46 wip 2020-02-08 16:51:27 +09:00
641dad586f 🎨 2020-02-08 15:50:04 +09:00
016144b960 wip 2020-02-08 15:47:16 +09:00
4d6c8efe44 wip 2020-02-08 15:17:35 +09:00
860a7d1eeb wip 2020-02-08 15:11:12 +09:00
2389857be8 🎨 2020-02-08 14:31:51 +09:00
18458f418f [wip] フォルダー名の変更と削除機能を実装 (#5874)
* フォルダーの削除機能を実装

* フォルダ名の変更を実装

* ダイアログの削除(v11準拠)とエラーメッセージを表示するように

* ダイアログのテキストのkeypathを変更
2020-02-08 13:09:38 +09:00
e812d054bc Clean up 2020-02-08 13:06:42 +09:00
44d2c0195a 翻訳の抜けを修正 (#5875)
* missing translation

* fix

* fix

* ✌️
2020-02-08 13:06:09 +09:00
42b4949b7f Update app.vue 2020-02-08 12:49:22 +09:00
d915ae0807 Resolve #5879 2020-02-08 12:46:14 +09:00
8eec8ea35f Fix #5844 2020-02-08 12:13:15 +09:00
023e0ba7aa Revert "Better page transition"
This reverts commit d0d3b70c73.
2020-02-08 11:54:39 +09:00
d0d3b70c73 Better page transition 2020-02-08 11:33:32 +09:00
a509045b25 Fix bug 2020-02-08 11:33:25 +09:00
7be6501571 EmojiReaction => EmojiReact (#5877) 2020-02-07 21:37:24 +09:00
bb4c35d481 fix #5854 2020-02-07 20:25:49 +09:00
47ea84957d docs 2020-02-07 19:45:15 +09:00
fc76f7874e Docs 2020-02-07 19:43:37 +09:00
77a778acf1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-07 19:15:12 +09:00
ff059d1268 wip docs 2020-02-07 19:15:08 +09:00
53bb5012b9 非ログイン時にエラーが発生していたのを修正 (#5872) 2020-02-07 11:39:44 +09:00
09a3a977d7 👀 (#5869) 2020-02-07 09:43:26 +09:00
04db5944d1 Fix #5854 2020-02-07 02:38:02 +09:00
9c97bb431c markdown 2020-02-07 01:20:04 +09:00
38215f2cf9 Fix wrong url on list page (#5865) 2020-02-07 00:19:40 +09:00
01e7a01daf Clean up 2020-02-06 23:27:47 +09:00
c2a8e29ef9 Not found page 2020-02-06 23:20:59 +09:00
15a41e31b0 Fix #5856 2020-02-06 23:12:27 +09:00
294c9840de 12.2.0 2020-02-06 22:27:32 +09:00
568ecd9477 Resolve #5861 2020-02-06 22:25:45 +09:00
169f3ed541 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-06 22:18:26 +09:00
ff7ae427fd Resolve #5860 2020-02-06 22:18:23 +09:00
1597415340 PWAとしてインストールできなかったのを修正 (#5863)
* pwa

* ✌️
2020-02-06 22:11:27 +09:00
47f3261b9f Update CHANGELOG.md 2020-02-06 22:10:55 +09:00
9e68eefbb7 Resolve #5859 2020-02-06 22:10:33 +09:00
630c531d99 Improve messaging form 2020-02-06 19:22:15 +09:00
c7da2a4b5f Resolve #5857 2020-02-06 19:11:14 +09:00
692078f490 🎨 2020-02-06 18:49:57 +09:00
0e29e864c8 Refactor 2020-02-06 18:25:25 +09:00
1b7a601d27 Fix i18n 2020-02-06 17:50:59 +09:00
a96076ee5b 🎨 2020-02-06 17:48:05 +09:00
d580622d1b Update ja-JP.yml 2020-02-06 17:44:41 +09:00
6edccad4dd 12.1.0 2020-02-06 17:29:59 +09:00
8fa47dbcb1 🎨 2020-02-06 17:28:45 +09:00
157f4bbc21 Update CHANGELOG.md 2020-02-06 17:26:09 +09:00
3b0d0df068 i18n 2020-02-06 17:25:04 +09:00
69802a9f00 Resolve #5850 2020-02-06 17:21:28 +09:00
b940da45af Update CHANGELOG.md 2020-02-06 17:11:46 +09:00
bd6de0e204 Fix #5848 (#5853) 2020-02-06 17:11:02 +09:00
958074e347 Update CHANGELOG.md 2020-02-06 17:08:05 +09:00
988ac80087 Correct Like id generation (#5852) 2020-02-06 17:07:37 +09:00
1c7c72181e Fix #5838 2020-02-06 17:05:19 +09:00
6857153367 Fix bug 2020-02-06 17:02:32 +09:00
0a3a0f3beb Update sequential-entrance.vue 2020-02-06 14:55:27 +09:00
e92e83746d Refactor 2020-02-06 14:37:29 +09:00
3b34b3e9ea Fix #5843 2020-02-06 14:29:36 +09:00
9506f53691 Update CHANGELOG.md 2020-02-06 14:25:36 +09:00
92dc6db134 Update CHANGELOG.md 2020-02-06 14:24:43 +09:00
1b88a7bc03 Fix #5842 and refactoring 2020-02-06 14:23:01 +09:00
464 changed files with 9576 additions and 3670 deletions

View File

@ -1,7 +1,188 @@
ChangeLog
=========
12.0.0 indigo (unreleased)
12.14.0 (2020/02/18)
-------------------
### ✨Improvements
* オブジェクトストレージの設定を実装
* サーバーログビューア実装
12.13.0 (2020/02/18)
-------------------
### ✨Improvements
* プロモーションノート機能を実装
* インスタンス管理者が、重要なお知らせやユーザーにやってもらいたいアンケートなどをタイムラインの途中に挿入する機能
* プロモーションされる期限を設定できる
* 複数のプロモーションがある場合はランダムに選択されて表示される
* ユーザーがプロモーションを個別に非表示にすることもできる
* ハイライトインジェクション機能を実装
* タイムラインの途中におすすめのノートを表示できる機能
* 設定で有効/無効を切り替えられる
* アクティビティウィジェットを実装
* フォトウィジェットを実装
* タイムラインの一番上までスクロールできるように
* 管理者はモデレーターに変更できないように
### 🐛Fixes
* admin/show-users APIがadminかつmoderator設定されているとき使えない問題を修正
12.12.0 (2020/02/17)
-------------------
### ✨Improvements
* インスタンス情報ページを強化
* インスタンス設定ページを強化
* 設定ページをアカウント設定ページとクライアント設定ページに分離
* UIの調整
12.11.0 (2020/02/16)
-------------------
### ✨Improvements
* 投稿詳細ページで前後の投稿を見れるように
* 自分のfollowersートはRenoteできるように
* 画像ダイアログを実装
* フォロー申請ページの調整
* 壁紙設定の強化
* 画面が狭い状態でMisskeyを起動した場合でも、画面幅が広がったときにウィジェットを表示するように
* 「もっと読み込む」したときの読み込み量を増量
### 🐛Fixes
* 認証なしでグローバルTLにアクセスすると妙なエラーが返る問題を修正
* API docが見れない問題を修正
* 画面右上に当たり判定があるのを修正
12.10.0 (2020/02/15)
-------------------
### ✨Improvements
* アンテナの受信ソースにグループを指定できるように
* 時計ウィジェットを追加
* ログアウトせずに新しいアカウントを追加できるように
* フォントサイズを設定できるように
* APIキー設定を実装
### 🐛Fixes
* v12アップデート後にトップページアクセスでOops!になっちゃうのを修正
* drive/files APIのパフォーマンスを改善
12.9.0 (2020/02/14)
-------------------
### ✨Improvements
* カスタム絵文字の管理を強化
* 動きのあるMFMを無効にするオプションを追加
### 🐛Fixes
* タイムラインに自分の返信と自分への返信と投稿者自身への返信以外の返信が含まれている問題を修正
* グループがない状態でグループチャットを開始しようとするとフリーズする問題を修正
* 通知インジケーターがずれる問題を修正
* AP: 投稿を削除したときに関係する投稿の削除アクティビティが連合されない問題を修正
12.8.0 (2020/02/13)
--------------------
### ✨Improvements
* タイムラインなどを遡っているときは新しいアイテムが来てもスクロールしないように
* 表示言語を切り替えられるように
* グループに招待されたときの通知を追加
* フランス語と関西弁を有効に
* OSネイティブの絵文字を使用オプションを追加
### 🐛Fixes
* リストを追加するとエラーが出る問題を修正
* タイムラインを放置すると先頭の投稿が見えなくなるのを修正
12.7.1 (2020/02/11)
--------------------
### 🐛Fixes
* 非ログイン時にページが表示されない問題を修正
12.7.0 (2020/02/10)
--------------------
### ✨Improvements
* ノートの文字数制限の設定を復活
* デザインの調整
### 🐛Fixes
* 中国語で表示できない問題を修正
12.6.0 (2020/02/10)
--------------------
### ✨Improvements
* リンクにホバーするとURLプレビューを表示するように
* ユーザーページからグループに招待できるように
* ウィジェットはブラウザごとに記憶するように
### 🐛Fixes
* 要素の幅を判定する処理が上手くいかないことがある問題を修正
12.5.0 (2020/02/09)
--------------------
### ✨Improvements
* チュートリアルを実装
* 検索のキーボードショートカットを追加
* タイムラインを遡っている状況でないときに、誰かをフォローまたはフォロー解除したときにタイムラインをリロードするように
### 🐛Fixes
* グループチャットが開始できない問題を修正
* Renoteメニューが開けない問題を修正
* 誕生日設定が崩れていたのを修正
* キャッシュが削除できない問題を修正
12.4.1 (2020/02/09)
--------------------
### 🐛Fixes
* グループの招待をacceptもrejectも出来ない問題を修正
* 非ログイン時に検索欄がズレていたのを修正
* バックグラウンドで受信したタイムラインの投稿のリアクションが受信されていない問題を修正
12.4.0 (2020/02/09)
--------------------
### ✨Improvements
* ローカルのみをデフォルトで操作できるように
* キーボード操作を改善
* AP: Create ActivityでattributedToの補完とaudienceのコピーを行うように
### 🐛Fixes
* ページ遷移してもナビゲーションが閉じない問題を修正
* デフォルトの公開範囲のリストにホームがなかったので復活
12.3.0 (2020/02/08)
--------------------
### ✨Improvements
* グループ実装
* /share実装
* 指定したURLのページが見つからなかった時のページを実装
* ドキュメント実装
* AP: EmojiReaction => EmojiReact
### 🐛Fixes
* 画面の縦の幅が狭いとメニューが一部隠れる問題を修正
* リストの設定ページが開けなかった問題を修正
* drive-file-thumbnailのicon-subがおかしい問題を修正
* ドライブのフォルダー名の変更と削除ができない問題を修正
12.2.0 (2020/02/06)
--------------------
### ✨Improvements
* UIのアニメーションを無効にできるように
* トークで絵文字ピッカーを表示できるように
* 戻るボタンだけでなく、ホームボタンを押してホームに戻ったときもスクロール位置を復元するように
* タブを見ていないときのタイムライン通知を削除
### 🐛Fixes
* PWAとしてインストールできなかったのを修正
* トークでドライブからファイルを添付出来ない問題を修正
12.1.0 (2020/02/06)
--------------------
### ✨Improvements
* サーバー切断時に自動でリロードできるように
### 🐛Fixes
* もっと読み込むを続けていくと表示が遅くなっていく問題を修正
* Renote メニューが自分の投稿のRenoteでない限り表示されない問題を修正
* MFM jump, spin, title が効かない問題を修正
* AP: Likeで正しいActivity IDを提示するように修正
* AP: Misskey以外からのトークの返信が受け取れないのを修正
12.0.0 indigo (2020/02/06)
--------------------
Misskey v12では、クライアントが設計し直され、全く新しいUIに生まれ変わりました。
レスポンシブになり、ひとつのコードで様々なデバイスに対応できるようにしました。

View File

@ -74,9 +74,17 @@ gulp.task('copy:client', () =>
.pipe(gulp.dest('./built/client/assets/'))
);
gulp.task('copy:docs', () =>
gulp.src([
'./src/docs/**/*',
])
.pipe(gulp.dest('./built/client/assets/docs/'))
);
gulp.task('build:client', gulp.parallel(
'build:client:styles',
'copy:client'
'copy:client',
'copy:docs'
));
gulp.task('build', gulp.parallel(

View File

@ -1 +1,2 @@
---
_lang_: "Čeština"

View File

@ -1 +1,2 @@
---
_lang_: "Dansk"

View File

@ -1 +1,39 @@
---
_lang_: "Deutsch"
monthAndDay: "{day}/{month}"
search: "Suchen"
notifications: "Benachrichtigungen"
username: "Benutzername"
password: "Passwort"
fetchingAsApObject: "Aus Fediverse holen"
ok: "OK"
gotIt: "Verstanden!"
cancel: "Abbrechen"
enterUsername: "Benutzername eingeben"
renotedBy: "Renote von {user}"
noNotes: "Keine Notizen"
noNotifications: "Keine Benachrichtigungen"
instance: "Instanz"
settings: "Einstellungen"
profile: "Profil"
timeline: "Zeitleiste"
noAccountDescription: "Keine Selbsteinführung"
login: "Einloggen"
loggingIn: "Einloggen in bearbeitung"
logout: "Ausloggen"
signup: "Registrieren"
uploading: "Upload läuft"
save: "Speichern"
users: "Benutzer"
addUser: "Benutzer hinzufügen"
copyUsername: "Benutzernamen kopieren"
selectUser: "Benutzer wählen"
instances: "Instanz"
mutedUsers: "Stummgestellte Benutzer"
blockedUsers: "Blockierte Benutzer"
noUsers: "Keine Benutzer"
_widgets:
notifications: "Benachrichtigungen"
timeline: "Zeitleiste"
_profile:
username: "Benutzername"

View File

@ -1,21 +1,6 @@
---
_ago:
unknown: "Unknown"
future: "Future"
justNow: "Just now"
secondsAgo: "{n}s ago"
minutesAgo: "{n}m ago"
hoursAgo: "{n}h ago"
daysAgo: "{n}d ago"
weeksAgo: "{n}week(s) ago"
monthsAgo: "{n}month(s) ago"
yearsAgo: "{n}year(s) ago"
_time:
second: "s"
minute: "m"
hour: "h"
day: "d"
introMisskey: "Welcome! Misskey is an open source distributed microblogging service.\nCreate \"notes\" to share what's happening or to tell everyone about you📡\nThen send \"reactions\" to respond quickly to everyone's notes👍\nLet's explore a new world🚀"
_lang_: "English"
introMisskey: "Welcome! Misskey is an open source and also decentralized microblogging service.\nWrite the \"notes\" to share what is happening now, or send out your own words to everyone 📡\nWith the \"reactions\", you can add your feelings to everyone's notes faster than anyone 👍\nLet's explore the new world 🚀"
monthAndDay: "{month}/{day}"
search: "Search"
notifications: "Notifications"
@ -65,12 +50,13 @@ import: "Import"
export: "Export"
files: "Files"
download: "Download"
driveFileDeleteConfirm: "Are you sure that you want to delete file \"{name}\"? Notes with this file attached will also be deleted."
driveFileDeleteConfirm: "Are you sure you want to delete the file \"{name}\"? Notes with this file attached will also be deleted."
unfollowConfirm: "Are you sure that you want to unfollow {name}?"
exportRequested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive."
importRequested: "You requested an import. This may take a while."
lists: "Lists"
noLists: "You don't have any lists"
note: "Notes"
notes: "Notes"
following: "Following"
followers: "Followers"
@ -80,20 +66,19 @@ manageLists: "Manage lists"
error: "Something happened :("
retry: "Retry"
enterListName: "List name"
renameList: "Rename list"
deleteList: "Delete list"
privacy: "Privacy"
makeFollowManuallyApprove: "Follow requests require approval"
defaultNoteVisibility: "Default visibility"
follow: "Following"
follow: "Follow"
followRequest: "Request follow"
followRequests: "Follow requests"
unfollow: "Unfollow"
followRequestPending: "Pending follow request"
enterEmoji: "Enter an emoji"
renote: "Renote"
unrenote: "Unrenote"
quote: "Quote"
pinnedNote: "Pinned note(s)"
pinnedNote: "Pinned note"
you: "You"
clickToShow: "Click to show"
sensitive: "NSFW"
@ -127,7 +112,7 @@ flagAsBot: "This account is a bot"
flagAsCat: "This account is a cat"
autoAcceptFollowed: "Automatically approve follow requests from users you're following"
addAcount: "Add Account"
loginFailed: "Sign in failure"
loginFailed: "Failed to sign in"
showOnRemote: "View on remote instance"
general: "General"
wallpaper: "Wallpaper"
@ -177,7 +162,7 @@ mutedUsers: "Muted users"
blockedUsers: "Blocked users"
noUsers: "There are no users"
editProfile: "Edit profile"
noteDeleteConfirm: "Are you sure that you want to delete this note?"
noteDeleteConfirm: "Are you sure you want to delete this note?"
pinLimitExceeded: "You cannot pin any more notes."
intro: "Installation of Misskey has been finished! Please create an admin user."
done: "Done"
@ -214,12 +199,10 @@ remove: "Delete"
removed: "Successfully deleted"
removeAreYouSure: "Are you sure that you want to delete \"{x}\"?"
saved: "Saved"
messaging: "Talk"
messaging: "Messaging"
upload: "Upload"
fromDrive: "From Drive"
fromUrl: "From URL"
editWidgets: "Edit widgets"
exitEdit: "Finish editing"
explore: "Explore"
games: "Misskey Games"
messageRead: "Read"
@ -266,12 +249,12 @@ instanceDescription: "Instance description"
maintainerName: "Maintainer"
maintainerEmail: "Maintainer email"
tosUrl: "Terms of Service URL"
thisYear: "This year"
thisMonth: "This month"
thisYear: "Year"
thisMonth: "Month"
today: "Today"
dayX: "{day} days"
monthX: "{month} months"
yearX: "{year} years"
dayX: "{day}"
monthX: "{month}"
yearX: "{year} /"
pages: "Pages"
integration: "Integration"
connectSerice: "Connect"
@ -302,8 +285,8 @@ name: "Name"
antennaSource: "Antenna source"
antennaKeywords: "Antenna keywords"
antennaKeywordsDescription: "Separate with spaces for AND condition. Separate with line breaks for OR."
notifyAntenna: "Get notification for new notes"
withFileAntenna: "Notes with attachment only"
notifyAntenna: "Notify newer notes"
withFileAntenna: "Filter only notes with file attached"
serviceworker: "ServiceWorker"
enableServiceworker: "Enable ServiceWorker"
antennaUsersDescription: "List one username per line"
@ -311,7 +294,7 @@ caseSensitive: "Case sensitive"
withReplies: "Include replies"
connectedTo: "Following account(s) are connected"
notesAndReplies: "Notes and replies"
withFiles: "Attach file(s)"
withFiles: "Media"
silence: "Silence"
silenceConfirm: "Are you sure that you want to silence this user?"
unsilenceConfirm: "Are you sure that you want to undo silence of this user?"
@ -328,6 +311,7 @@ aboutMisskey: "About Misskey"
aboutMisskeyText: "Misskey is an open-source software developed by syuilo since 2014."
misskeyMembers: "It is currently developed an maintained by the members listed below:"
misskeySource: "Source code is available here:"
misskeyTranslation: "Help us with your contribution to translate Misskey:"
misskeyDonate: "Help us to keep improving the software by donating here:"
morePatrons: "We really appreciate the support of many other helpers not listed here. Thank you! 🥰"
patrons: "Backers"
@ -342,17 +326,147 @@ registerSecurityKey: "Register a security key"
lastUsed: "Last used"
unregister: "Unregister"
passwordLessLogin: "Set up password-less login"
resetPassword: "Reste password"
resetPassword: "Reset password"
newPasswordIs: "The new password is \"{password}\""
post: "Notes"
posted: "Posted!"
autoReloadWhenDisconnected: "Auto reload when disconnected with server"
autoNoteWatch: "Watch note automatically"
autoNoteWatchDescription: "Get notified about the notes which you reactioned or replied."
reduceUiAnimation: "Reduce animations of User Interface"
share: "Share"
notFound: "Not found"
notFoundDescription: "There was no page corresponding to the specified URL."
uploadFolder: "Default Upload location"
cacheClear: "Clear cache"
markAsReadAllNotifications: "Mark all notifications as read"
markAsReadAllUnreadNotes: "Mark all notes as read"
markAsReadAllTalkMessages: "Mark all messages as read"
help: "Help"
inputMessageHere: "Enter message here"
close: "Close"
group: "Groups"
groups: "Groups"
createGroup: "Create a group"
ownedGroups: "Owned Groups"
joinedGroups: "Membership in groups"
invites: "Invite"
groupName: "Group name"
members: "Members"
transfer: "Transfer"
messagingWithUser: "Messaging with other user"
messagingWithGroup: "Messaging within group"
title: "Title"
text: "Text"
enable: "Enable"
next: "Next"
retype: "Enter again"
noteOf: "{user}'s notes"
inviteToGroup: "Invite to group"
maxNoteTextLength: "Character limit of the note"
quoteAttached: "Quoted"
quoteQuestion: "Do you want to append a quote?"
noMessagesYet: "No messages yet"
newMessageExists: "You've got a new message"
onlyOneFileCanBeAttached: "You can only attach one file to a message"
signinRequired: "Please sign in"
invitationCode: "Invitation code"
checking: "Checking"
available: "Available"
unavailable: "Not available"
usernameInvalidFormat: "letters, numbers and _ are acceptable."
tooShort: "Too short"
tooLong: "Too long"
weakPassword: "Weak password"
normalPassword: "Good password"
strongPassword: "Strong password"
passwordMatched: "Matched"
passwordNotMatched: "Doesn't match"
signinWith: "Sign in with {x}"
tapSecurityKey: "Tap your security key"
or: "Or"
uiLanguage: "UI display language"
groupInvited: "Invited to group"
aboutX: "About {x}"
useOsNativeEmojis: "Use the OS native Emojis"
noGroups: "No groups"
joinOrCreateGroup: "Get invited to join the groups or you can create your own group."
noHistory: "No history items"
disableAnimatedMfm: "Disable MFM which has animations"
doing: "On my way"
category: "Category"
tags: "Tags"
docSource: "Source of this document"
createAccount: "Create account"
existingAcount: "Existing accounts"
regenerate: "Regenerate"
fontSize: "Font size"
noFollowRequests: "You don't have any pending follow requests"
openImageInNewTab: "Open image in new tab"
dashboard: "Dashboard"
local: "Local"
remote: "Remote"
total: "Total"
weekOverWeekChanges: "Weekly"
dayOverDayChanges: "Daily"
accessibility: "Accessibility"
clinetSettings: "Client Settings"
accountSettings: "Account Settings"
promotion: "Promoted"
promote: "Promote"
numberOfDays: "Amount of days"
hideThisNote: "Hide this note"
showFeaturedNotesInTimeline: "Show Featured notes in Timeline"
objectStorage: "Object Storage"
useObjectStorage: "Use object storage"
_ago:
unknown: "Unknown"
future: "Future"
justNow: "Just now"
secondsAgo: "{n}s ago"
minutesAgo: "{n}m ago"
hoursAgo: "{n}h ago"
daysAgo: "{n}d ago"
weeksAgo: "{n}week(s) ago"
monthsAgo: "{n}month(s) ago"
yearsAgo: "{n}year(s) ago"
_time:
second: "s"
minute: "m"
hour: "h"
day: "d"
_tutorial:
title: "How to use Misskey"
step1_1: "Welcome!"
step1_2: "This page is called \"timeline\". It shows chronologically ordered \"notes\" of people who you \"follow\"."
step1_3: "Your timeline is currently empty, since you have not posed any notes or followed anyone yet."
step2_1: "Let's finish setting up your profile before writing a note or following anyone."
step2_2: "Providing some information about who you are will make it easier for others to follow you back."
step3_1: "Finished setting up your profile?"
step3_2: "The next step is to post a note. You can do this by pressing a pencil icon on the screen."
step3_3: "Fill in the modal and press the button on the right top to post."
step3_4: "Have nothing to say? Try \"just setting up my msky\"!"
step4_1: "Finished posting your first note?"
step4_2: "Hurray! Now your first note is displayed on your timeline."
step5_1: "Now, let's try making your timeline more lively by following other people."
step5_2: "{featured} will show you trending notes in this instance. {explore} will let you find trending users. Try following people you like!"
step5_3: "To follow other users, click on their icon and press \"follow\" button on their profile."
step5_4: "If the other user has a lock icon next to their name, that user will have to manually approve your follow request."
step6_1: "Now you will be able to see other users' notes on your timeline."
step6_2: "You can also put \"reactions\" on other people's notes to quickly respond."
step6_3: "To attach a \"reaction\", press \"+\" mark on other user's note and choose an emoji you'd like to react with."
step7_1: "Congratulations! You have now finished Misskey's basic tutorial."
step7_2: "If you would like to learn more about Misskey, try the {help} section."
step7_3: "Good luck and have fun! 🚀"
_2fa:
alreadyRegistered: "You have already registered 2-factor authentication device."
registerDevice: "Register a new device"
registerKey: "Register a new Security Key"
step1: "First, install an authentication app (such as {a} or {b}) on your device."
step2: "Then, scan the QR code on the screen."
step3: "Enter the token provided by your app to finish setup."
step4: "From now, any login attempt will ask for your login token."
securityKeyInfo: "You can set up a hardware security key (must support FIDO2) to further secure your login."
securityKeyInfo: "You can setup WebAuthN authentication to further secure the log-in process with not only hardware security key which supports FIDO2, but also fingerprint or PIN authentication on your device."
_permissions:
"read:account": "View your account information"
"write:account": "Edit your account information"
@ -364,11 +478,11 @@ _permissions:
"write:favorites": "Edit your favorites list"
"read:following": "View your following information"
"write:following": "Follow or unfollow other accounts"
"read:messaging": "View your talks"
"write:messaging": "Start or delete your talks"
"read:messaging": "View your messages"
"write:messaging": "Compose or Delete messages"
"read:mutes": "View the list of people you muted"
"write:mutes": "Edit the list of people you muted"
"write:notes": "Compose and delete notes"
"write:notes": "Compose or Delete notes"
"read:notifications": "View notifications"
"write:notifications": "Work with notifications"
"read:reactions": "View reactions"
@ -386,8 +500,9 @@ _auth:
_antennaSources:
all: "All notes"
homeTimeline: "Notes from following users"
users: "Notes from specific user(s)"
users: "Notes from specific users"
userList: "Notes from specific list"
userGroup: "Notes from users in the specified group"
_weekday:
sunday: "Sunday"
monday: "Monday"
@ -403,6 +518,9 @@ _widgets:
calendar: "Calendar"
trends: "Trending"
clock: "Clock"
rss: "RSS reader"
activity: "Activity"
photos: "Photos"
_cw:
hide: "Hide"
show: "Load more"
@ -440,9 +558,10 @@ _visibility:
followersDescription: "Post to followers only"
specified: "Direct"
specifiedDescription: "Post to specified users only"
localOnly: "Local only"
_postForm:
replyPlaceholder: "Reply to this note"
quotePlaceholder: "Quote this post..."
replyPlaceholder: "Reply to this note..."
quotePlaceholder: "Quote this note..."
_placeholders:
a: "What are you up to?"
b: "What's happening around you?"
@ -471,7 +590,7 @@ _charts:
usersTotal: "Total # of users"
activeUsers: "Active users"
notesIncDec: "Difference in # of notes"
localNotesIncDec: "Total # of local notes"
localNotesIncDec: "Difference in # of local notes"
remoteNotesIncDec: "Difference in # of remote notes"
notesTotal: "Total # of notes"
filesIncDec: "Difference in # of files"

View File

@ -1,21 +1,6 @@
---
_ago:
unknown: "Desconocido"
future: "Futuro"
justNow: "Recién ahora"
secondsAgo: "Hace {n} segundos"
minutesAgo: "Hace {n} minutos"
hoursAgo: "Hace {n} horas"
daysAgo: "Hace {n} días"
weeksAgo: "Hace {n} semanas"
monthsAgo: "Hace {n} meses"
yearsAgo: "Hace {n} años"
_time:
second: "Segundos"
minute: "Minutos"
hour: "Horas"
day: "Días"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto. Escribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos. 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos.👍\nExplora un nuevo mundo.🚀"
_lang_: "Español"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\nExplora un nuevo mundo 🚀"
monthAndDay: "{day}/{month}"
search: "Buscar"
notifications: "Notificaciones"
@ -71,6 +56,7 @@ exportRequested: "Se ha solicitado la exportación. Puede tomar un tiempo. Cuand
importRequested: "Se ha solicitado la importación. Puede tomar un tiempo."
lists: "Listas"
noLists: "No tiene listas"
note: "Notas"
notes: "Notas"
following: "Sigue"
followers: "Seguidores"
@ -80,8 +66,6 @@ manageLists: "Administrar listas"
error: "Ocurrió un problema"
retry: "Reintentar"
enterListName: "Ingrese nombre de lista"
renameList: "Renombrar lista"
deleteList: "Borrar lista"
privacy: "Privacidad"
makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento"
defaultNoteVisibility: "Visibilidad por defecto"
@ -92,6 +76,7 @@ unfollow: "Dejar de seguir"
followRequestPending: "Solicitudes de seguimiento pendientes"
enterEmoji: "Ingresar emojis"
renote: "Renotar"
unrenote: "Quitar renota"
quote: "Citar"
pinnedNote: "Nota fijada"
you: "Tú"
@ -214,18 +199,16 @@ remove: "Borrar"
removed: "Borrado"
removeAreYouSure: "¿Desea borrar \"{x}\"?"
saved: "Guardado"
messaging: "Conversación"
messaging: "Chat"
upload: "Subir"
fromDrive: "Desde el drive"
fromUrl: "Desde la URL"
editWidgets: "Editar widgets"
exitEdit: "Terminar edición"
explore: "Explorar"
games: "Misskey Games"
messageRead: "Ya leído"
recentUsedEmojis: "Emojis usados recientemente"
noMoreHistory: "El historial se ha acabado"
startMessaging: "Iniciar conversación"
startMessaging: "Iniciar chat"
nUsersRead: "Leído por {n} personas"
agreeTo: "De acuerdo con {0}"
tos: "Términos de uso"
@ -302,8 +285,8 @@ name: "Nombre"
antennaSource: "Origen de la antena"
antennaKeywords: "Palabras clave de la antena"
antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR"
notifyAntenna: "Notificar notas nuevas"
withFileAntenna: "Solo notas con adjuntos"
notifyAntenna: "Notificar nueva nota"
withFileAntenna: "Sólo notas con archivos adjuntados"
serviceworker: "ServiceWorker"
enableServiceworker: "Activar ServiceWorker"
antennaUsersDescription: "Elegir nombres de usuarios separados por una linea nueva"
@ -328,6 +311,7 @@ aboutMisskey: "Sobre Misskey"
aboutMisskeyText: "Misskey es un software de código abierto, desarrollado por syuilo desde el 2014"
misskeyMembers: "Es creado y mantenido por los miembros aquí listados:"
misskeySource: "El código fuente está disponible aquí:"
misskeyTranslation: "Ayúdanos con tu contribución para traducir Misskey:"
misskeyDonate: "Puedes contribuir al desarrollo de Misskey donando aquí:"
morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
patrons: "Patrocinadores"
@ -346,13 +330,143 @@ resetPassword: "Resetear contraseña"
newPasswordIs: "La nueva contraseña es \"{password}\""
post: "Nota"
posted: "Posteado"
autoReloadWhenDisconnected: "Recargar automáticamente cuando el servidor está desconectado"
autoNoteWatch: "Ver nota automáticamente"
autoNoteWatchDescription: "Recibe notificaciones sobre las notas de otros usuarios que a los que respondiste y reaccionaste"
reduceUiAnimation: "Reducir la animación de la UI"
share: "Compartir"
notFound: "No se encuentra"
notFoundDescription: "No se encontró la página correspondiente a la URL elegida"
uploadFolder: "Carpeta de subidas por defecto"
cacheClear: "Borrar caché"
markAsReadAllNotifications: "Marcar todas las notificaciones como leídas"
markAsReadAllUnreadNotes: "Marcar todas las notas como leídas"
markAsReadAllTalkMessages: "Marcar todos los chats como leídos"
help: "Ayuda"
inputMessageHere: "Escribe el mensaje aquí"
close: "Cerrar"
group: "Grupo"
groups: "Grupos"
createGroup: "Crear grupo"
ownedGroups: "Tus"
joinedGroups: "Grupos a los que me uní"
invites: "Invitar"
groupName: "Nombre del grupo"
members: "Miembros"
transfer: "Transferir"
messagingWithUser: "Chatear con usuario"
messagingWithGroup: "Chatear en grupo"
title: "Título"
text: "Texto"
enable: "Activar"
next: "Siguiente"
retype: "Intentar de nuevo"
noteOf: "Notas de {user}"
inviteToGroup: "Invitar al grupo"
maxNoteTextLength: "Límite de caracteres en una nota"
quoteAttached: "Cita añadida"
quoteQuestion: "¿Quiere añadir una cita?"
noMessagesYet: "Aún no hay chat"
newMessageExists: "Tienes un mensaje nuevo"
onlyOneFileCanBeAttached: "Solo se puede añadir un archivo al mensaje"
signinRequired: "Iniciar sesión"
invitationCode: "Código de invitación"
checking: "Comprobando"
available: "Disponible"
unavailable: "No disponible"
usernameInvalidFormat: "utiliza letras, números y/o -."
tooShort: "Demasiado corto"
tooLong: "Demasiado largo"
weakPassword: "Contraseña débil"
normalPassword: "Buena contraseña"
strongPassword: "Muy buena contraseña"
passwordMatched: "Correcto"
passwordNotMatched: "Las contraseñas no son las mismas"
signinWith: "Inicie sesión con {x}"
tapSecurityKey: "Toque la clave de seguridad"
or: "O"
uiLanguage: "Idioma de visualización de la interfaz"
groupInvited: "Invitado al grupo"
aboutX: "Acerca de {x}"
useOsNativeEmojis: "Usa los emojis nativos de la plataforma"
noGroups: "Sin grupos"
joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo."
noHistory: "No hay datos en el historial"
disableAnimatedMfm: "Deshabilitar MFM que tiene animaciones"
doing: "Voy en camino"
category: "Categoría"
tags: "Etiqueta"
docSource: "Fuente de este documento"
createAccount: "Crear cuenta"
existingAcount: "Cuentas existentes"
regenerate: "Regenerar"
fontSize: "Tamaño de la letra"
noFollowRequests: "No hay solicitudes de seguimiento"
openImageInNewTab: "Abrir imagen en nueva pestaña"
dashboard: "Panel de control"
local: "Local"
remote: "Remoto"
total: "Total"
weekOverWeekChanges: "Dif semanal"
dayOverDayChanges: "Dif diaria"
accessibility: "Accesibilidad"
clinetSettings: "Ajustes del cliente"
accountSettings: "Ajustes de cuenta"
promotion: "Promovido"
promote: "Promover"
numberOfDays: "Cantidad de dias"
hideThisNote: "Ocultar esta nota"
showFeaturedNotesInTimeline: "Mostrar notas destacadas en la línea de tiempo"
objectStorage: "Almacenamiento de objetos"
useObjectStorage: "Usar almacenamiento de objetos"
_ago:
unknown: "Desconocido"
future: "Futuro"
justNow: "Recién ahora"
secondsAgo: "Hace {n} segundos"
minutesAgo: "Hace {n} minutos"
hoursAgo: "Hace {n} horas"
daysAgo: "Hace {n} días"
weeksAgo: "Hace {n} semanas"
monthsAgo: "Hace {n} meses"
yearsAgo: "Hace {n} años"
_time:
second: "Segundos"
minute: "Minutos"
hour: "Horas"
day: "Días"
_tutorial:
title: "Cómo usar Misskey"
step1_1: "Bienvenido"
step1_2: "Esta imagen se llama \"Linea de tiempo\" y muestra en orden cronológico las \"notas\" tuyas y de la gente que \"sigues\""
step1_3: "Si no estás escribiendo ninguna nota y no estás siguiendo a nadie, es esperable que no se muestre nada en la linea de tiempo"
step2_1: "Antes de crear notas y seguir a alguien, primero vamos a crear tu perfil"
step2_2: "Si provees información sobre quien eres, será más fácil para que otros usuarios te sigan"
step3_1: "¿Has podido crear tu perfil sin problemas?"
step3_2: "Con esto, prueba hacer una nota. Aprieta el botón con forma de lápiz que está arriba de la imagen y abre el formulario."
step3_3: "Si has escrito el contenido, aprieta el botón que está arriba a la derecha del formulario para postear."
step3_4: "¿No se te ocurre un contenido? Prueba con decir \"Empecé a usar Misskey\""
step4_1: "¿Has posteado?"
step4_2: "Si tu nota puede verse en la linea de tiempo, fue todo un éxito."
step5_1: "Luego, ponte a seguir a otra gente y haz que tu linea de tiempo esté más animada."
step5_2: "Puedes ver las notas destacadas en {featured} y desde allí seguir a usuarios que te importan. También puedes buscar usuario destacados en {explore}."
step5_3: "Para seguir a un usuario, haz click en su avatar para ver su página de usuario y allí apretar el botón \"seguir\""
step5_4: "De esa manera, puede pasar un tiempo hasta que el usuario apruebe al seguidor."
step6_1: "Si puedes ver en la linea de tiempo las notas de otros usuarios, fue todo un éxito."
step6_2: "En las notas de otros usuarios puedes añadir una \"reacción\", para poder responder rápidamente."
step6_3: "Para añadir una reacción, haz click en el botón \"+\" de la nota y elige la reacción que prefieras."
step7_1: "Así terminó la explicación del funcionamiento básico de Misskey. Eso fue todo."
step7_2: "Si quieres conocer más sobre Misskey, prueba con la sección {help}."
step7_3: "Así, disfruta de Misskey 🚀"
_2fa:
alreadyRegistered: "Ya has completado la configuración."
registerDevice: "Registrar dispositivo"
registerKey: "Registrar clave"
step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
step3: "Para terminar, ingrese el token mostrado en la aplicación."
step4: "Ahora cuando inicie sesión, ingrese el mismo token"
securityKeyInfo: "Se puede configurar para que se inicie sesión usando una clave de seguridad de hardware que soporte FIDO2."
securityKeyInfo: "Se puede configurar el inicio de sesión usando una clave de seguridad de hardware que soporte FIDO2 o con un certificado de huella digital o con un PIN"
_permissions:
"read:account": "Ver información de la cuenta"
"write:account": "Editar información de la cuenta"
@ -364,11 +478,11 @@ _permissions:
"write:favorites": "Addministrar favoritos"
"read:following": "Ver información de seguidor"
"write:following": "Seguir o dejar de seguir"
"read:messaging": "Ver conversación"
"write:messaging": "Administrar coversación"
"read:messaging": "Ver chat"
"write:messaging": "Administrar chat"
"read:mutes": "Ver usuarios silenciados"
"write:mutes": "Administrar usuarios silenciados"
"write:notes": "Crear o borrar notas"
"write:notes": "Crear/borrar notas"
"read:notifications": "Ver notificaciones"
"write:notifications": "Administrar notificaciones"
"read:reactions": "Ver reacciones"
@ -386,8 +500,9 @@ _auth:
_antennaSources:
all: "Todas las notas"
homeTimeline: "Notas de los usuarios que sigues"
users: "Solo notas de determinados usuarios"
userList: "Solo notas de usuarios de una lista"
users: "Notas de un usuario o varios"
userList: "Notas de los usuarios de una lista"
userGroup: "Notas de los usuarios de una grupo"
_weekday:
sunday: "Domingo"
monday: "Lunes"
@ -403,6 +518,9 @@ _widgets:
calendar: "Calendario"
trends: "Tendencias"
clock: "Reloj"
rss: "Lector RSS"
activity: "Actividad"
photos: "Fotos"
_cw:
hide: "Ocultar"
show: "Ver más"
@ -440,6 +558,7 @@ _visibility:
followersDescription: "Visible sólo para tus seguidores"
specified: "Mensaje directo"
specifiedDescription: "Visible sólo para los usuarios elegidos"
localOnly: "Solo local"
_postForm:
replyPlaceholder: "Responder a esta nota"
quotePlaceholder: "Citar esta nota"
@ -470,9 +589,9 @@ _charts:
usersIncDec: "Variación de usuarios"
usersTotal: "Total de usuarios"
activeUsers: "Cantidad de usuarios activos"
notesIncDec: "Variación de cantidad de notas"
localNotesIncDec: "Variación de cantidad de notas locales"
remoteNotesIncDec: "Variación de cantidad de notas remotas"
notesIncDec: "Variación de la cantidad de notas"
localNotesIncDec: "Variación de la cantidad de notas locales"
remoteNotesIncDec: "Variación de la cantidad de notas remotas"
notesTotal: "Total de notas"
filesIncDec: "Variación de cantidad de archivos"
filesTotal: "Total de archivos"
@ -482,8 +601,8 @@ _instanceCharts:
requests: "Pedidos"
users: "Variación de usuarios"
usersTotal: "Total de usuarios"
notes: "Variación de cantidad de notas"
notesTotal: "Total de notas"
notes: "Variación de la cantidad de notas"
notesTotal: "Estimación de notas"
ff: "Variación de cantidad de seguidos/seguidores"
ffTotal: "Total de seguidos/seguidores"
cacheSize: "Variación del tamaño de la caché"

View File

@ -1 +1,871 @@
---
_lang_: "Français"
introMisskey: "Bienvenue! Misskey est un service de microblogage décentralisé open source.\nÉcrivez des «notes» pour partager ce qui vous arrive maintenant ou pour parler de vous à tout le monde 📡\nAvec la fonction «réactions», vous pouvez également ajouter une réaction rapide aux notes de chacun 👍\nExplorez un nouveau monde 🚀"
monthAndDay: "{day}/{month}"
search: "Rechercher"
notifications: "Notifications"
username: "Nom d'utilisateur·rice"
password: "Mot de passe"
fetchingAsApObject: "Récupération depuis le fédiverse"
ok: "D'accord"
gotIt: "J'ai compris !"
cancel: "Annuler"
enterUsername: "Entrer un nom d'utilisateur·rice"
renotedBy: "Renoté par {user}"
noNotes: "Pas de notes"
noNotifications: "Pas de notifications"
instance: "Instance"
settings: "Paramètres"
profile: "Profil"
timeline: "Fil d'actualité"
noAccountDescription: "L'utilisateur·rice n'a pas renseigné de présentation sur son profil"
login: "Se connecter"
loggingIn: "Connexion en cours"
logout: "Se déconnecter"
signup: "S'enregistrer"
uploading: "Envoi en cours"
save: "Enregistrer"
users: "Utilisateur·rice·s"
addUser: "Ajouter un·e utilisateur·rice"
favorite: "Ajouter aux favoris"
favorites: "Favoris"
unfavorite: "Retirer des favoris"
pin: "Épingler sur le profil"
unpin: "Désépingler"
copyContent: "Copier le contenu"
copyLink: "Copier le lien"
delete: "Supprimer"
addToList: "Ajouter à une liste"
sendMessage: "Envoyer un message"
copyUsername: "Copier le nom d'utilisateur"
reply: "Répondre"
loadMore: "Voir plus"
youGotNewFollower: "Vous a suivi"
receiveFollowRequest: "Demande de suivi reçue"
followRequestAccepted: "Suivre la demande acceptée"
mentions: "Mentions"
directNotes: "Messages directs"
importAndExport: "Import et export"
import: "Importer"
export: "Exporter"
files: "Fichier·s"
download: "Télécharger"
driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées."
unfollowConfirm: "Êtes-vous sûr·e ne plus vouloir suivre {name} ?"
exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive."
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
lists: "Listes"
noLists: "Aucune liste"
note: "Note"
notes: "Notes"
following: "Abonnements"
followers: "Abonné·e·s"
followsYou: "Votre abonné"
createList: "Créer une liste"
manageLists: "Gérer les listes"
error: "Une erreur est survenue"
retry: "Réessayer"
enterListName: "Nom de la liste"
privacy: "Vie privée"
makeFollowManuallyApprove: "Demandes dabonnements requiert lapprobation"
defaultNoteVisibility: "Visibilité par défaut"
follow: "Abonnement"
followRequest: "Demande dabonnement"
followRequests: "Demandes dabonnement"
unfollow: "Se désabonner"
followRequestPending: "En attente dapprobation"
enterEmoji: "ou entrez un émoji"
renote: "Renote"
unrenote: "Annuler Renote"
quote: "Citation"
pinnedNote: "Note épinglée"
you: "Vous"
clickToShow: "Cliquer pour afficher"
sensitive: "Contenu sensible"
add: "Ajouter"
reaction: "Réactions"
reactionSettingDescription: "Personnaliser les émojis à afficher dans le sélecteur de réactions, délimités par les sauts de ligne."
rememberNoteVisibility: "Se souvenir de la visibilité des notes"
renameFile: "Renommer le ficher"
attachCancel: "Enlever le fichier attaché"
markAsSensitive: "Marquer comme sensible"
unmarkAsSensitive: "Enlever le marquage comme sensible"
enterFileName: "Entrer le nom du fichier"
mute: "Mettre en sourdine"
unmute: "Enlever la sourdine"
block: "Bloquer"
unblock: "Débloquer"
suspend: "Suspendre"
unsuspend: "Annuler la suspension"
blockConfirm: "Désirez-vous bloquer ce compte ?"
unblockConfirm: "Désirez-vous débloquer ce compte ?"
suspendConfirm: "Désirez-vous suspendre ce compte ?"
unsuspendConfirm: "Désirez-vous annuler la suspension de ce compte ?"
selectList: "Sélectionner une liste"
customEmojis: "Émojis personnalisés"
emojiName: "Nom de lémoji"
emojiUrl: "URL de lémoji"
addEmoji: "Ajouter un émoji"
cacheRemoteFiles: "Mettre en cache des fichiers distants"
cacheRemoteFilesDescription: "Quand ce paramètre est désactivé, les fichiers distants sont chargés directement de l'instance distante. Désactiver cela diminuera l'utilisation du stockage mais augmentera le trafic parce les miniatures ne seront pas générées."
flagAsBot: "Ce compte est un robot"
flagAsCat: "Ce compte est un chat"
autoAcceptFollowed: "Approuver automatiquement les abonnements des utilisateurs abonné·e·s"
addAcount: "Ajouter un compte"
loginFailed: "Échec de la connexion"
showOnRemote: "Voir sur l'instance distante"
general: "Général"
wallpaper: "Arrière plan"
removeWallpaper: "Supprimer l'arrière plan"
searchWith: "Recherche : {q}"
youHaveNoLists: "Vous n'avez aucune liste"
followConfirm: "Désirez-vous suivre {name} ?"
proxyAccount: "Compte proxy"
proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la suit. Le compte proxy va donc le·la suivre pour que ses notes soient acheminées."
host: "Hôte"
selectUser: "Sélectionner un·e utilisateur·rice"
recipient: "Correspondant·e"
annotation: "Commentaires"
federation: "Fédération"
instances: "Instance"
registeredAt: "Premier contact le"
latestRequestSentAt: "Dernière requête envoyée"
latestRequestReceivedAt: "Dernière requête reçue"
latestStatus: "Dernière statut"
storageUsage: "Stockage utilisé"
charts: "Graphiques"
perHour: "par heure"
perDay: "par jour"
stopActivityDelivery: "Arrêter l'envoi d'activités"
blockThisInstance: "Bloquer cette instnce"
operations: "Opérations"
software: "Logiciel"
version: "Version"
metadata: "Métadonnées"
withNFiles: "{n} fichier(s)"
monitor: "Écran de contrôle"
jobQueue: "File dattente"
cpuAndMemory: "Processeur et mémoire"
network: "Réseau"
disk: "Disque"
instanceInfo: "Informations sur l'instance"
statistics: "Statistiques"
clearQueue: "Vider la file d'attente"
clearQueueConfirmTitle: "Êtes-vous sûr·e de vouloir vider la file d'attente ?"
clearQueueConfirmText: "Les notes non distribuées ne seront pas livrées. Normalement, vous n'avez PAS besoin d'effectuer cette opération."
clearCachedFiles: "Vider le cache"
clearCachedFilesConfirm: "Êtes-vous sûr·e de vouloir vider le cache de fichiers distants ?"
blockedInstances: "Instances bloquées"
blockedInstancesDescription: "Listez les instance que vous désirez bloquer, une par ligne. Ces instances bloquées ne seront pas capable d'interagir avec cette instance."
muteAndBlock: "Masqués / Bloqués"
mutedUsers: "Utilisateur·rice·s en sourdine"
blockedUsers: "Utilisateur·rice·s bloqué·e·s"
noUsers: "Il n'y a aucun utilisateur·rice"
editProfile: "Modifier votre profil"
noteDeleteConfirm: "Confirmez-vous la suppression de cette note ?"
pinLimitExceeded: "Je ne peux plus épingler"
intro: "L'installation de Misskey est terminée! Créons le compte administrateur."
done: "Terminé"
processing: "Traitement en cours"
preview: "Prévisualisation"
noCustomEmojis: "Il a pas démoji"
customEmojisOfRemote: "Émojis l'instance distante"
noJobs: "Il n'y a aucune tâche planifiée"
federating: "En cours de fédération"
blocked: "Bloqué"
suspended: "Suspendu"
all: "Tous"
subscribing: "Abonné"
publishing: "Publié"
notResponding: "Ne répond pas"
instanceFollowing: "Abonnements une instance"
instanceFollowers: "Abonné·e·s de l'instance"
instanceUsers: "Utilisateur·e·s de l'instance"
changePassword: "Modifier votre mot de passe"
security: "Sécurité"
retypedNotMatch: "Les saisies ne correspondent pas."
currentPassword: "Mot de passe actuel"
newPassword: "Nouveau mot de passe"
newPasswordRetype: "Nouveau mot de passe (répéter)"
attachFile: "Joindre un fichier"
more: "Plus !"
featured: "Surlignage"
usernameOrUserId: "Nom d'utilisateur ou ID utilisateur"
noSuchUser: "Utilisateur non trouvé"
lookup: "Recherche"
announcements: "Annonces"
imageUrl: "URL de limage"
remove: "Supprimer"
removed: "Supprimé"
removeAreYouSure: "Supprimer «{x}» ?"
saved: "Enregistré"
messaging: "Discuter"
upload: "Téléchargez"
fromDrive: "Depuis le Drive"
fromUrl: "De l'URL"
explore: "Découvrir"
games: "Jeux de Misskey"
messageRead: "Lus"
recentUsedEmojis: "Emoji récemment utilisé"
noMoreHistory: "Plus d'histoire passée"
startMessaging: "Commencer à écrire un discutez"
nUsersRead: "{n} personnes ont lu"
agreeTo: "D'accord {0}"
tos: "Conditions d'utilisation"
start: "Commencer"
home: "Principal"
remoteUserCaution: "Les informations sont incomplètes en raison de l'utilisateur distant."
activity: "Activités"
images: "Images"
birthday: "Date de naissance"
yearsOld: "{age} ans"
registeredDate: "Date de création"
location: "Localisation"
theme: "Thème"
lightThemes: "Thème lumineux"
darkThemes: "Thème sombre"
drive: "Drive"
selectFile: "Choisir le fichier"
selectFiles: "Choisir le fichiers"
renameFolder: "Renommer le dossier"
createFolder: "Créer un dossier"
deleteFolder: "Supprimer le dossier"
addFile: "Ajoutez un fichier"
emptyDrive: "Le Drive est vide"
emptyFolder: "Le dossier est vide"
copyUrl: "Copier lURL"
rename: "Renommer"
avatar: "Avatar"
banner: "Bannière"
nsfw: "Contenu sensible"
disconnectedFromServer: "Déconnecté du serveur"
reloadConfirm: "Voulez-vous recharger?"
watch: "Surveiller"
unwatch: "Ne plus surveiller"
accept: "Autoriser"
reject: "Refuser"
instanceName: "Nom de linstance"
instanceDescription: "Description de linstance"
maintainerName: "Nom d'administrateur"
maintainerEmail: "Email de l'administrateur"
tosUrl: "URL des conditions d'utilisation"
thisYear: "Cette année"
thisMonth: "Ce mois-ci"
today: "Aujourd'hui"
dayX: "{day} jour"
monthX: "{month} mois"
yearX: "{year} année"
pages: "Pages"
integration: "Intégrations"
connectSerice: "Connecter"
disconnectSerice: "Déconnecter"
enableLocalTimeline: "Activer le fil local"
enableGlobalTimeline: "Activer le fil global"
disablingTimelinesInfo: "Si vous désactivez ces le fils, les administrateurs et les modérateurs pourront toujours y accéder pour plus de commodité."
registration: "S'inscrire"
enableRegistration: "Autoriser nimporte qui à senregistrés"
invite: "Inviter"
proxyRemoteFiles: "Proxy fichiers distants"
proxyRemoteFilesDescription: "Si vous activez ce paramètre, les fichiers distants non stockés ou supprimés en raison d'une capacité excédentaire seront affichés via un proxy local et généreront une miniature. Cela n'affectera pas le stockage du serveur."
driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local"
driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant"
inMb: "en mégaoctets"
iconUrl: "URL de l'image de l'icône"
bannerUrl: "URL de l'image de la bannière"
basicInfo: "Informations basiques"
pinnedUsers: "Utilisateur·rice épinglé·e"
pinnedUsersDescription: "Décrivez les utilisateurs que vous souhaitez définir sur la page \"Découvrir\" séparés par une nouvelle ligne"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Activation de reCAPTCHA"
recaptchaSiteKey: "Clé du site"
recaptchaSecretKey: "Clé secrète"
antennas: "Antenne"
manageAntennas: "Gestion d'antenne"
name: "Nom"
antennaSource: "Recevoir la source"
antennaKeywords: "Mots clés entrants"
antennaKeywordsDescription: "Lorsqu'il est séparé par un espace, il devient une spécification ET, et lorsqu'il est séparé par un saut de ligne, il devient une spécification OU."
notifyAntenna: "Notifier les nouvelles notes"
withFileAntenna: "Notes uniquement avec fichiers joints"
serviceworker: "ServiceWorker"
enableServiceworker: "Activer ServiceWorker"
antennaUsersDescription: "Spécifiez les noms d'utilisateurs séparés par des sauts de ligne"
caseSensitive: "Sensible à la casse"
withReplies: "Y compris répondres"
connectedTo: "Vous êtes connectés aux services suivants"
notesAndReplies: "Notes et Répondres"
withFiles: "Avec fichiers joints"
silence: "Mettre en masquer"
silenceConfirm: "Mettre l'utilisateur sous masquer ?"
unsilenceConfirm: "Voulez-vous annuler le masquer ?"
popularUsers: "Utilisateur·rice·s populaires"
recentlyUpdatedUsers: "Utilisateur·rice·s actif·ve·s récemment"
recentlyRegisteredUsers: "Utilisateur·rice·s récemment enregistrés"
recentlyDiscoveredUsers: "Utilisateur·rice·s récemment découverts"
exploreUsersCount: "Il y a {count} utilisateur·rice·s"
exploreFediverse: "Explorer le Fédiverse"
popularTags: "Mots-clés populaires"
userList: "Listes"
about: "Informations"
aboutMisskey: "À propos de Misskey"
aboutMisskeyText: "Misskey est un logiciel open source, développé par syuilo depuis 2014."
misskeyMembers: "Il est développé et maintenu par les membres répertoriés ici:"
misskeySource: "Le code source est disponible ici:"
misskeyTranslation: "Aidez-nous avec votre contribution à traduire Misskey:"
misskeyDonate: "Vous pouvez contribuer au développement de Misskey en faisant un don ici:"
morePatrons: "Nous apprécions vraiment le soutien de nombreux autres les soutiens non répertoriés ici. Merci beaucoup à tous! 🥰"
patrons: "Supporteurs"
administrator: "Administrateur"
token: "Jeton"
twoStepAuthentication: "Authentification à deux facteurs"
moderator: "Modérateurs"
nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s"
securityKey: "Clé de sécurité"
securityKeyName: "Nom de la clé"
registerSecurityKey: "Sinscrire la clé de sécurité"
lastUsed: "Dernier utilisé"
unregister: "Se désinscrire"
passwordLessLogin: "Connectez-vous sans mot de passe"
resetPassword: "Réinitialiser mot de passe"
newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
post: "Notes"
posted: "Publié !"
autoReloadWhenDisconnected: "Rechargement automatique lorsque le serveur se déconnecte"
autoNoteWatch: "Surveiller automatique pour les notes"
autoNoteWatchDescription: "Soyez informé des notes auxquelles vous avez réagi ou répondu."
reduceUiAnimation: "Réduire l'animation de l'interface"
share: "Partager"
notFound: "Non trouvé"
notFoundDescription: "Aucune page ne correspond à l'URL spécifiée."
uploadFolder: "Emplacement de téléversement par défaut"
cacheClear: "Vider le cache"
markAsReadAllNotifications: "Marquer toutes les notifications comme lues"
markAsReadAllUnreadNotes: "Marquer toutes les notes comme lues"
markAsReadAllTalkMessages: "Marquer toutes les discutez comme lues"
help: "Aide"
inputMessageHere: "Tapez ici votre message"
close: "Fermer"
group: "Groupe"
groups: "Groupes"
createGroup: "Créer un groupe"
ownedGroups: "Groupe propriétaire"
joinedGroups: "Membre dans les groupes"
invites: "Inviter"
groupName: "Nom du groupe"
members: "Membres"
transfer: "Transférer"
messagingWithUser: "Discutez avec les utilisateurs"
messagingWithGroup: "Discuter en groupe"
title: "Titre"
text: "Texte"
enable: "Activer"
next: "Suivant"
retype: "Retapez"
noteOf: "{user} notes"
inviteToGroup: "Inviter au groupe"
maxNoteTextLength: "Limite de note caractères"
quoteAttached: "Avec citation"
quoteQuestion: "Souhaitez-vous ajoutez une citation ?"
noMessagesYet: "Pas encore discuté"
newMessageExists: "Vous avez un nouveau message"
onlyOneFileCanBeAttached: "Vous ne pouvez joindre qu'un seul fichier au message"
signinRequired: "Veuillez vous connecter"
invitationCode: "Code dinvitation"
checking: "Vérification"
available: "Disponible"
unavailable: "Non disponible"
usernameInvalidFormat: "Vous pouvez utiliser des lettres, des nombres et _"
tooShort: "Est trop court"
tooLong: "Est trop long"
weakPassword: "Faible mot de passe"
normalPassword: "Bon mot de passe"
strongPassword: "Fort mot de passe"
passwordMatched: "Correcte"
passwordNotMatched: "Ne correspond pas"
signinWith: "Connectez-vous avec {x}"
tapSecurityKey: "Touchez la clé de sécurité"
or: "OU"
uiLanguage: "Langue d'affichage de l'interface"
groupInvited: "Invité au groupe"
aboutX: "À propos de {x}"
useOsNativeEmojis: "Utilisez les emojis natifs de la plateforme"
noGroups: "Pas de groupes"
joinOrCreateGroup: "Soyez invité à rejoindre les groupes ou vous pouvez créer votre propre groupe."
noHistory: "Pas d'historique"
disableAnimatedMfm: "Désactiver MFM qui a des animations"
doing: "Attends une seconde"
category: "Catégories"
tags: "Étiquettes"
docSource: "Source de ce document"
createAccount: "Créer compte"
existingAcount: "Comptes existants"
regenerate: "Régénérer"
fontSize: "Taille de la police"
noFollowRequests: "Vous n'avez aucune demandes d'abonnement en attente"
openImageInNewTab: "Ouvrir l'image dans un nouvel onglet"
dashboard: "Tableau de bord"
local: "Local"
remote: "Distant"
total: "Total"
weekOverWeekChanges: "Diff hebdo"
dayOverDayChanges: "Diff quotidien"
accessibility: "Accessibilité"
clinetSettings: "Paramètres du client"
accountSettings: "Paramètres du compte"
promotion: "Promu"
promote: "Promouvoir"
numberOfDays: "Nombre de jours"
hideThisNote: "Masquer cette note"
showFeaturedNotesInTimeline: "Afficher les notes en vedette dans Fil d'actualité"
objectStorage: "Stockage d'objets"
useObjectStorage: "Utiliser le stockage d'objets"
_ago:
unknown: "Inconnu"
future: "Futur"
justNow: "à linstant"
secondsAgo: "Il y a {n}s"
minutesAgo: "Il y a {n}min"
hoursAgo: "Il y a {n} heures"
daysAgo: "Il y a {n} jours"
weeksAgo: "Il y a {n} semaines"
monthsAgo: "Il y a {n} mois"
yearsAgo: "Il y a {n} ans"
_time:
second: "s"
minute: "min"
hour: "h"
day: "j"
_tutorial:
title: "Comment utiliser Misskey"
step1_1: "Bienvenue,"
_2fa:
alreadyRegistered: "Cette étape à déjà été complétée"
registerDevice: "Sinscrire l'appareil"
registerKey: "Sinscrire la clé"
step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil."
step2: "Ensuite, scannez le code QR affiché avec l'application."
step3: "Entrez le jeton affiché sur l'application et vous avez terminé."
step4: "Lorsque vous vous connectez, entrez le jeton de la même manière."
securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion avec non seulement la clé de sécurité matérielle qui prend en charge FIDO2, mais également l'authentification par empreinte digitale ou PIN sur votre appareil."
_permissions:
"read:account": "Afficher les informations du compte"
"write:account": "Mettre à jour les informations de votre compte"
"read:blocks": "Voir les blocs"
"write:blocks": "Écrire des blocs"
"read:drive": "Parcourir le Drive"
"write:drive": "Écrire sur le Drive"
"read:favorites": "Afficher les favoris"
"write:favorites": "Écrire des favoris"
"read:following": "Voir les informations de l'abonné"
"write:following": "Abonnements/Se désabonner"
"read:messaging": "Cherche à discuter"
"write:messaging": "Contrôler le discuter"
"read:mutes": "Voir les comptes muets"
"write:mutes": "Gérer les comptes muets"
"write:notes": "Créer / supprimer des notes"
"read:notifications": "Afficher les notifications"
"write:notifications": "Gérer vos notifications"
"read:reactions": "Lire les réactions"
"write:reactions": "Gérer vos réactions"
"write:votes": "Voter"
"read:pages": "Afficher la page"
"write:pages": "Mettre à jour les Pages"
"read:page-likes": "Lire les favoris sur les Pages"
"write:page-likes": "Mettre à jour les favoris sur les Pages"
"read:user-groups": "Voir les groupes d'utilisateur·rice·s"
"write:user-groups": "Éditer les groupes des utilisateur·rice·s"
_auth:
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
permissionAsk: "Cette application nécessite les autorisations suivantes "
_antennaSources:
all: "Toutes les notes"
homeTimeline: "Notes de l'utilisateur auquel je m'abonne"
users: "Notes des un ou plusieurs utilisateurs spécifiés"
userList: "Notes pour les utilisateurs de la liste spécifiée"
userGroup: "Notes pour les utilisateurs de la groupe spécifiée"
_weekday:
sunday: "Dimanche"
monday: "Lundi"
tuesday: "Mardi"
wednesday: "Mercredi"
thursday: "Jeudi"
friday: "Vendredi"
saturday: "Samedi"
_widgets:
memo: "Note collante"
notifications: "Notifications"
timeline: "Fil d'actualité"
calendar: "Calendrier"
trends: "Tendances"
clock: "Horloge"
rss: "Lecteur de flux RSS"
activity: "Activités"
_cw:
hide: "Masquer"
show: "Voir plus"
chars: "{count} caractères"
files: "{count} fichiers"
poll: "Sondage"
_poll:
noOnlyOneChoice: "Au moins 2 réponses nécéssaires"
choiceN: "Choix {n}"
noMore: "Vous ne pouvez pas en ajouter davantage"
canMultipleVote: "Autoriser le multi-choix"
expiration: "Fin du sondage"
infinite: "Illimité"
at: "Choisir une date"
after: "Chosir une durée"
deadlineDate: "Date de fin"
deadlineTime: "Heure de fin"
duration: "Durée"
votesCount: "{n} votes"
totalVotes: "{n} votes au total"
vote: "Voter"
showResult: "Voir les résultats"
voted: "Déjà voté"
closed: "Terminé"
remainingDays: "{d} jours, {h} heures restantes"
remainingHours: "{h} heures et {m} minutes restantes"
remainingMinutes: "{m} minutes et {s} secondes restantes"
remainingSeconds: "{s} secondes restantes"
_visibility:
public: "Public"
publicDescription: "Publier à tou·te·s les utilisateur·rice·s"
home: "Principal"
homeDescription: "Publier sur le fil principal uniquement"
followers: "Abonné·e·s"
followersDescription: "Publier à vos abonné·e·s uniquement"
specified: "Direct"
specifiedDescription: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
localOnly: "Local seulement"
_postForm:
replyPlaceholder: "Répondre à cette note ..."
quotePlaceholder: "Citez cette note ..."
_placeholders:
a: "Qu'est-ce qu'il se passe ?"
b: "Quoi de neuf ?"
c: "Quavez-vous en tête ?"
d: "Désirez-vous publier quelques mots ?"
e: "Écrivez ici"
f: "En attente de vos écrits ..."
_profile:
name: "Nom"
username: "Nom d'utilisateur·rice"
description: "À propos de moi"
youCanIncludeHashtags: "Vous pouvez également inclure des hashtags."
metadata: "Informations complémentaires"
metadataLabel: "Étiquette"
metadataContent: "Contenu"
_exportOrImport:
allNotes: "Toutes les notes"
followingList: "Abonnements"
muteList: "Mettre en sourdine"
blockingList: "Bloquer"
userLists: "Listes"
_charts:
federationInstancesIncDec: "Variation du nombre d'instances"
federationInstancesTotal: "Nombre d'instances au total"
usersIncDec: "Variation du nombre d'utilisateur·rice·s"
usersTotal: "Nombre d'utilsateur·rice·s au total"
activeUsers: "Utilisateur·rice·s actif·ve·s"
notesIncDec: "Variation du nombre d'notes"
localNotesIncDec: "Variation du nombre de notes local"
remoteNotesIncDec: "Variation du nombre dnotes distant"
notesTotal: "Nombre d'notes au total"
filesIncDec: "Variation du nombre de fichiers"
filesTotal: "Nombre de fichiers au total"
storageUsageIncDec: "Variation de l'utilisation du stockage"
storageUsageTotal: "Utilisation totale du stockage"
_instanceCharts:
requests: "Requêtes"
users: "Variation du nombre d'utilisateur·rice·s"
usersTotal: "Somme du nombre d'utilisateur·rice·s accumulés"
notes: "Variation du nombre d'notes"
notesTotal: "Somme du nombre dnotes accumulés"
ff: "Variation des abonné·e·s"
ffTotal: "Somme du nombre d'abonnements accumulés"
cacheSize: "Variation de la taille du cache"
cacheSizeTotal: "Somme de la taille du cache accumulé"
files: "Variation du nombre de fichiers"
filesTotal: "Somme du nombre de fichiers accumulés"
_timelines:
home: "Principal"
local: "Local"
social: "Social"
global: "Global"
_pages:
newPage: "Créer une page"
editPage: "Modifier une page"
readPage: "Voir la source"
page-created: "Page a été créée !"
page-updated: "A mis à jour la page"
name-already-exists: "Une page portant le même nom existe déjà"
title-invalid-name: "LURL de la page spécifiée nest pas valide"
text-invalid-name: "Assurez-vous qu'il n'est pas vide"
editThisPage: "Éditer cette page"
viewSource: "Afficher la source"
viewPage: "Afficher la page"
like: "Favori"
unlike: "Je nfavoris pas"
liked-pages: "Pages favoris"
my-pages: "Mes pages"
inspector: "Inspecteur"
content: "Bloc de page"
variables: "Variables"
variables-info: "Vous pouvez créer une page dynamique à l'aide de variables. En tapant le <b>{nom de variable}</b> dans le texte, vous pouvez y incorporer la valeur de la variable. Par exemple, si dans le texte <b>Bonjour {chose} monde!</b> la valeur de la variable (chose) est <b>ai</b>, le texte devient est <b>Bonjour ai monde!</b>."
variables-info2: "L'évaluation des variables (le calcul des valeurs) se fait de haut en bas, donc l'variable ne peut pas se référer à une autre qui est en dessous. Par exemple, lorsque les variables <b>A、B、C</b> sont définies, <b>C</b> peut faire référence à <b>A</b> ou <b>B</b>, mais <b>A</b> ne peut pas faire référence à <b>B</b> ou <b>C</b>."
variables-info3: "Pour recevoir une entrée utilisateur, ajoutez un bloc \"Entrée\" sur la page et définissez le nom des variables que vous souhaitez stocker dans le champ \"Nom de la variable\" (les variables seront créées automatiquement). Les actions seront exécutées en fonction de l'entrée utilisateur de ces variables."
variables-info4: "Les fonctions vous permettent d'organiser le processus de calcul des valeurs sous une forme réutilisable. Pour créer une fonction, créez une variable de type \"fonction\". Une fonction peut avoir un slot (argument) et sa valeur peut être utilisée comme variable dans la fonction. Il existe également une fonction qui prend une fonction comme argument dans la norme AiScript (appelée fonction d'ordre supérieur). En plus des fonctions prédéfinies, elles peuvent être définies instantanément dans ces emplacements de fonction d'ordre supérieur."
more-details: "Description"
title: "Titre"
url: "URL de page"
summary: "Résumé de page"
alignCenter: "Centrée"
hide-title-when-pinned: "Masquer le titre de la page lorsque celle-ci est épinglée au profil"
font: "Police de caractères"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
set-eye-catching-image: "Définir une image attirante"
remove-eye-catching-image: "Supprimer une image attirante"
chooseBlock: "Ajouter un bloc"
selectType: "Choisir un type"
enterVariableName: "Veuillez entrer un nom de variable"
the-variable-name-is-already-used: "Cette variable est déjà utilisée"
content-blocks: "Contenu"
input-blocks: "Entrée"
special-blocks: "Spécial"
post-from-post-form: "Publier ce contenu"
posted-from-post-form: "Publié !"
blocks:
text: "Texte"
textarea: "Zone de texte"
section: "Section"
image: "Images"
button: "Bouton"
if: "Si"
_if:
variable: "Variables"
post: "Formulaire à publier"
_post:
text: "Contenu"
textInput: "Entrée de textuelle"
_textInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
textareaInput: "Entrée de textuelle multiligne"
_textareaInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
numberInput: "Entrée numérique"
_numberInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
switch: "Basculer"
_switch:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
counter: "Compteur"
_counter:
name: "Nom de la variable"
text: "Titre"
inc: "Augmenter le chiffre"
_button:
text: "Titre"
colored: "Coloré"
action: "L'opération lorsque le bouton sera pressé"
_action:
dialog: "Afficher une fenêtre de dialogue"
_dialog:
content: "Contenu"
resetRandom: "Réinitialiser le nombre aléatoire"
pushEvent: "Envoyer un évènement"
_pushEvent:
event: "Nom de l'évènement"
message: "Message à afficher lorsque appuyé"
variable: "Variable à envoyer"
no-variable: "Rien"
radioButton: "Choix"
_radioButton:
name: "Nom de la variable"
title: "Titre"
values: "Choix séparés par des sauts de ligne"
default: "Valeur par défaut"
script:
categories:
flow: "Contrôle"
logical: "Opération logique"
operation: "Calculer"
comparison: "Comparer"
random: "Aléatoire"
value: "Valeur"
fn: "Fonction"
text: "Manipulation de texte"
convert: "Convertir"
list: "Listes"
blocks:
text: "Texte"
multiLineText: "Texte (Multi-lignes)"
textList: "Liste de texte"
_textList:
info: "Veuillez séparer chacun avec une nouvelle ligne"
strLen: "Longueur d'un texte"
_strLen:
arg1: "Texte"
strPick: "Extraire un caractère"
_strPick:
arg1: "Texte"
arg2: "Position du joueur"
strReplace: "Remplacement de texte"
_strReplace:
arg1: "Texte"
arg2: "Avant le remplacement"
arg3: "Après le remplacement"
strReverse: "Inverser le texte"
_strReverse:
arg1: "Texte"
join: "Concaténer du texte"
_join:
arg1: "Listes"
arg2: "Séparateur"
add: "Ajouter"
_add:
arg1: "A"
arg2: "B"
subtract: "Soustraire"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiplier par"
_multiply:
arg1: "A"
arg2: "B"
divide: "Diviser par"
_divide:
arg1: "A"
arg2: "B"
mod: "Reste"
_mod:
arg1: "A"
arg2: "B"
round: "Décimal rond"
_round:
arg1: "Numérique"
eq: "A et B sont équivalents"
_eq:
arg1: "A"
arg2: "B"
notEq: "A et B sont différents"
_notEq:
arg1: "A"
arg2: "B"
and: "A et B"
_and:
arg1: "A"
arg2: "B"
or: "A ou B"
_or:
arg1: "A"
arg2: "B"
lt: "A est plus petit que B"
_lt:
arg1: "A"
arg2: "B"
gt: "A est supérieur à B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "A est plus petit ou égal à B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: "A est supérieur ou égal à B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Branche"
_if:
arg1: "Si"
arg2: "Si"
arg3: "Sinon"
not: "Nier"
_not:
arg1: "Nier"
random: "Aléatoire"
_random:
arg1: "Probabilité"
rannum: "Nombre aléatoire"
_rannum:
arg1: "Minimum"
arg2: "Maximum"
randomPick: "Sélectionner au hasard dans la liste"
_randomPick:
arg1: "Listes"
dailyRandom: "Aléatoire (Quotidien pour chaque utilisateur)"
_dailyRandom:
arg1: "Probabilité"
dailyRannum: "Numéros aléatoires (Quotidien pour chaque utilisateur)"
_dailyRannum:
arg1: "Minimum"
arg2: "Maximum"
dailyRandomPick: "Sélectionné au hasard dans la liste (Quotidien pour chaque utilisateur)"
_dailyRandomPick:
arg1: "Listes"
seedRandom: "Aléatoire (graine)"
_seedRandom:
arg1: "Graine"
arg2: "Probabilité"
seedRannum: "Nombre aléatoire (Graine)"
_seedRannum:
arg1: "Graine"
arg2: "Minimum"
arg3: "Maximum"
seedRandomPick: "Sélectionné au hasard dans la liste (graine)"
_seedRandomPick:
arg1: "Graine"
arg2: "Listes"
DRPWPM: "Sélectionné au hasard dans une liste de probabilités (Quotidien pour chaque utilisateur)"
_DRPWPM:
arg1: "Liste de texte"
pick: "Sélectionner dans la liste"
_pick:
arg1: "Listes"
arg2: "Position"
listLen: "Longueur de la liste"
_listLen:
arg1: "Listes"
number: "Numérique"
stringToNumber: "Convertir du texte en numérique"
_stringToNumber:
arg1: "Texte"
numberToString: "Convertir du numérique en texte"
_numberToString:
arg1: "Numérique"
splitStrByLine: "Séparer le texte par lignes"
_splitStrByLine:
arg1: "Texte"
ref: "Variables"
fn: "Fonction"
_fn:
slots: "Slots"
slots-info: "Veuillez délimiter chaque slot par un saut de ligne"
arg1: "Sortie"
for: "Répéter"
_for:
arg1: "Compter"
arg2: "Action"
typeError: "Le slot {slot} accepte \"{expect}\" mais a \"{actual}\" !"
thereIsEmptySlot: "Slot {slot} est vide !"
types:
string: "Texte"
number: "Numérique"
boolean: "Marqueur"
array: "Listes"
stringArray: "Liste de texte"
emptySlot: "Slot vide"
enviromentVariables: "Variables d'environnement"
pageVariables: "Élément de page"
argVariables: "Entrée slot"

View File

@ -19,13 +19,13 @@ const languages = [
//'de-DE',
'en-US',
'es-ES',
//'fr-FR',
'fr-FR',
'ja-JP',
//'ja-KS',
'ja-KS',
'ko-KR',
//'nl-NL',
//'pl-PL',
//'zh-CN',
'zh-CN',
//'zh-TW',
];

View File

@ -1,20 +1,4 @@
_ago:
unknown: "謎"
future: "未来"
justNow: "たった今"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分前"
hoursAgo: "{n}時間前"
daysAgo: "{n}日前"
weeksAgo: "{n}週間前"
monthsAgo: "{n}ヶ月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分"
hour: "時間"
day: "日"
_lang_: "日本語"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスです。\n「ート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
monthAndDay: "{month}月 {day}日"
@ -28,7 +12,7 @@ gotIt: "わかった"
cancel: "キャンセル"
enterUsername: "ユーザー名を入力"
renotedBy: "{user}がRenote"
noNotes: "投稿はありません"
noNotes: "ノートはありません"
noNotifications: "通知はありません"
instance: "インスタンス"
settings: "設定"
@ -66,13 +50,14 @@ import: "インポート"
export: "エクスポート"
files: "ファイル"
download: "ダウンロード"
driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを添付した投稿も消えます。"
driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを添付したノートも消えます。"
unfollowConfirm: "{name}のフォローを解除しますか?"
exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。"
importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。"
lists: "リスト"
noLists: "リストはありません"
notes: "投稿"
note: "ノート"
notes: "ノート"
following: "フォロー"
followers: "フォロワー"
followsYou: "フォローされています"
@ -81,8 +66,6 @@ manageLists: "リストの管理"
error: "問題が発生しました"
retry: "再試行"
enterListName: "リスト名を入力"
renameList: "リスト名を変更"
deleteList: "リストを削除"
privacy: "プライバシー"
makeFollowManuallyApprove: "フォローを承認制にする"
defaultNoteVisibility: "デフォルトの公開範囲"
@ -95,7 +78,7 @@ enterEmoji: "絵文字を入力"
renote: "Renote"
unrenote: "Renote解除"
quote: "引用"
pinnedNote: "ピン留めされた投稿"
pinnedNote: "ピン留めされたノート"
you: "あなた"
clickToShow: "クリックして表示"
sensitive: "閲覧注意"
@ -179,7 +162,7 @@ mutedUsers: "ミュートしたユーザー"
blockedUsers: "ブロックしたユーザー"
noUsers: "ユーザーはいません"
editProfile: "プロフィールを編集"
noteDeleteConfirm: "この投稿を削除しますか?"
noteDeleteConfirm: "このノートを削除しますか?"
pinLimitExceeded: "これ以上ピン留めできません"
intro: "Misskeyのインストールが完了しました管理者アカウントを作成しましょう。"
done: "完了"
@ -216,18 +199,16 @@ remove: "削除"
removed: "削除しました"
removeAreYouSure: "「{x}」を削除しますか?"
saved: "保存しました"
messaging: "トーク"
messaging: "チャット"
upload: "アップロード"
fromDrive: "ドライブから"
fromUrl: "URLから"
editWidgets: "ウィジェットを編集"
exitEdit: "編集を終了"
explore: "みつける"
games: "Misskey Games"
messageRead: "既読"
recentUsedEmojis: "最近使用した絵文字"
noMoreHistory: "これより過去の履歴はありません"
startMessaging: "トークを開始"
startMessaging: "チャットを開始"
nUsersRead: "{n}人が読みました"
agreeTo: "{0}に同意"
tos: "利用規約"
@ -304,8 +285,8 @@ name: "名前"
antennaSource: "受信ソース"
antennaKeywords: "受信キーワード"
antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
notifyAntenna: "新しい投稿を通知する"
withFileAntenna: "ファイルが添付された投稿のみ"
notifyAntenna: "新しいノートを通知する"
withFileAntenna: "ファイルが添付されたノートのみ"
serviceworker: "ServiceWorker"
enableServiceworker: "ServiceWorkerを有効にする"
antennaUsersDescription: "ユーザー名を改行で区切って指定します"
@ -330,6 +311,7 @@ aboutMisskey: "Misskeyについて"
aboutMisskeyText: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。"
misskeyMembers: "現在以下のメンバーによって開発・メンテナンスされています:"
misskeySource: "ソースコードはここで公開されています:"
misskeyTranslation: "Misskeyの翻訳にご協力をお願いします:"
misskeyDonate: "Misskeyに寄付をして開発をサポートできます:"
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰"
patrons: "支援者"
@ -348,14 +330,149 @@ resetPassword: "パスワードをリセット"
newPasswordIs: "新しいパスワードは「{password}」です"
post: "投稿"
posted: "投稿しました"
autoReloadWhenDisconnected: "サーバー切断時に自動リロード"
autoNoteWatch: "ノートの自動ウォッチ"
autoNoteWatchDescription: "あなたがリアクションしたり返信したりした他のユーザーのノートに関する通知を受け取るようにします。"
reduceUiAnimation: "UIのアニメーションを減らす"
share: "共有"
notFound: "見つかりません"
notFoundDescription: "指定されたURLに該当するページはありませんでした。"
uploadFolder: "既定アップロード先"
cacheClear: "キャッシュを削除"
markAsReadAllNotifications: "すべての通知を既読にする"
markAsReadAllUnreadNotes: "すべての投稿を既読にする"
markAsReadAllTalkMessages: "すべてのチャットを既読にする"
help: "ヘルプ"
inputMessageHere: "ここにメッセージを入力"
close: "閉じる"
group: "グループ"
groups: "グループ"
createGroup: "グループを作成"
ownedGroups: "所有グループ"
joinedGroups: "参加しているグループ"
invites: "招待"
groupName: "グループ名"
members: "メンバー"
transfer: "譲渡"
messagingWithUser: "ユーザーとチャット"
messagingWithGroup: "グループでチャット"
title: "タイトル"
text: "テキスト"
enable: "有効にする"
next: "次"
retype: "再入力"
noteOf: "{user}のノート"
inviteToGroup: "グループに招待"
maxNoteTextLength: "ノートの文字数制限"
quoteAttached: "引用付き"
quoteQuestion: "引用として添付しますか?"
noMessagesYet: "まだチャットはありません"
newMessageExists: "新しいメッセージがあります"
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
signinRequired: "ログインしてください"
invitationCode: "招待コード"
checking: "確認しています"
available: "利用できます"
unavailable: "利用できません"
usernameInvalidFormat: "a~z、A~Z、0~9、_が使えます"
tooShort: "短すぎます"
tooLong: "長すぎます"
weakPassword: "弱いパスワード"
normalPassword: "普通のパスワード"
strongPassword: "強いパスワード"
passwordMatched: "一致しました"
passwordNotMatched: "一致していません"
signinWith: "{x}でログイン"
tapSecurityKey: "セキュリティーキーにタッチ"
or: "もしくは"
uiLanguage: "UIの表示言語"
groupInvited: "グループに招待されました"
aboutX: "{x}について"
useOsNativeEmojis: "OSネイティブの絵文字を使用"
noGroups: "グループがありません"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
noHistory: "履歴はありません"
disableAnimatedMfm: "動きのあるMFMを無効にする"
doing: "やっています"
category: "カテゴリ"
tags: "タグ"
docSource: "このドキュメントのソース"
createAccount: "アカウントを作成"
existingAcount: "既存のアカウント"
regenerate: "再生成"
fontSize: "フォントサイズ"
noFollowRequests: "フォロー申請はありません"
openImageInNewTab: "画像を新しいタブで開く"
dashboard: "ダッシュボード"
local: "ローカル"
remote: "リモート"
total: "合計"
weekOverWeekChanges: "前週比"
dayOverDayChanges: "前日比"
accessibility: "アクセシビリティ"
clinetSettings: "クライアント設定"
accountSettings: "アカウント設定"
promotion: "プロモーション"
promote: "プロモート"
numberOfDays: "日数"
hideThisNote: "このノートを非表示"
showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示する"
objectStorage: "オブジェクトストレージ"
useObjectStorage: "オブジェクトストレージを使用"
serverLogs: "サーバーログ"
deleteAll: "全て削除"
_ago:
unknown: "謎"
future: "未来"
justNow: "たった今"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分前"
hoursAgo: "{n}時間前"
daysAgo: "{n}日前"
weeksAgo: "{n}週間前"
monthsAgo: "{n}ヶ月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分"
hour: "時間"
day: "日"
_tutorial:
title: "Misskeyの使い方"
step1_1: "ようこそ。"
step1_2: "この画面は「タイムライン」と呼ばれ、あなたや、あなたが「フォロー」する人の「ノート」が時系列で表示されます。"
step1_3: "あなたはまだ何もノートを投稿しておらず、誰もフォローしていないので、タイムラインには何も表示されていないはずです。"
step2_1: "ノートを作成したり誰かをフォローしたりする前に、まずあなたのプロフィールを完成させましょう。"
step2_2: "あなたがどんな人かわかると、多くの人にノートを見てもらえたり、フォローしてもらいやすくなります。"
step3_1: "プロフィール設定はうまくできましたか?"
step3_2: "では試しに、何かノートを投稿してみてください。画面上にある鉛筆マークのボタンを押すとフォームが開きます。"
step3_3: "内容を書いたら、フォーム右上のボタンを押すと投稿できます。"
step3_4: "内容が思いつかない「Misskey始めました」というのはいかがでしょう。"
step4_1: "投稿できましたか?"
step4_2: "あなたのノートがタイムラインに表示されていれば成功です。"
step5_1: "次は、他の人をフォローしてタイムラインを賑やかにしたいところです。"
step5_2: "{featured}で人気のノートが見れるので、その中から気になった人を選んでフォローしたり、{explore}で人気のユーザーを探すこともできます。"
step5_3: "ユーザーをフォローするには、ユーザーのアイコンをクリックしてユーザーページを表示し、「フォロー」ボタンを押します。"
step5_4: "ユーザーによっては、フォローが承認されるまで時間がかかる場合があります。"
step6_1: "タイムラインに他のユーザーのノートが表示されていれば成功です。"
step6_2: "他の人のノートには、「リアクション」を付けることができ、簡単にあなたの反応を伝えられます。"
step6_3: "リアクションを付けるには、ノートの「+」マークをクリックして、好きなリアクションを選択します。"
step7_1: "これで、Misskeyの基本的な使い方の説明は終わりました。お疲れ様でした。"
step7_2: "もっとMisskeyについて知りたいときは、{help}を見てみてください。"
step7_3: "では、Misskeyをお楽しみください🚀"
_2fa:
alreadyRegistered: "既に設定は完了しています。"
registerDevice: "デバイスを登録"
registerKey: "キーを登録"
step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。"
step2: "次に、表示されているQRコードをアプリでスキャンします。"
step3: "アプリに表示されているトークンを入力して完了です。"
step4: "これからログインするときも、同じようにトークンを入力します。"
securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーを使用してログインするように設定できます。"
securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーもしくは端末の指紋認証やPINを使用してログインするように設定できます。"
_permissions:
"read:account": "アカウントの情報を見る"
@ -368,11 +485,11 @@ _permissions:
"write:favorites": "お気に入りを操作する"
"read:following": "フォローの情報を見る"
"write:following": "フォロー・フォロー解除する"
"read:messaging": "トークを見る"
"write:messaging": "トークを操作する"
"read:messaging": "チャットを見る"
"write:messaging": "チャットを操作する"
"read:mutes": "ミュートを見る"
"write:mutes": "ミュートを操作する"
"write:notes": "投稿を作成・削除する"
"write:notes": "ノートを作成・削除する"
"read:notifications": "通知を見る"
"write:notifications": "通知を操作する"
"read:reactions": "リアクションを見る"
@ -390,10 +507,11 @@ _auth:
permissionAsk: "このアプリは次の権限を要求しています"
_antennaSources:
all: "全ての投稿"
homeTimeline: "フォローしているユーザーの投稿"
users: "指定した一人または複数のユーザーの投稿"
userList: "指定したリストのユーザーの投稿"
all: "全てのノート"
homeTimeline: "フォローしているユーザーのノート"
users: "指定した一人または複数のユーザーのノート"
userList: "指定したリストのユーザーのノート"
userGroup: "指定したグループのユーザーのノート"
_weekday:
sunday: "日曜日"
@ -411,6 +529,9 @@ _widgets:
calendar: "カレンダー"
trends: "トレンド"
clock: "時計"
rss: "RSSリーダー"
activity: "アクティビティ"
photos: "フォト"
_cw:
hide: "隠す"
@ -451,10 +572,11 @@ _visibility:
followersDescription: "自分のフォロワーのみに公開"
specified: "ダイレクト"
specifiedDescription: "指定したユーザーのみに公開"
localOnly: "ローカルのみ"
_postForm:
replyPlaceholder: "この投稿に返信..."
quotePlaceholder: "この投稿を引用..."
replyPlaceholder: "このノートに返信..."
quotePlaceholder: "このノートを引用..."
_placeholders:
a: "いまどうしてる?"
b: "何かありましたか?"
@ -473,7 +595,7 @@ _profile:
metadataContent: "内容"
_exportOrImport:
allNotes: "全ての投稿"
allNotes: "全てのノート"
followingList: "フォロー"
muteList: "ミュート"
blockingList: "ブロック"
@ -485,10 +607,10 @@ _charts:
usersIncDec: "ユーザーの増減"
usersTotal: "ユーザーの合計"
activeUsers: "アクティブユーザー数"
notesIncDec: "投稿の増減"
localNotesIncDec: "ローカルの投稿の増減"
remoteNotesIncDec: "リモートの投稿の増減"
notesTotal: "投稿の合計"
notesIncDec: "ノートの増減"
localNotesIncDec: "ローカルのノートの増減"
remoteNotesIncDec: "リモートのノートの増減"
notesTotal: "ノートの合計"
filesIncDec: "ファイルの増減"
filesTotal: "ファイルの合計"
storageUsageIncDec: "ストレージ使用量の増減"
@ -498,8 +620,8 @@ _instanceCharts:
requests: "リクエスト"
users: "ユーザーの増減"
usersTotal: "ユーザーの積算"
notes: "投稿の増減"
notesTotal: "投稿の積算"
notes: "ノートの増減"
notesTotal: "ノートの積算"
ff: "フォロー/フォロワーの増減"
ffTotal: "フォロー/フォロワーの積算"
cacheSize: "キャッシュサイズの増減"

View File

@ -1 +1,170 @@
---
_lang_: "日本語 (関西弁)"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ート」を作成しぃ、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素はよ反応を追加することもできます✌\n新しい世界を探検しよう🚀"
monthAndDay: "{month}月 {day}日"
search: "探す"
notifications: "通知"
username: "ユーザー名"
password: "パスワード"
fetchingAsApObject: "連合に照会中"
ok: "おっけー"
gotIt: "ほい"
cancel: "やめとくわ"
enterUsername: "ユーザー名を入れてや"
renotedBy: "{user}がRenote"
noNotes: "ノートはあらへん"
noNotifications: "通知はあらへん"
instance: "インスタンス"
settings: "設定"
profile: "プロフィール"
timeline: "タイムライン"
noAccountDescription: "自己紹介はあらへん"
login: "ログイン"
loggingIn: "ログインしとります"
logout: "ログアウト"
signup: "新規登録"
uploading: "アップロードしとります"
save: "保存"
users: "ユーザー"
addUser: "ユーザー増やす"
favorite: "お気に入り"
favorites: "お気に入り"
unfavorite: "お気に入りやめる"
pin: "ピン留め"
unpin: "ピン留めやめる"
copyContent: "内容をコピー"
copyLink: "リンクをコピー"
delete: "ほかす"
addToList: "リストに入れたる"
reply: "返す"
loadMore: "もっとあるやろ!"
mentions: "あんた宛て"
directNotes: "ダイレクト投稿"
import: "インポート"
export: "エクスポート"
files: "ファイル"
download: "ダウンロード"
lists: "リスト"
noLists: "リストはあらへん"
followsYou: "フォローされとるで"
error: "問題が発生してん"
enterListName: "リスト名を入れてや"
privacy: "プライバシーってなんや?オカンの年齢か?"
makeFollowManuallyApprove: "他人のフォローは許可してからや!"
defaultNoteVisibility: "もとからの公開範囲"
follow: "フォロー"
followRequest: "フォロー許してくれや!言うてみる"
followRequests: "フォロー許してくれや!"
followRequestPending: "フォロー許してくれるん待っとる"
enterEmoji: "絵文字を入れてや"
you: "あんた"
clickToShow: "押してみ、見せたるわ"
sensitive: "見たらあかんで"
add: "増やす"
reaction: "リアクション"
renameFile: "ファイル名をいらう"
attachCancel: "くっつけるのやめよか"
markAsSensitive: "ちょっと見せられへんわ"
unmarkAsSensitive: "別にええんじゃね?"
enterFileName: "ファイル名を入れてや"
mute: "ミュート"
unmute: "ミュートやめたる"
block: "ブロック"
unblock: "ブロックやめたる"
suspend: "凍結"
unsuspend: "溶かす"
customEmojis: "カスタム絵文字"
cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定をチャラにすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されへんので通信量が増加します。"
loginFailed: "ログインに失敗してん"
wallpaper: "壁紙"
removeWallpaper: "壁紙ほかす"
youHaveNoLists: "リストはあらへん"
proxyAccountDescription: "プロキシアカウントは、代わりにフォローしてくれるアカウントや。例えば、551に豚まんが無いときやったり、ユーザーがリモートユーザーをアカウントに入れたとき、リストに入れられたユーザーが誰からもフォローされてないと寂しいやん。寂しいし、アクティビティも配達されへんから、プロキシアカウントがフォローしてくれるで。ええやつやん…"
host: "ホスト"
federation: "連合"
instances: "インスタンス"
charts: "チャート"
perHour: "1時間ごと"
perDay: "1日ごと"
operations: "操作"
version: "バージョン"
network: "ネットワーク"
statistics: "統計"
clearQueueConfirmText: "未配達の投稿は配送されなくなるで。通常この操作を行う必要はあらへんや。"
muteAndBlock: "ミュートとブロック"
noUsers: "ユーザーはおらへん"
pinLimitExceeded: "これ以上ピン留めできひん"
intro: "Misskeyのインストールが完了してん管理者アカウントを作ってや。"
noCustomEmojis: "絵文字はあらへん"
noJobs: "ジョブはあらへん"
all: "みな"
retypedNotMatch: "そやないねん。"
remove: "ほかす"
noMoreHistory: "これより過去の履歴はあらへんで"
nsfw: "見たらあかんで"
userList: "リスト"
about: "情報"
aboutMisskey: "Misskeyってなんや"
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
close: "さいなら"
joinedGroups: "参加しとるグループ"
_ago:
unknown: "謎"
future: "未来"
justNow: "たった今"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分前"
hoursAgo: "{n}時間前"
daysAgo: "{n}日前"
weeksAgo: "{n}週間前"
monthsAgo: "{n}ヶ月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分"
hour: "時間"
day: "日"
_2fa:
alreadyRegistered: "もう設定終わっとるわ"
_auth:
permissionAsk: "このアプリは次の権限を要求しとるで"
_antennaSources:
all: "みなのノート"
homeTimeline: "フォローしとるユーザーのノート"
_widgets:
notifications: "通知"
timeline: "タイムライン"
_cw:
show: "もっとあるやろ!"
_poll:
noMore: "これ以上追加でけへん"
deadlineTime: "時間"
_visibility:
publicDescription: "みなのユーザーに公開"
_profile:
username: "ユーザー名"
_exportOrImport:
allNotes: "全てのノート"
muteList: "ミュート"
blockingList: "ブロック"
userLists: "リスト"
_pages:
script:
categories:
list: "リスト"
blocks:
_join:
arg1: "リスト"
_randomPick:
arg1: "リスト"
_dailyRandomPick:
arg1: "リスト"
_seedRandomPick:
arg2: "リスト"
_pick:
arg1: "リスト"
_listLen:
arg1: "リスト"
types:
array: "リスト"

34
locales/kn-IN.yml Normal file
View File

@ -0,0 +1,34 @@
---
_lang_: "ಕನ್ನಡ"
introMisskey: "ಸ್ವಾಗತ! Misskey ಓಪನ್ ಸೋರ್ಸ್ ಒಕ್ಕೂಟ ಮೈಕ್ರೋಬ್ಲಾಗಿಂಗ್ ಸೇವೆಯಾಗಿದೆ.\n ಏನಾಗುತ್ತಿದೆ ಎಂಬುದನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಅಥವಾ ನಿಮ್ಮ ಬಗ್ಗೆ ಎಲ್ಲರಿಗೂ ಹೇಳಲು \"ಟಿಪ್ಪಣಿ\"ಗಳನ್ನು ರಚಿಸಿ📡\n \"ಸ್ಪಂದನೆ\" ಕ್ರಿಯೆಯೊಂದಿಗೆ, ನೀವು ಎಲ್ಲರ ಟಿಪ್ಪಣಿಗಳಿಗೆ ತ್ವರಿತವಾಗಿ ಸ್ಪಂದನೆಗಳನ್ನು ಕೂಡ ಸೇರಿಸಬಹುದು.👍\n ಹೊಸ ಜಗತ್ತನ್ನು ಅನ್ವೇಷಿಸಿ🚀"
monthAndDay: "{month}ನೇ ತಿಂಗಳ {day}ನೇ ದಿನ"
search: "ಹುಡುಕು"
notifications: "ಅಧಿಸೂಚನೆಗಳು"
username: "ಬಳಕೆಹೆಸರು"
password: "ಗುಪ್ತಪದ"
fetchingAsApObject: "ಒಕ್ಕೂಟದಿಂದ ಪಡೆಯಲಾಗುತ್ತಿದೆ..."
ok: "ಸರಿ"
gotIt: "ಅರ್ಥವಾಯಿತು!"
cancel: "ರದ್ದು"
enterUsername: "ಬಳಕೆಹೆಸರನ್ನು ಭರ್ತಿ ಮಾಡಿ"
renotedBy: "{user} ಪುನರಾವರ್ತಿಸಿದರು"
noNotes: "ಟಿಪ್ಪಣಿಗಳಿಲ್ಲ"
noNotifications: "ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"
instance: "ನಿದರ್ಶನ"
settings: "ಸಿದ್ಧತೆಗಳು"
profile: "ಪ್ರೊಫೈಲು"
timeline: "ಸಮಯಸಾಲು"
noAccountDescription: "ಇವರು ಸ್ವಯಂ ಪರಿಚಯ ರಚಿಸಿಲ್ಲ"
login: "ಪ್ರವೇಶ"
loggingIn: "ಪ್ರವೇಶಿಸುತ್ತಾ..."
logout: "ಆಚೆಗೆ"
signup: "ನೋಂದಣಿ"
uploading: "ಅಪ್‌ಲೋಡಾಗುತ್ತಿದೆ"
save: "ಉಳಿಸಿ"
users: "ಬಳಕೆದಾರ"
instances: "ನಿದರ್ಶನ"
_widgets:
notifications: "ಅಧಿಸೂಚನೆಗಳು"
timeline: "ಸಮಯಸಾಲು"
_profile:
username: "ಬಳಕೆಹೆಸರು"

View File

@ -1,20 +1,5 @@
---
_ago:
unknown: "알 수 없음"
future: "미래"
justNow: "방금 전"
secondsAgo: "{n}초 전"
minutesAgo: "{n}분 전"
hoursAgo: "{n}시간 전"
daysAgo: "{n}일 전"
weeksAgo: "{n}주 전"
monthsAgo: "{n}개월 전"
yearsAgo: "{n}년 전"
_time:
second: "초"
minute: "분"
hour: "시간"
day: "일"
_lang_: "한국어"
introMisskey: "환영합니다! Misskey 는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n\"노트\" 를 작성해서, 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n\"리액션\" 기능으로, 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀"
monthAndDay: "{month}월 {day}일"
search: "검색"
@ -26,8 +11,8 @@ ok: "OK"
gotIt: "알겠어요"
cancel: "취소"
enterUsername: "유저명 입력"
renotedBy: "{user}님이 리노트"
noNotes: "게시물이 없습니다"
renotedBy: "{user}님이 Renote"
noNotes: "노트가 없습니다"
noNotifications: "표시할 알림이 없습니다"
instance: "인스턴스"
settings: "설정"
@ -71,6 +56,7 @@ exportRequested: "내보내기를 요청하였습니다. 이 작업은 시간이
importRequested: "가져오기를 요청하였습니다. 이 작업에는 시간이 걸릴 수 있습니다."
lists: "리스트"
noLists: "리스트가 없습니다"
note: "노트"
notes: "노트"
following: "팔로잉"
followers: "팔로워"
@ -80,8 +66,6 @@ manageLists: "리스트 관리"
error: "오류가 발생했습니다"
retry: "다시 시도"
enterListName: "리스트 이름을 입력"
renameList: "리스트 이름 바꾸기"
deleteList: "리스트 삭제"
privacy: "프라이버시"
makeFollowManuallyApprove: "팔로우를 수동으로 승인"
defaultNoteVisibility: "기본 공개 범위"
@ -91,9 +75,10 @@ followRequests: "팔로우 요청"
unfollow: "팔로우 해제"
followRequestPending: "팔로우 허가 대기중"
enterEmoji: "이모지 입력"
renote: "리노트"
renote: "Renote"
unrenote: "Renote 취소"
quote: "인용"
pinnedNote: "고정 노트"
pinnedNote: "고정해놓은 노트"
you: "당신"
clickToShow: "클릭하여 보기"
sensitive: "열람주의"
@ -121,8 +106,8 @@ customEmojis: "커스텀 이모지"
emojiName: "이모지 이름"
emojiUrl: "이모지 URL"
addEmoji: "이모지 추가"
cacheRemoteFiles: "원격 파일을 캐시"
cacheRemoteFilesDescription: "이 설정을 해지하면 원격 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다."
cacheRemoteFiles: "리모트 파일을 캐시"
cacheRemoteFilesDescription: "이 설정을 해지하면 리모트 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다."
flagAsBot: "나는 봇입니다"
flagAsCat: "나는 고양이다냥"
autoAcceptFollowed: "팔로우 중인 유저로부터의 팔로우 요청을 자동 수락"
@ -169,7 +154,7 @@ clearQueue: "대기열 비우기"
clearQueueConfirmTitle: "대기열을 비우시겠습니까?"
clearQueueConfirmText: "대기열에 남아 있는 노트는 더이상 연합되지 않습니다. 보통의 경우 이 작업은 필요하지 않습니다."
clearCachedFiles: "캐시 비우기"
clearCachedFilesConfirm: "캐시된 원격 파일을 모두 삭제하시겠습니까?"
clearCachedFilesConfirm: "캐시된 리모트 파일을 모두 삭제하시겠습니까?"
blockedInstances: "차단된 인스턴스"
blockedInstancesDescription: "차단하려는 인스턴스의 호스트 이름을 줄바꿈으로 구분하여 설정합니다. 차단된 인스턴스는 이 인스턴스와 통신할 수 없게 됩니다."
muteAndBlock: "뮤트 및 차단"
@ -218,14 +203,12 @@ messaging: "대화"
upload: "업로드"
fromDrive: "드라이브에서"
fromUrl: "URL로부터"
editWidgets: "위젯 편집"
exitEdit: "편집 종료"
explore: "발견하기"
games: "Misskey Games"
messageRead: "읽음"
recentUsedEmojis: "최근에 사용한 이모지"
noMoreHistory: "이것보다 과거의 기록이 없습니다"
startMessaging: "대화 시작"
startMessaging: "대화 시작하기"
nUsersRead: "{n}명이 읽음"
agreeTo: "{0}에 동의"
tos: "이용 약관"
@ -257,8 +240,8 @@ banner: "배너"
nsfw: "열람주의"
disconnectedFromServer: "서버와의 연결이 끊어졌습니다"
reloadConfirm: "새로고침 하시겠습니까?"
watch: "알림 받기"
unwatch: "알림 받기 해제"
watch: "지켜보기"
unwatch: "지켜보기 해제"
accept: "허가"
reject: "거부"
instanceName: "인스턴스 이름"
@ -270,7 +253,7 @@ thisYear: "올해"
thisMonth: "이번 달"
today: "오늘"
dayX: "{day}일"
monthX: "{month}월"
monthX: "{month}월"
yearX: "{year}년"
pages: "페이지"
integration: "연동"
@ -280,10 +263,10 @@ enableLocalTimeline: "로컬 타임라인 활성화"
enableGlobalTimeline: "글로벌 타임라인 활성화"
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
registration: "등록"
enableRegistration: "신규 사용자 등록을 활성화"
enableRegistration: "신규 회원가입을 활성화"
invite: "초대"
proxyRemoteFiles: "원격 파일 프록시"
proxyRemoteFilesDescription: "이 설정을 활성화할 경우, 저장되지 않았거나 저장용량 초과로 삭제된 원격 파일을 로컬에서 프록시하여 썸네일을 생성하게 됩니다. 서버의 스토리지에는 영향을 주지 않습니다."
proxyRemoteFiles: "리모트 파일 프록시"
proxyRemoteFilesDescription: "이 설정을 활성화할 경우, 저장되지 않았거나 저장용량 초과로 삭제된 리모트 파일을 로컬에서 프록시하여 썸네일을 생성하게 됩니다. 서버의 스토리지에는 영향을 주지 않습니다."
driveCapacityPerLocalAccount: "로컬 유저 한 명당 드라이브 용량"
driveCapacityPerRemoteAccount: "리모트 유저 한 명당 드라이브 용량"
inMb: "메가바이트 단위"
@ -302,8 +285,8 @@ name: "이름"
antennaSource: "받을 소스"
antennaKeywords: "받을 키워드"
antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
notifyAntenna: "새로운 글이 게시될 때 알림 받기"
withFileAntenna: "파일이 첨부된 게시물만"
notifyAntenna: "새로운 노트를 알림"
withFileAntenna: "파일이 첨부된 노트만"
serviceworker: "ServiceWorker"
enableServiceworker: "ServiceWorker 사용"
antennaUsersDescription: "유저명을 한 줄에 한 명씩 적습니다"
@ -311,7 +294,7 @@ caseSensitive: "대소문자를 구분"
withReplies: "답글 포함"
connectedTo: "다음 계정에 연결되어 있습니다"
notesAndReplies: "글과 답글"
withFiles: "파일 첨부"
withFiles: "미디어"
silence: "사일런스"
silenceConfirm: "이 계정을 사일런스로 설정하시겠습니까?"
unsilenceConfirm: "이 계정의 사일런스를 해제하시겠습니까?"
@ -325,9 +308,10 @@ popularTags: "인기 태그"
userList: "리스트"
about: "정보"
aboutMisskey: "Misskey에 대하여"
aboutMisskeyText: "Misskey는 syuilo에 의해 2014년부터 개발 오픈 소스 소프트웨어입니다."
misskeyMembers: "현재는 아래 멤버들에 의해 개발 및 유지보수 되고 있습니다."
misskeySource: "소스 코드는 여기에서 보실 수 있습니다:"
aboutMisskeyText: "Misskey는 syuilo에 의해 2014년부터 개발되어 온 오픈소스 소프트웨어 입니다."
misskeyMembers: "현재는 아래 멤버들에 의해 개발 및 유지보수 되고 있습니다:"
misskeySource: "소스코드는 여기에 공개되어 있습니다:"
misskeyTranslation: "Misskey의 번역을 함께해 주시길 부탁드립니다:"
misskeyDonate: "Misskey에 기부하심으로써 개발에 도움을 주실 수 있습니다:"
morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
patrons: "후원자들"
@ -346,48 +330,179 @@ resetPassword: "비밀번호 재설정"
newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다"
post: "작성"
posted: "게시하였습니다"
autoReloadWhenDisconnected: "서버와의 연결이 끊기면 자동 새로고침"
autoNoteWatch: "노트를 자동으로 지켜보기"
autoNoteWatchDescription: "리액션하거나 답글을 남긴 다른 유저의 노트에 대한 알림을 받습니다."
reduceUiAnimation: "UI의 애니메이션을 줄이기"
share: "공유"
notFound: "찾을 수 없습니다"
notFoundDescription: "지정한 URL에 해당하는 페이지가 존재하지 않습니다."
uploadFolder: "기본 업로드 위치"
cacheClear: "캐시 지우기"
markAsReadAllNotifications: "모든 알림을 읽은 상태로 표시"
markAsReadAllUnreadNotes: "모든 글을 읽은 상태로 표시"
markAsReadAllTalkMessages: "모든 대화를 읽은 상태로 표시"
help: "도움말"
inputMessageHere: "여기에 메시지를 입력하세요"
close: "닫기"
group: "그룹"
groups: "그룹"
createGroup: "그룹 만들기"
ownedGroups: "소유 그룹"
joinedGroups: "참여중인 그룹"
invites: "초대"
groupName: "그룹명"
members: "멤버"
transfer: "양도"
messagingWithUser: "유저와 대화하기"
messagingWithGroup: "그룹끼리 대화하기"
title: "제목"
text: "텍스트"
enable: "사용"
next: "다음"
retype: "다시 입력"
noteOf: "{user}의 노트"
inviteToGroup: "그룹에 초대하기"
maxNoteTextLength: "노트의 문자 수 제한"
quoteAttached: "인용함"
quoteQuestion: "인용해서 작성하시겠습니까?"
noMessagesYet: "아직 대화가 없습니다"
newMessageExists: "새 메시지가 있습니다"
onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
signinRequired: "로그인 해주세요"
invitationCode: "초대 코드"
checking: "확인하는 중입니다"
available: "사용 가능합니다"
unavailable: "사용할 수 없습니다"
usernameInvalidFormat: "a~z, A~Z, 0-9, _를 사용할 수 있습니다"
tooShort: "너무 짧습니다"
tooLong: "너무 깁니다"
weakPassword: "약한 비밀번호"
normalPassword: "좋은 비밀번호"
strongPassword: "강한 비밀번호"
passwordMatched: "일치합니다"
passwordNotMatched: "일치하지 않습니다"
signinWith: "{x}로 로그인"
tapSecurityKey: "보안 키를 터치"
or: "혹은"
uiLanguage: "UI 표시 언어"
groupInvited: "그룹에 초대되었습니다"
aboutX: "{x}에 대하여"
useOsNativeEmojis: "OS 기본 이모지를 사용"
noGroups: "그룹이 없습니다"
joinOrCreateGroup: "다른 그룹의 초대를 받거나, 직접 새 그룹을 만들어 보세요."
noHistory: "기록이 없습니다"
disableAnimatedMfm: "움직임이 있는 MFM을 비활성화"
doing: "잠시만요"
category: "카테고리"
tags: "태그"
docSource: "이 문서의 소스"
createAccount: "계정 만들기"
existingAcount: "기존 계정"
regenerate: "다시 생성"
fontSize: "글자 크기"
noFollowRequests: "처리되지 않은 팔로우 요청이 없습니다"
openImageInNewTab: "새 탭에서 이미지 열기"
dashboard: "대시보드"
local: "로컬"
remote: "리모트"
total: "합계"
weekOverWeekChanges: "지난주보다"
dayOverDayChanges: "어제보다"
accessibility: "접근성"
clinetSettings: "클라이언트 설정"
accountSettings: "계정 설정"
promotion: "프로모션"
promote: "프로모션하기"
numberOfDays: "며칠동안"
hideThisNote: "이 노트를 숨기기"
showFeaturedNotesInTimeline: "타임라인에 추천 노트를 표시"
objectStorage: "오브젝트 스토리지"
useObjectStorage: "오브젝트 스토리지를 사용"
_ago:
unknown: "알 수 없음"
future: "미래"
justNow: "방금 전"
secondsAgo: "{n}초 전"
minutesAgo: "{n}분 전"
hoursAgo: "{n}시간 전"
daysAgo: "{n}일 전"
weeksAgo: "{n}주 전"
monthsAgo: "{n}개월 전"
yearsAgo: "{n}년 전"
_time:
second: "초"
minute: "분"
hour: "시간"
day: "일"
_tutorial:
title: "Misskey의 사용 방법"
step1_1: "환영합니다!"
step1_2: "이 페이지는 \"타임라인\"이라고 불립니다. 당신이 \"팔로우\"하고 있는 사람들의 \"노트\"가 시간순으로 나타납니다."
step1_3: "아직 아무 유저도 팔로우하고 있지 않기에 타임라인은 비어 있을 것입니다."
step2_1: "새 노트를 작성하거나 다른 사람을 팔로우하기 전에, 먼저 프로필을 완성해보도록 합시다."
step2_2: "당신이 어떤 사람인지를 알린다면, 다른 사람들이 당신을 팔로우할 확률이 올라갈 것입니다."
step3_1: "프로필 설정은 잘 끝내셨나요?"
step3_2: "그럼 시험삼아 노트를 작성해 보세요. 화면에 있는 연필 버튼을 누르면 작성 폼이 열립니다."
step3_3: "내용을 작성한 후, 폼 오른쪽 상단의 버튼을 눌러 노트를 올릴 수 있습니다."
step3_4: "쓸 말이 없나요? \"Misskey 시작했어요!\" 같은 건 어떨까요? :>"
step4_1: "노트 작성을 끝내셨나요?"
step4_2: "당신의 노트가 타임라인에 표시되어 있다면 성공입니다."
step5_1: "이제, 다른 사람을 팔로우하여 타임라인을 활기차게 만들어보도록 합시다."
step5_2: "{featured}에서 이 인스턴스의 인기 노트를 보실 수 있습니다. {explore}에서는 인기 사용자를 찾을 수 있구요. 마음에 드는 사람을 골라 팔로우해 보세요!"
step5_3: "다른 유저를 팔로우하려면 해당 유저의 아이콘을 클릭하여 프로필 페이지를 띄운 후, 팔로우 버튼을 눌러 주세요."
step5_4: "사용자에 따라 팔로우가 승인될 때까지 시간이 걸릴 수 있습니다."
step6_1: "타임라인에 다른 사용자의 노트가 나타난다면 성공입니다."
step6_2: "다른 유저의 노트에 \"리액션\"을 붙여 간단하게 당신의 반응을 전달할 수도 있습니다."
step6_3: "리액션을 붙이려면, 노트의 \"+\" 버튼을 클릭하고 원하는 이모지를 선택합니다."
step7_1: "이것으로 Misskey의 기본 튜토리얼을 마치겠습니다. 수고하셨습니다!"
step7_2: "Misskey에 대해 더 알고 싶으시다면 {help}를 참고해 주세요."
step7_3: "그럼 Misskey를 즐기세요! 🚀"
_2fa:
alreadyRegistered: "이미 설정이 완료되었습니다."
registerDevice: "디바이스 등록"
registerKey: "키를 등록"
step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다."
step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
step3: "앱에 표시된 토큰을 입력하시면 완료됩니다."
step4: "다음 로그인부터는 토큰을 입력해야 합니다."
securityKeyInfo: "FIDO2를 지원하는 하드웨어 보안 키를 사용하여 로그인하도록 설정할 수 있습니다."
securityKeyInfo: "FIDO2를 지원하는 하드웨어 보안 키 혹은 디바이스의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
_permissions:
"read:account": "계정 정보 보기"
"write:account": "계정 정보 변경"
"read:blocks": "차단 보기"
"write:blocks": "차단 수정"
"read:drive": "드라이브 보기"
"write:drive": "드라이브 수정"
"read:favorites": "즐겨찾기 보기"
"write:favorites": "즐겨찾기 수정"
"read:following": "팔로우 정보 보기"
"write:following": "팔로잉 및 팔로우 수정"
"read:messaging": "대화 보기"
"write:messaging": "대화 수정"
"read:mutes": "뮤트 보기"
"write:mutes": "뮤트 수정"
"write:notes": "노트 작성 및 삭제"
"read:notifications": "알림 보기"
"write:notifications": "알림 수정"
"read:reactions": "리액션 보기"
"write:reactions": "리액션 수정"
"write:votes": "투표하기"
"read:pages": "페이지 보기"
"write:pages": "페이지 수정"
"read:page-likes": "페이지의 좋아요 보기"
"write:page-likes": "페이지의 좋아요 수정"
"read:user-groups": "유저 그룹 조회"
"write:user-groups": "유저 그룹 변경"
"read:account": "계정 정보를 봅니다"
"write:account": "계정 정보 변경합니다"
"read:blocks": "차단 여부를 확인합니다"
"write:blocks": "차단을 하거나 해제합니다"
"read:drive": "드라이브를 조회합니다"
"write:drive": "드라이브에 파일을 올리거나, 이름을 변경하거나, 삭제합니다"
"read:favorites": "즐겨찾기를 조회합니다"
"write:favorites": "즐겨찾기에 추가하거나 삭제합니다"
"read:following": "팔로우 상태를 봅니다"
"write:following": "팔로우하거나 팔로우를 해제합니다"
"read:messaging": "대화를 읽습니다"
"write:messaging": "대화를 시작하거나 메시지를 보냅니다"
"read:mutes": "뮤트 여부를 확인합니다"
"write:mutes": "뮤트를 하거나 해제합니다"
"write:notes": "노트 작성하거나 삭제합니다"
"read:notifications": "알림을 확인합니다"
"write:notifications": "알림을 모두 읽음 처리합니다"
"read:reactions": "리액션을 확인합니다"
"write:reactions": "리액션을 추가하거나 취소합니다"
"write:votes": "투표를 합니다"
"read:pages": "페이지를 봅니다"
"write:pages": "페이지 수정합니다"
"read:page-likes": "페이지의 좋아요를 확인합니다"
"write:page-likes": "페이지의 좋아요를 추가하거나 삭제합니다"
"read:user-groups": "유저 그룹 조회합니다"
"write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다"
_auth:
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
permissionAsk: "이 앱은 다음의 권한을 요청합니다"
_antennaSources:
all: "모든 게시물"
homeTimeline: "팔로우중인 유저의 게시물"
users: "지정한 유저(들)의 게시물"
userList: "지정한 리스트에 속한 유저의 게시물"
all: "모든 노트"
homeTimeline: "팔로우중인 유저의 노트"
users: "지정한 한 명 혹은 여러 명의 유저의 노트"
userList: "지정한 리스트에 속한 유저의 노트"
userGroup: "지정한 그룹에 속한 유저의 노트"
_weekday:
sunday: "일요일"
monday: "월요일"
@ -403,6 +518,9 @@ _widgets:
calendar: "달력"
trends: "트렌드"
clock: "시계"
rss: "RSS 리더"
activity: "활동"
photos: "사진"
_cw:
hide: "숨기기"
show: "더 보기"
@ -440,9 +558,10 @@ _visibility:
followersDescription: "팔로워에게만 공개"
specified: "다이렉트"
specifiedDescription: "지정한 유저에게만 공개"
localOnly: "로컬에만"
_postForm:
replyPlaceholder: "이 에 답글..."
quotePlaceholder: "이 글을 인용..."
replyPlaceholder: "이 노트에 답글..."
quotePlaceholder: "이 노트를 인용..."
_placeholders:
a: "지금 무엇을 하고 있나요?"
b: "무슨 일이 일어나고 있나요?"
@ -459,7 +578,7 @@ _profile:
metadataLabel: "라벨"
metadataContent: "내용"
_exportOrImport:
allNotes: "모든 게시물"
allNotes: "모든 노트"
followingList: "팔로잉"
muteList: "뮤트"
blockingList: "차단"
@ -467,12 +586,12 @@ _exportOrImport:
_charts:
federationInstancesIncDec: "연합 인스턴스 수 증감"
federationInstancesTotal: "총 연합 인스턴스 수"
usersIncDec: "유저 증감"
usersTotal: "유저 합계"
usersIncDec: "유저 증감"
usersTotal: "유저 합계"
activeUsers: "활성 유저 수"
notesIncDec: "노트 수 증감"
localNotesIncDec: "로컬 노트 수 증감"
remoteNotesIncDec: "외부 노트 수 증감"
remoteNotesIncDec: "리모트 노트 수 증감"
notesTotal: "총 노트 수"
filesIncDec: "파일 수 증감"
filesTotal: "총 파일 수"
@ -480,10 +599,10 @@ _charts:
storageUsageTotal: "총 스토리지 사용량"
_instanceCharts:
requests: "요청"
users: "유저 증감"
usersTotal: "누적 사용자 수"
users: "유저 증감"
usersTotal: "누적 유저 수"
notes: "노트 수 증감"
notesTotal: "누적 노트 수"
notesTotal: " 노트 수"
ff: "팔로잉/팔로워 증감"
ffTotal: "팔로잉/팔로워 누적"
cacheSize: "캐시 용량 증감"

View File

@ -1 +1,2 @@
---
_lang_: "Nederlands"

View File

@ -1 +1,2 @@
---
_lang_: "Norsk Bokmål"

View File

@ -1 +1,2 @@
---
_lang_: "język polski"

View File

@ -1 +1,2 @@
---
_lang_: "Português"

View File

@ -1 +1,2 @@
---
_lang_: "Русский язык"

View File

@ -1 +1,834 @@
---
_lang_: "中文(简体)"
introMisskey: "欢迎Misskey是一个开源的分散型SNS服务。\n通过「帖子」来分享现在发生的事情吧📡\n「反应」功能可以让你快速的对大家的「帖子」来表达感情👍\n一起来探索新的世界吧🚀"
monthAndDay: "{month}月 {day}日"
search: "搜索"
notifications: "通知"
username: "用户名"
password: "密码"
fetchingAsApObject: "联合查询中"
ok: "OK"
gotIt: "我明白了"
cancel: "取消"
enterUsername: "输入用户名"
renotedBy: "由 {user} 转推"
noNotes: "没有投稿"
noNotifications: "无通知"
instance: "实例"
settings: "设置"
profile: "个人资料"
timeline: "时间线"
noAccountDescription: "这个人很懒,没有写自我介绍"
login: "登录"
loggingIn: "正在登录..."
logout: "登出"
signup: "新用户注册"
uploading: "正在上传"
save: "保存"
users: "用户"
addUser: "添加用户"
favorite: "收藏"
favorites: "收藏"
unfavorite: "取消收藏"
pin: "置顶"
unpin: "取消置顶"
copyContent: "复制内容"
copyLink: "复制链接"
delete: "删除"
addToList: "添加至列表"
sendMessage: "发送"
copyUsername: "复制用户名"
reply: "回复"
loadMore: "查看更多"
youGotNewFollower: "你有新的关注者"
receiveFollowRequest: "收到关注请求"
followRequestAccepted: "同意关注请求"
mentions: "提及"
directNotes: "指定用户可见"
importAndExport: "导入和导出"
import: "导入"
export: "导出"
files: "文件"
download: "下载"
driveFileDeleteConfirm: "要删除「{name}」文件吗?附加此文件的帖子也会消失。"
unfollowConfirm: "要取消对{name}的关注吗?"
exportRequested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
importRequested: "导入请求已提交。这可能需要花一点时间。"
lists: "列表"
noLists: "列表为空"
note: "帖子"
notes: "帖子"
following: "关注中"
followers: "关注者"
followsYou: "关注了你"
createList: "创建列表"
manageLists: "管理列表"
error: "有点小问题"
retry: "重试"
enterListName: "输入列表名称"
privacy: "隐私"
makeFollowManuallyApprove: "关注者请求需要批准"
defaultNoteVisibility: "默认可见性"
follow: "关注"
followRequest: "关注申请"
followRequests: "关注申请"
unfollow: "取消关注"
followRequestPending: "发送关注申请"
enterEmoji: "输入Emoji"
renote: "转发"
unrenote: "取消转发"
quote: "引用"
pinnedNote: "已置顶的帖子"
you: "您"
clickToShow: "点击以显示"
sensitive: "阅读注意"
add: "添加"
reaction: "反应"
reactionSettingDescription: "快速选择回应中的自定义表情符号,以换行符分隔。"
rememberNoteVisibility: "记录公开范围"
renameFile: "重命名文件"
attachCancel: "删除附件"
markAsSensitive: "阅读注意"
unmarkAsSensitive: "取消标记为敏感内容"
enterFileName: "请输入文件名"
mute: "屏蔽"
unmute: "解除屏蔽"
block: "屏蔽"
unblock: "取消屏蔽"
suspend: "冻结"
unsuspend: "解除冻结"
blockConfirm: "确定要屏蔽吗?"
unblockConfirm: "确定要解除屏蔽吗?"
suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表"
customEmojis: "自定义Emoji"
emojiName: "Emoji 名称"
emojiUrl: "emoji 地址"
addEmoji: "添加Emoji"
cacheRemoteFiles: "远程文件缓存"
cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程实例载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。"
flagAsBot: "这个账户是Bot"
flagAsCat: "这个账户是Cat"
autoAcceptFollowed: "自动允许关注"
addAcount: "添加账户"
loginFailed: "登录失败"
showOnRemote: "转到所在实例显示"
general: "常规设置"
wallpaper: "壁纸"
removeWallpaper: "移除壁纸"
searchWith: "搜索:{q}"
youHaveNoLists: "列表为空"
followConfirm: "你确定要关注{name}吗?"
proxyAccount: "代理账户"
proxyAccountDescription: "代理帐户是在某些情况下充当用户的远程关注者的帐户。 例如,当一个用户列出一个远程用户时,如果没有人跟随该列出的用户,则该活动将不会传递到该实例,因此将代之以代理帐户。"
host: "主机名"
selectUser: "选择用户"
recipient: "收件人"
annotation: "注解"
federation: "联合"
instances: "实例"
registeredAt: "初次观察"
latestRequestSentAt: "上次发送的请求"
latestRequestReceivedAt: "上次收到的请求"
latestStatus: "最后状态"
storageUsage: "已用存储"
charts: "图表"
perHour: "每小时"
perDay: "每天"
stopActivityDelivery: "停止发送活动"
blockThisInstance: "阻止此实例"
operations: "操作"
software: "软件"
version: "版本"
metadata: "元数据"
withNFiles: "{n}个文件"
monitor: "监视器"
jobQueue: "作业队列"
cpuAndMemory: "CPU使用量"
network: "网络"
disk: "存储"
instanceInfo: "实例情报"
statistics: "统计"
clearQueue: "清除队列"
clearQueueConfirmTitle: "确定清除队列?"
clearQueueConfirmText: "未送达的帖子将不会送达。 通常,您不需要这样做。"
clearCachedFiles: "清除缓存"
clearCachedFilesConfirm: "确定要清除缓存文件?"
blockedInstances: "被阻拦的实例"
blockedInstancesDescription: "设定要阻拦的实例,以换行来进行分割。被阻拦的实例将无法与本实例进行交换通讯。"
muteAndBlock: "屏蔽/拉黑"
mutedUsers: "禁言用户"
blockedUsers: "已屏蔽用户"
noUsers: "无用户"
editProfile: "编辑资料"
noteDeleteConfirm: "要删除该帖子吗?"
pinLimitExceeded: "无法置顶更多了"
intro: "Misskey的部署结束啦填写管理员账号吧"
done: "完成"
processing: "处理中"
preview: "预览"
noCustomEmojis: "无自定义Emoji"
customEmojisOfRemote: "远程Emoji"
noJobs: "没有任务"
federating: "联合中"
blocked: "已拦截"
suspended: "停止推流"
all: "全部"
subscribing: "已订阅"
publishing: "直播中"
notResponding: "没有响应"
instanceFollowing: "关注实例"
instanceFollowers: "关注实例"
instanceUsers: "实例用户"
changePassword: "修改密码"
security: "安全性"
retypedNotMatch: "两次输入不一致!"
currentPassword: "现在的密码"
newPassword: "新密码"
newPasswordRetype: "重新输入密码:"
attachFile: "插入附件"
more: "更多!"
featured: "热门"
usernameOrUserId: "用户名或用户ID"
noSuchUser: "用户不存在"
lookup: "查询"
announcements: "公告"
imageUrl: "图片URL"
remove: "删除"
removed: "已删除"
removeAreYouSure: "要删掉「{x}」吗?"
saved: "已保存"
messaging: "聊天"
upload: "上传"
fromDrive: "从网盘中"
fromUrl: "从 URL"
explore: "发现"
games: "Misskey游戏"
messageRead: "已读"
recentUsedEmojis: "最近使用的Emoji表情"
noMoreHistory: "没有更多的历史记录"
startMessaging: "开始聊天"
nUsersRead: "{n}人已读"
agreeTo: "{0}人同意"
tos: "服务条款"
start: "开始"
home: "首页"
remoteUserCaution: "由于是远程用户,信息不完整。"
activity: "活动"
images: "图片"
birthday: "生日"
yearsOld: "{age}岁"
registeredDate: "注册于"
location: "位置"
theme: "主题"
lightThemes: "亮色主题"
darkThemes: "暗色主题"
drive: "网盘"
selectFile: "选择文件"
selectFiles: "选择文件"
renameFolder: "重命名文件夹"
createFolder: "创建文件夹"
deleteFolder: "删除文件夹"
addFile: "添加文件"
emptyDrive: "驱动器为空"
emptyFolder: "空文件夹"
copyUrl: "复制链接"
rename: "重命名"
avatar: "头像"
banner: "Banner"
nsfw: "阅读注意"
disconnectedFromServer: "已从服务器断开连接"
reloadConfirm: "确定要重新加载吗"
watch: "关注"
unwatch: "取消关注"
accept: "允许"
reject: "拒绝"
instanceName: "实例名称"
instanceDescription: "实例介绍"
maintainerName: "管理员名称"
maintainerEmail: "管理员电子邮箱"
tosUrl: "服务条款URL"
thisYear: "今年"
thisMonth: "本月"
today: "今天"
dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "页面"
integration: "连携"
connectSerice: "已连接"
disconnectSerice: "断开连接"
enableLocalTimeline: "启用本地时间线功能"
enableGlobalTimeline: "启用全局时间线"
disablingTimelinesInfo: "即使时间线功能被禁用,出于便利性的原因,管理员和数据图表也可以继续使用。"
registration: "注册"
enableRegistration: "允许新用户注册"
invite: "邀请"
proxyRemoteFiles: "代理远程文件"
proxyRemoteFilesDescription: "启用此设置后,由于超出存储容量而导致未保存被删除的远程文件将被本地代理,并且会生成缩略图。不会影响服务器的存储。"
driveCapacityPerLocalAccount: "每个用户的网盘空间"
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
inMb: "以兆字节(Mbps)为单位"
iconUrl: "图标URL"
bannerUrl: "Banner URL"
basicInfo: "基本信息"
pinnedUsers: "置顶用户"
pinnedUsersDescription: "在「发现」页面中使用换行标记想要置顶的用户。"
recaptcha: "reCAPTCHA"
enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)"
recaptchaSiteKey: "网站密钥"
recaptchaSecretKey: "reCAPTCHA 密钥"
antennas: "天线"
name: "名称"
antennaKeywordsDescription: "使用空格分隔会产生AND规范并且使用换行符分隔会产生OR规范"
serviceworker: "ServiceWorker"
enableServiceworker: "启用ServiceWorker"
caseSensitive: "区分大小写"
connectedTo: "您的账号已连到接以下社交账号"
notesAndReplies: "帖子与回复"
withFiles: "附件"
silence: "禁言"
silenceConfirm: "确认要禁言吗?"
unsilenceConfirm: "要解除禁言吗?"
popularUsers: "热门用户"
recentlyUpdatedUsers: "最近投稿用户"
recentlyRegisteredUsers: "最近登录用户"
recentlyDiscoveredUsers: "最近发现的用户"
exploreUsersCount: "有{count}个用户"
exploreFediverse: "探索Fediverse"
popularTags: "热门标签"
userList: "列表"
about: "关于"
aboutMisskey: "关于 Misskey"
aboutMisskeyText: "Misskey是由syuilo于2014年开发的开放源代码软件。"
misskeyMembers: "现在由以下成员进行开发和维护:"
misskeySource: "源代码在这里公开:"
misskeyTranslation: "与我们一同进行Misskey的翻译工作"
misskeyDonate: "可以向 Misskey 进行捐款以支持开发:"
morePatrons: "还有很多其他的人也在支持我们,非常感谢🥰"
patrons: "支持者"
administrator: "管理员"
token: "令牌"
twoStepAuthentication: "两步验证"
moderator: "版主"
nUsersMentioned: "{n} 被提到"
securityKey: "安全密钥"
securityKeyName: "密钥名称"
lastUsed: "最后使用:"
unregister: "删除账户"
resetPassword: "重置密码"
newPasswordIs: "新的密码是「{password}」"
post: "投稿"
posted: "已投稿"
autoReloadWhenDisconnected: "断开连接时自动重新加载"
autoNoteWatch: "自动关注帖子"
autoNoteWatchDescription: "让您能够收到关于「反应」和回复其他用户的帖子的通知。"
reduceUiAnimation: "减少UI动画"
share: "分享"
notFound: "未找到"
uploadFolder: "默认上传文件夹"
cacheClear: "清空缓存"
markAsReadAllNotifications: "将所有通知标为已读"
markAsReadAllUnreadNotes: "将所有帖子标记为已读"
help: "帮助"
inputMessageHere: "在此键入信息"
close: "关闭"
group: "群组"
groups: "群组"
createGroup: "创建群组"
ownedGroups: "拥有的群组"
joinedGroups: "已加入的群组"
invites: "邀请"
groupName: "群组名"
members: "成员"
transfer: "转让"
title: "标题"
text: "文本"
enable: "启用"
next: "下一个"
retype: "重新输入"
noteOf: "{user}的帖子"
inviteToGroup: "群组邀请"
maxNoteTextLength: "帖子的字数限制"
quoteAttached: "已引用"
quoteQuestion: "是否将其作为引用附上?"
newMessageExists: "新信息"
onlyOneFileCanBeAttached: "只能添加一个附件"
signinRequired: "请先登录"
invitationCode: "邀请码"
checking: "正在确认"
available: "可用"
unavailable: "不可用"
usernameInvalidFormat: "可使用大小写英文字母、数字和下划线。"
tooShort: "过短"
tooLong: "过长"
weakPassword: "密码强度:弱"
normalPassword: "密码强度:中等"
strongPassword: "密码强度:强"
passwordMatched: "密码一致"
passwordNotMatched: "密码不一致"
or: "或者"
uiLanguage: "显示语言"
groupInvited: "群组招待"
aboutX: "关于 {x}"
useOsNativeEmojis: "使用OS原生Emoji"
noGroups: "没有组"
joinOrCreateGroup: "加入或者创建群组"
noHistory: "没有历史记录"
disableAnimatedMfm: "禁用MFM动画"
doing: "正在进行"
category: "类别"
tags: "标签"
createAccount: "注册账户"
existingAcount: "现有的帐户"
regenerate: "重新生成"
fontSize: "字体大小"
noFollowRequests: "没有关注申请"
openImageInNewTab: "在新标签页中打开图片"
dashboard: "Dashboard"
local: "本地"
remote: "远程"
total: "总计"
weekOverWeekChanges: "与前一周相比"
dayOverDayChanges: "与前一日相比"
accessibility: "辅助功能"
clinetSettings: "客户端设置"
accountSettings: "账户设置"
_ago:
unknown: "未知"
future: "未来"
justNow: "最近"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分前"
hoursAgo: "{n}小时前"
daysAgo: "{n}日前"
weeksAgo: "{n}周前"
monthsAgo: "{n}月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分"
hour: "小时"
day: "日"
_tutorial:
title: "Misskey的使用方法"
step1_1: "欢迎!"
step1_2: "这个页面叫做「时间线」,它会按照时间顺序显示所有你「关注」的人所发的「帖子」。"
step1_3: "如果你并没有发布任何帖子,也没有关注其他的人,你的时间线页面应当什么都没有显示。"
step2_1: "在你想发布一些帖子之前,让我们先进行一下个人资料设置。"
step2_2: "如果别人能够更加的了解你,关注你的概率也会得到提升。"
step3_1: "已经设置完个人资料了吗?"
step3_2: "那么接下来,试着写一些什么东西来发布吧。你可以通过点击屏幕上的铅笔图标来打开投稿页面。"
step3_3: "写完内容后,点击窗口右上方的按钮就可以投稿。"
step3_4: "不知道说些什么好吗那就写下「Misskey我来啦」这样的话吧。"
step4_1: "将你的话语发布出去了吗?"
step4_2: "太棒了!现在你可以在你的时间线中看到你刚刚发布的帖子了。"
step7_3: "接下来享受Misskey带来的乐趣吧🚀"
_2fa:
alreadyRegistered: "此设备已被注册"
registerDevice: "注册设备"
registerKey: "注册密钥"
_permissions:
"read:account": "查看账户信息"
"write:account": "更改帐户信息"
"read:blocks": "查看黑名单"
"write:blocks": "编辑黑名单"
"read:drive": "查看网盘"
"write:drive": "管理网盘文件"
"read:favorites": "查看收藏夹"
"write:favorites": "编辑收藏夹"
"read:following": "查看关注信息"
"write:following": "关注/取消关注"
"read:mutes": "查看屏蔽列表"
"write:mutes": "编辑屏蔽列表"
"read:notifications": "查看通知"
"write:notifications": "管理通知"
"read:reactions": "查看回应"
"write:reactions": "回应操作"
"write:votes": "投票"
"read:pages": "查看页面"
"write:pages": "操作页面"
"read:page-likes": "查看喜欢的页面"
"write:page-likes": "操作喜欢的页面"
"read:user-groups": "查看用户组"
"write:user-groups": "操作用户组"
_auth:
permissionAsk: "这个应用程序需要以下权限"
_antennaSources:
all: "所有帖子"
_weekday:
sunday: "星期日"
monday: "星期一"
tuesday: "星期二"
wednesday: "星期三"
thursday: "星期四"
friday: "星期五"
saturday: "星期六"
_widgets:
memo: "便签"
notifications: "通知"
timeline: "时间线"
calendar: "日历"
trends: "趋势"
clock: "时钟"
rss: "RSS阅读器"
activity: "活动"
_cw:
hide: "隐藏"
show: "查看更多"
chars: "{count}个字符"
files: "{count} 个文件"
poll: "投票"
_poll:
noOnlyOneChoice: "需要至少两个选项"
choiceN: "选择{n}"
noMore: "无法再添加更多了"
canMultipleVote: "允许多个投票"
expiration: "截止时间"
infinite: "无限期"
at: "指定日期"
after: "指定时间"
deadlineDate: "截止日期"
deadlineTime: "小时"
duration: "时长"
votesCount: "{n}票"
totalVotes: "总票数{n}"
vote: "投票"
showResult: "显示结果"
voted: "已投票"
closed: "已截止"
remainingDays: "{d}天{h}小时后截止"
remainingHours: "{h}小时{m}分后截止"
remainingMinutes: "{m}分{s}秒后截止"
remainingSeconds: "{s}秒后截止"
_visibility:
public: "公开"
home: "首页"
homeDescription: "仅发送至首页的时间线"
followers: "关注者"
followersDescription: "仅发送至关注者"
specified: "指定用户"
specifiedDescription: "仅发送至指定用户"
localOnly: "仅限本地"
_postForm:
replyPlaceholder: "回复这个帖子..."
quotePlaceholder: "引用这个帖子..."
_placeholders:
a: "现在如何?"
b: "发生了什么?"
c: "你有什么想法?"
d: "你想要发布些什么吗?"
e: "请写下来吧"
f: "等待您的发布..."
_profile:
name: "名称"
username: "用户名"
description: "个人简介"
youCanIncludeHashtags: "您可以包含一个哈希标签。"
metadata: "额外信息"
metadataLabel: "标签"
metadataContent: "内容"
_exportOrImport:
allNotes: "所有帖子"
followingList: "关注中"
muteList: "屏蔽"
blockingList: "屏蔽"
userLists: "列表"
_charts:
federationInstancesIncDec: "联合:增加/减少"
federationInstancesTotal: "联合总数"
usersIncDec: "用户数量:增加/减少"
usersTotal: "用户总数"
activeUsers: "活跃用户数"
notesIncDec: "帖子:增加/减少"
notesTotal: "帖子总数"
_instanceCharts:
users: "用户数量:增加/减少"
usersTotal: "用户总数"
notes: "帖子:增加/减少"
notesTotal: "帖子:总数"
ff: "关注/被关注:数量变化"
ffTotal: "关注/被关注:总数"
cacheSize: "缓存大小:增加/减少"
_timelines:
home: "首页"
local: "本地"
social: "社交"
global: "全局"
_pages:
newPage: "创建页面"
editPage: "编辑页面"
readPage: "查看源"
page-created: "页面已创建"
page-updated: "页面已更新"
name-already-exists: "该页面URL已存在"
title-invalid-name: "无效的页面URL"
text-invalid-name: "请确认该项不为空"
editThisPage: "编辑此页面"
viewSource: "查看源代码"
viewPage: "查看页面"
like: "赞"
unlike: "取消赞"
liked-pages: "喜欢的页面"
my-pages: "我的页面"
inspector: "检查器"
content: "页面内容"
variables: "变量"
variables-info: "您可以使用变量创建动态页面。在文本中通过<b>{变量名}</b>的写法来嵌入变量值。例如在文本<b>Hello { thing } world!</b>中,如果变量(thing)的值为<b>ai</b>,那么该文本会成为<b>Hello ai world!</b>。"
variables-info2: "因为变量的计算(计算变量值)是从上到下执行的,所以不能在变量中引用下面的变量。例如从上到下依次定义了<b>ABC</b>3个变量那么<b>C</b>中可以引用<b>A</b>或<b>B</b>,但是<b>A</b>无法引用<b>B</b>或<b>C</b>。"
variables-info3: "为了接收来自用户的输入,页面上设有“用户输入”块,在“变量名称”中设置要在其中保存输入值的变量名(变量会自动创建)。您可以使用该变量执行操作以响应用户输入。"
variables-info4: "通过使用函数,您可以将数值计算过程组合成可重用的形式。要创建函数,需要创建一个“函数”类型的变量。你可以将函数设定为槽函数(参数)的格式槽函数的值可作为函数中的变量使用。另外AiScript标准中还有一些函数会将函数作为参数(称为高阶函数)。\n除了已经预先定义的函数外您也可以将它们设置为这些高阶函数的槽函数。"
more-details: "详细说明"
title: "标题"
url: "页面URL"
summary: "页面摘要"
alignCenter: "居中"
hide-title-when-pinned: "置顶时隐藏标题"
font: "字体"
fontSerif: "衬线字体"
fontSansSerif: "无衬线字体"
set-eye-catching-image: "设置封面图片"
remove-eye-catching-image: "删除封面图片"
chooseBlock: "添加块"
selectType: "选择类型"
enterVariableName: "请输入变量名"
the-variable-name-is-already-used: "变量名已使用"
content-blocks: "内容"
input-blocks: "输入"
special-blocks: "特殊"
post-from-post-form: "发布此内容"
posted-from-post-form: "已发布"
blocks:
text: "文本"
textarea: "文本区域"
section: "章节"
image: "图片"
button: "按钮"
if: "如果"
_if:
variable: "变量"
post: "投稿窗口"
_post:
text: "内容"
textInput: "文本输入"
_textInput:
name: "变量名"
text: "标题"
default: "默认值"
textareaInput: "多行文本输入"
_textareaInput:
name: "变量名"
text: "标题"
default: "默认值"
numberInput: "输入数值"
_numberInput:
name: "变量名"
text: "标题"
default: "默认值"
switch: "开关"
_switch:
name: "变量名"
text: "标题"
default: "默认值"
counter: "计数器"
_counter:
name: "变量名"
text: "标题"
inc: "增加值"
_button:
text: "标题"
colored: "彩色"
action: "按下按钮时的行为"
_action:
dialog: "显示对话框"
_dialog:
content: "内容"
resetRandom: "重置随机值"
pushEvent: "发送事件"
_pushEvent:
event: "事件名称"
message: "按下时显示的消息"
variable: "发送的变量"
no-variable: "空"
radioButton: "选择项"
_radioButton:
name: "变量名"
title: "标题"
values: "使用换行区分的选择项"
default: "默认值"
script:
categories:
flow: "控制"
logical: "逻辑运算"
operation: "计算"
comparison: "比较"
random: "随机"
value: "值"
fn: "函数"
text: "文本操作"
convert: "转换"
list: "列表"
blocks:
text: "文本"
multiLineText: "文本 (多行)"
textList: "文本列表"
_textList:
info: "请使用换行符分隔每行"
strLen: "文本长度"
_strLen:
arg1: "文本"
strPick: "提取字符"
_strPick:
arg1: "文本"
arg2: "字符位置"
strReplace: "替换文本"
_strReplace:
arg1: "文本"
arg2: "替换之前"
arg3: "替换之后"
strReverse: "文本反向"
_strReverse:
arg1: "文本"
join: "合并文本"
_join:
arg1: "列表"
arg2: "分隔符"
add: "加"
_add:
arg1: "A"
arg2: "B"
subtract: "减"
_subtract:
arg1: "A"
arg2: "B"
multiply: "乘"
_multiply:
arg1: "A"
arg2: "B"
divide: "除"
_divide:
arg1: "A"
arg2: "B"
mod: "取模(MOD)"
_mod:
arg1: "A"
arg2: "B"
round: "四舍五入"
_round:
arg1: "数值"
eq: "A和B相等"
_eq:
arg1: "A"
arg2: "B"
notEq: "A和B不等"
_notEq:
arg1: "A"
arg2: "B"
and: "A和B"
_and:
arg1: "A"
arg2: "B"
or: "A或B"
_or:
arg1: "A"
arg2: "B"
lt: "< A小于B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A大于B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A小于等于B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A大于等于B"
_gtEq:
arg1: "A"
arg2: "B"
if: "分支"
_if:
arg1: "如果"
arg2: "如果"
arg3: "否则"
not: "否"
_not:
arg1: "否"
random: "随机"
_random:
arg1: "概率"
rannum: "随机数"
_rannum:
arg1: "最小值"
arg2: "最大值"
randomPick: "从列表中随机选择"
_randomPick:
arg1: "列表"
dailyRandom: "随机(每个用户每日)"
_dailyRandom:
arg1: "概率"
dailyRannum: "随机数(每个用户每日)"
_dailyRannum:
arg1: "最小值"
arg2: "最大值"
dailyRandomPick: "从列表中随机选择(每个用户每日)"
_dailyRandomPick:
arg1: "列表"
seedRandom: "随机 (种子)"
_seedRandom:
arg1: "种子"
arg2: "概率"
seedRannum: "随机数(种子)"
_seedRannum:
arg1: "种子"
arg2: "最小值"
arg3: "最大值"
seedRandomPick: "从列表中随机选择 (种子)"
_seedRandomPick:
arg1: "种子"
arg2: "列表"
DRPWPM: "从概率列表中随机选择(每用户每天)"
_DRPWPM:
arg1: "文本列表"
pick: "从列表中选择"
_pick:
arg1: "列表"
arg2: "位置"
listLen: "获取列表长度"
_listLen:
arg1: "列表"
number: "数值"
stringToNumber: "文本到数字"
_stringToNumber:
arg1: "文本"
numberToString: "数字到文本"
_numberToString:
arg1: "数值"
splitStrByLine: "将文本按行拆分"
_splitStrByLine:
arg1: "文本"
ref: "变量"
fn: "函数"
_fn:
slots: "槽函数"
slots-info: "请使用换行符分隔每个槽函数"
arg1: "输出"
for: "重复"
_for:
arg1: "次数"
arg2: "处理"
typeError: "槽函数{slot}需要传入“{expect}”,但是实际传入为“{actual}”!"
thereIsEmptySlot: "槽函数{slot}为空!"
types:
string: "文字"
number: "数值"
boolean: "Flag"
array: "列表"
stringArray: "文本列表"
emptySlot: "空白槽函数"
enviromentVariables: "环境变量"
pageVariables: "页面元素"
argVariables: "输入变量"

View File

@ -1 +1,2 @@
---
_lang_: "中文(繁体)"

View File

@ -0,0 +1,38 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class userGroupInvitation1581526429287 implements MigrationInterface {
name = 'userGroupInvitation1581526429287'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "user_group_invitation" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_160c63ec02bf23f6a5c5e8140d6" PRIMARY KEY ("id"))`, undefined);
await queryRunner.query(`CREATE INDEX "IDX_bfbc6305547539369fe73eb144" ON "user_group_invitation" ("userId") `, undefined);
await queryRunner.query(`CREATE INDEX "IDX_5cc8c468090e129857e9fecce5" ON "user_group_invitation" ("userGroupId") `, undefined);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e9793f65f504e5a31fbaedbf2f" ON "user_group_invitation" ("userId", "userGroupId") `, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`, undefined);
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, undefined);
await queryRunner.query(`CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, undefined);
await queryRunner.query(`DROP TYPE "notification_type_enum_old"`, undefined);
await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, undefined);
await queryRunner.query(`ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_bfbc6305547539369fe73eb144a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_5cc8c468090e129857e9fecce5a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`, undefined);
await queryRunner.query(`ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_5cc8c468090e129857e9fecce5a"`, undefined);
await queryRunner.query(`ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_bfbc6305547539369fe73eb144a"`, undefined);
await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS ''`, undefined);
await queryRunner.query(`CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted')`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, undefined);
await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined);
await queryRunner.query(`ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_e9793f65f504e5a31fbaedbf2f"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_5cc8c468090e129857e9fecce5"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_bfbc6305547539369fe73eb144"`, undefined);
await queryRunner.query(`DROP TABLE "user_group_invitation"`, undefined);
}
}

View File

@ -0,0 +1,28 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class userGroupAntenna1581695816408 implements MigrationInterface {
name = 'userGroupAntenna1581695816408'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`, undefined);
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, undefined);
await queryRunner.query(`CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list', 'group')`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, undefined);
await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying array NOT NULL DEFAULT '{}'`, undefined);
await queryRunner.query(`CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list')`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, undefined);
await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined);
await queryRunner.query(`ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, undefined);
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`, undefined);
}
}

View File

@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class driveUserFolderIdIndex1581708415836 implements MigrationInterface {
name = 'driveUserFolderIdIndex1581708415836'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE INDEX "IDX_55720b33a61a7c806a8215b825" ON "drive_file" ("userId", "folderId", "id") `, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP INDEX "IDX_55720b33a61a7c806a8215b825"`, undefined);
}
}

View File

@ -0,0 +1,28 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class promo1581979837262 implements MigrationInterface {
name = 'promo1581979837262'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "promo_note" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "REL_e263909ca4fe5d57f8d4230dd5" UNIQUE ("noteId"), CONSTRAINT "PK_e263909ca4fe5d57f8d4230dd5c" PRIMARY KEY ("noteId"))`, undefined);
await queryRunner.query(`CREATE INDEX "IDX_83f0862e9bae44af52ced7099e" ON "promo_note" ("userId") `, undefined);
await queryRunner.query(`CREATE TABLE "promo_read" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_61917c1541002422b703318b7c9" PRIMARY KEY ("id"))`, undefined);
await queryRunner.query(`CREATE INDEX "IDX_9657d55550c3d37bfafaf7d4b0" ON "promo_read" ("userId") `, undefined);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2882b8a1a07c7d281a98b6db16" ON "promo_read" ("userId", "noteId") `, undefined);
await queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "promo_read" ADD CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "promo_read" ADD CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "promo_read" DROP CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4"`, undefined);
await queryRunner.query(`ALTER TABLE "promo_read" DROP CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05"`, undefined);
await queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_2882b8a1a07c7d281a98b6db16"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_9657d55550c3d37bfafaf7d4b0"`, undefined);
await queryRunner.query(`DROP TABLE "promo_read"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_83f0862e9bae44af52ced7099e"`, undefined);
await queryRunner.query(`DROP TABLE "promo_note"`, undefined);
}
}

View File

@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class featuredInjecttion1582019042083 implements MigrationInterface {
name = 'featuredInjecttion1582019042083'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "user_profile" ADD "injectFeaturedNote" boolean NOT NULL DEFAULT true`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "injectFeaturedNote"`, undefined);
}
}

View File

@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.0.0",
"version": "12.14.0",
"codename": "indigo",
"repository": {
"type": "git",
@ -30,20 +30,22 @@
"lodash": "^4.17.13"
},
"dependencies": {
"@elastic/elasticsearch": "7.5.0",
"@fortawesome/fontawesome-svg-core": "1.2.26",
"@fortawesome/free-brands-svg-icons": "5.12.0",
"@fortawesome/free-regular-svg-icons": "5.12.0",
"@fortawesome/free-solid-svg-icons": "5.12.0",
"@elastic/elasticsearch": "7.6.0",
"@fortawesome/fontawesome-svg-core": "1.2.27",
"@fortawesome/free-brands-svg-icons": "5.12.1",
"@fortawesome/free-regular-svg-icons": "5.12.1",
"@fortawesome/free-solid-svg-icons": "5.12.1",
"@fortawesome/vue-fontawesome": "0.1.9",
"@juggle/resize-observer": "3.0.2",
"@koa/cors": "3.0.0",
"@koa/multer": "2.0.2",
"@koa/router": "8.0.6",
"@koa/router": "8.0.8",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.12.0",
"@types/cbor": "5.0.0",
"@types/dateformat": "3.0.1",
"@types/double-ended-queue": "2.1.1",
"@types/glob": "7.1.1",
"@types/gulp": "4.0.6",
"@types/gulp-mocha": "0.0.32",
"@types/gulp-rename": "0.0.33",
@ -52,7 +54,7 @@
"@types/js-yaml": "3.12.2",
"@types/jsdom": "12.2.4",
"@types/katex": "0.11.0",
"@types/koa": "2.11.0",
"@types/koa": "2.11.1",
"@types/koa-bodyparser": "4.3.0",
"@types/koa-compress": "2.0.9",
"@types/koa-cors": "0.0.0",
@ -65,8 +67,9 @@
"@types/koa__multer": "2.0.1",
"@types/koa__router": "8.0.2",
"@types/lolex": "5.1.0",
"@types/markdown-it": "0.0.9",
"@types/mocha": "7.0.1",
"@types/node": "13.7.0",
"@types/node": "13.7.1",
"@types/nodemailer": "6.4.0",
"@types/nprogress": "0.2.0",
"@types/oauth": "0.9.1",
@ -77,7 +80,7 @@
"@types/qrcode": "1.3.4",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.14",
"@types/redis": "2.8.15",
"@types/rename": "1.0.1",
"@types/request": "2.48.4",
"@types/request-promise-native": "1.0.17",
@ -92,18 +95,18 @@
"@types/tmp": "0.1.0",
"@types/uuid": "3.4.7",
"@types/web-push": "3.3.0",
"@types/webpack": "4.41.3",
"@types/webpack": "4.41.6",
"@types/webpack-stream": "3.2.10",
"@types/websocket": "1.0.0",
"@types/ws": "7.2.1",
"@typescript-eslint/parser": "2.18.0",
"@typescript-eslint/parser": "2.19.2",
"agentkeepalive": "4.1.0",
"animejs": "3.1.0",
"apexcharts": "3.15.3",
"apexcharts": "3.15.6",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
"aws-sdk": "2.610.0",
"aws-sdk": "2.617.0",
"bcryptjs": "2.4.3",
"bull": "3.12.1",
"cafy": "15.2.1",
@ -112,7 +115,7 @@
"chalk": "3.0.0",
"chart.js": "2.9.3",
"cli-highlight": "2.1.4",
"commander": "4.1.0",
"commander": "4.1.1",
"content-disposition": "0.5.3",
"crc-32": "1.2.0",
"css-loader": "3.4.2",
@ -125,8 +128,9 @@
"eventemitter3": "4.0.0",
"feed": "4.1.0",
"fibers": "4.0.2",
"file-type": "13.1.2",
"file-type": "14.1.2",
"fluent-ffmpeg": "2.1.2",
"glob": "7.1.6",
"gulp": "4.0.2",
"gulp-clean-css": "4.2.0",
"gulp-dart-sass": "0.9.1",
@ -140,12 +144,12 @@
"hard-source-webpack-plugin": "0.13.1",
"html-minifier": "4.0.0",
"http-signature": "1.3.1",
"https-proxy-agent": "4.0.0",
"https-proxy-agent": "5.0.0",
"insert-text-at-cursor": "0.3.0",
"is-root": "2.1.0",
"is-svg": "4.2.1",
"js-yaml": "3.13.1",
"jsdom": "16.0.1",
"jsdom": "16.1.0",
"json5": "2.1.1",
"json5-loader": "3.0.0",
"jsrsasign": "8.0.12",
@ -164,6 +168,7 @@
"loader-utils": "1.2.3",
"lolex": "5.1.2",
"lookup-dns-cache": "2.1.0",
"markdown-it": "10.0.0",
"mocha": "7.0.1",
"moji": "0.5.1",
"ms": "2.1.2",
@ -193,16 +198,16 @@
"randomcolor": "0.5.4",
"ratelimiter": "3.4.0",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.3.0",
"redis": "2.8.0",
"reconnecting-websocket": "4.4.0",
"redis": "3.0.2",
"redis-lock": "0.1.4",
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"request": "2.88.0",
"request": "2.88.2",
"request-promise-native": "1.0.8",
"request-stats": "3.0.0",
"require-all": "3.0.0",
"rimraf": "3.0.1",
"rimraf": "3.0.2",
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sass": "1.25.0",
@ -216,7 +221,7 @@
"style-loader": "1.1.3",
"summaly": "2.3.1",
"syslog-pro": "1.0.0",
"systeminformation": "4.21.1",
"systeminformation": "4.21.2",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "2.3.4",
"textarea-caret": "3.1.0",
@ -240,7 +245,7 @@
"vue-cropperjs": "4.0.1",
"vue-i18n": "8.15.3",
"vue-json-pretty": "1.6.3",
"vue-loader": "15.8.3",
"vue-loader": "15.9.0",
"vue-marquee-text-component": "1.1.1",
"vue-meta": "2.3.2",
"vue-prism-component": "1.1.1",
@ -251,10 +256,10 @@
"vue-template-compiler": "2.6.11",
"vuedraggable": "2.23.2",
"vuex": "3.1.2",
"vuex-persistedstate": "2.7.0",
"vuex-persistedstate": "2.7.1",
"web-push": "3.4.3",
"webpack": "4.41.5",
"webpack-cli": "3.3.10",
"webpack": "4.41.6",
"webpack-cli": "3.3.11",
"websocket": "1.0.31",
"ws": "7.2.1",
"xev": "2.0.1"

View File

@ -2,10 +2,10 @@
<div class="mk-app" v-hotkey.global="keymap">
<header class="header">
<div class="title" ref="title">
<transition name="header" mode="out-in" appear>
<transition :name="$store.state.device.animation ? 'header' : ''" mode="out-in" appear>
<button class="_button back" v-if="canBack" @click="back()"><fa :icon="faChevronLeft"/></button>
</transition>
<transition name="header" mode="out-in" appear>
<transition :name="$store.state.device.animation ? 'header' : ''" mode="out-in" appear>
<div class="body" :key="pageKey">
<div class="default">
<portal-target name="avatar" slim/>
@ -18,65 +18,86 @@
</transition>
</div>
<div class="sub">
<fa :icon="faSearch"/>
<input type="search" class="search" :placeholder="$t('search')" v-model="searchQuery" v-autocomplete="{ model: 'searchQuery' }" :disabled="searchWait" @keypress="searchKeypress"/>
<button v-if="widgetsEditMode" class="_button edit active" @click="widgetsEditMode = false"><fa :icon="faGripVertical"/></button>
<button v-else class="_button edit" @click="widgetsEditMode = true"><fa :icon="faGripVertical"/></button>
<div class="search">
<fa :icon="faSearch"/>
<input type="search" :placeholder="$t('search')" v-model="searchQuery" v-autocomplete="{ model: 'searchQuery' }" :disabled="searchWait" @keypress="searchKeypress"/>
</div>
<button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
<x-clock v-if="isDesktop" class="clock"/>
</div>
</header>
<nav class="nav" ref="nav">
<div>
<button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn">
<mk-avatar :user="$store.state.i" class="avatar"/><mk-acct class="text" :user="$store.state.i"/>
</button>
<router-link class="item" active-class="active" to="/" exact v-if="$store.getters.isSignedIn">
<fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/" exact v-else>
<fa :icon="faHome" fixed-width/><span class="text">{{ $t('home') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/featured">
<fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/explore">
<fa :icon="faHashtag" fixed-width/><span class="text">{{ $t('explore') }}</span>
</router-link>
<button class="item _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn">
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn">
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
</router-link>
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked">
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
<i v-if="$store.state.i.pendingReceivedFollowRequestsCount"><fa :icon="faCircle"/></i>
</router-link>
<router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn">
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/announcements">
<fa :icon="faBroadcastTower" fixed-width/><span class="text">{{ $t('announcements') }}</span>
<i v-if="$store.getters.isSignedIn && $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i>
</router-link>
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)" @click="oepnInstanceMenu">
<fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span>
</button>
<button class="item _button" @click="search()">
<fa :icon="faSearch" fixed-width/><span class="text">{{ $t('search') }}</span>
</button>
<button class="item _button" @click="more">
<fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
<i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes)"><fa :icon="faCircle"/></i>
</button>
</div>
</nav>
<transition name="nav-back">
<div class="nav-back"
v-if="showNav"
@click="showNav = false"
@touchstart="showNav = false"
></div>
</transition>
<div class="contents">
<transition name="nav">
<nav class="nav" ref="nav" v-show="showNav">
<div>
<button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn">
<mk-avatar :user="$store.state.i" class="avatar"/><mk-acct class="text" :user="$store.state.i"/>
</button>
<div class="divider"></div>
<button class="item _button index active" @click="top()" v-if="$route.name === 'index'">
<fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</button>
<router-link class="item index" active-class="active" to="/" exact v-else>
<fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</router-link>
<button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn">
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn">
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
</router-link>
<router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn">
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked">
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
<i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i>
</router-link>
<div class="divider"></div>
<router-link class="item" active-class="active" to="/featured">
<fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/explore">
<fa :icon="faHashtag" fixed-width/><span class="text">{{ $t('explore') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/announcements">
<fa :icon="faBroadcastTower" fixed-width/><span class="text">{{ $t('announcements') }}</span>
<i v-if="$store.getters.isSignedIn && $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i>
</router-link>
<button class="item _button" @click="search()">
<fa :icon="faSearch" fixed-width/><span class="text">{{ $t('search') }}</span>
</button>
<div class="divider"></div>
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)" @click="oepnInstanceMenu">
<fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span>
</button>
<button class="item _button" @click="more">
<fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
<i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes)"><fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/settings">
<fa :icon="faCog" fixed-width/><span class="text">{{ $t('settings') }}</span>
</router-link>
</div>
</nav>
</transition>
<div class="contents" ref="contents" :class="{ wallpaper }">
<main ref="main">
<div class="content">
<transition name="page" mode="out-in">
<transition :name="$store.state.device.animation ? 'page' : ''" mode="out-in" @enter="onTransition">
<keep-alive :include="['index']">
<router-view></router-view>
</keep-alive>
@ -90,7 +111,7 @@
<div class="widgets">
<div ref="widgets" :class="{ edit: widgetsEditMode }">
<template v-if="enableWidgets && $store.getters.isSignedIn">
<template v-if="isDesktop && $store.getters.isSignedIn">
<template v-if="widgetsEditMode">
<mk-button primary @click="addWidget" class="add"><fa :icon="faPlus"/></mk-button>
<x-draggable
@ -100,9 +121,9 @@
class="sortable"
@sort="onWidgetSort"
>
<div v-for="widget in widgets" class="customize-container" :key="widget.id">
<div v-for="widget in widgets" class="customize-container _panel" :key="widget.id">
<header>
<span class="handle"><fa :icon="faBars"/></span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)"><fa :icon="faTimes"/></button>
<span class="handle"><fa :icon="faBars"/></span>{{ $t('_widgets.' + widget.name) }}<button class="remove _button" @click="removeWidget(widget)"><fa :icon="faTimes"/></button>
</header>
<div @click="widgetFunc(widget.id)">
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true"/>
@ -113,16 +134,15 @@
<template v-else>
<component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget"/>
</template>
<button ref="widgetsEditButton" v-if="widgetsEditMode" class="_button edit" @click="widgetsEditMode = false">{{ $t('exitEdit') }}</button>
<button ref="widgetsEditButton" v-else class="_button edit" @click="widgetsEditMode = true">{{ $t('editWidgets') }}</button>
</template>
</div>
</div>
</div>
<div class="buttons">
<button v-if="$store.getters.isSignedIn" class="button nav _button" @click="showNav" ref="navButton"><fa :icon="faBars"/><i v-if="$store.state.i.hasUnreadSpecifiedNotes || $store.state.i.pendingReceivedFollowRequestsCount || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement"><fa :icon="faCircle"/></i></button>
<button v-if="$store.getters.isSignedIn" class="button home _button" :disabled="$route.path === '/'" @click="$router.push('/')"><fa :icon="faHome"/></button>
<button class="button nav _button" @click="showNav = true" ref="navButton"><fa :icon="faBars"/><i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadSpecifiedNotes || $store.state.i.hasPendingReceivedFollowRequest || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement)"><fa :icon="faCircle"/></i></button>
<button v-if="$route.name === 'index'" class="button home _button" @click="top()"><fa :icon="faHome"/></button>
<button v-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button>
<button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
<button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
</div>
@ -137,19 +157,23 @@
<script lang="ts">
import Vue from 'vue';
import { faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { ResizeObserver } from '@juggle/resize-observer';
import { v4 as uuid } from 'uuid';
import i18n from './i18n';
import { host } from './config';
import { host, instanceName } from './config';
import { search } from './scripts/search';
import contains from './scripts/contains';
import MkToast from './components/toast.vue';
const DESKTOP_THRESHOLD = 1100;
export default Vue.extend({
i18n,
components: {
XClock: () => import('./components/header-clock.vue').then(m => m.default),
XNotifications: () => import('./components/notifications.vue').then(m => m.default),
MkButton: () => import('./components/ui/button.vue').then(m => m.default),
XDraggable: () => import('vuedraggable'),
@ -159,6 +183,7 @@ export default Vue.extend({
return {
host: host,
pageKey: 0,
showNav: false,
searching: false,
notificationsOpen: false,
accounts: [],
@ -167,10 +192,11 @@ export default Vue.extend({
searchQuery: '',
searchWait: false,
widgetsEditMode: false,
enableWidgets: window.innerWidth >= 1100,
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
canBack: false,
disconnectedDialog: null as Promise<void> | null,
faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer
wallpaper: localStorage.getItem('wallpaper') != null,
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer
};
},
@ -179,11 +205,13 @@ export default Vue.extend({
return {
'p': this.post,
'n': this.post,
's': this.search,
'h|/': this.help
};
},
widgets(): any[] {
return this.$store.state.settings.widgets;
return this.$store.state.deviceUser.widgets;
}
},
@ -191,6 +219,7 @@ export default Vue.extend({
$route(to, from) {
this.pageKey++;
this.notificationsOpen = false;
this.showNav = false;
this.canBack = (window.history.length > 0 && !['index'].includes(to.name));
},
@ -204,6 +233,10 @@ export default Vue.extend({
el.removeEventListener('mousedown', this.onMousedown);
}
}
},
isDesktop() {
if (this.isDesktop) this.adjustWidgetsWidth();
}
},
@ -213,15 +246,29 @@ export default Vue.extend({
this.connection.on('notification', this.onNotification);
if (this.widgets.length === 0) {
this.$store.dispatch('settings/setWidgets', [{
name: 'notifications',
this.$store.commit('deviceUser/setWidgets', [{
name: 'calendar',
id: 'a', data: {}
}, {
name: 'notifications',
id: 'b', data: {}
}, {
name: 'trends',
id: 'c', data: {}
}]);
}
}
this.$root.stream.on('_disconnected_', () => {
if (!this.disconnectedDialog) {
if (this.disconnectedDialog) return;
if (this.$store.state.device.autoReload) {
location.reload();
return;
}
setTimeout(() => {
if (this.$root.stream.state !== 'reconnecting') return;
this.disconnectedDialog = this.$root.dialog({
type: 'warning',
showCancelButton: true,
@ -233,27 +280,64 @@ export default Vue.extend({
}
this.disconnectedDialog = null;
});
}
}, 150)
});
},
mounted() {
if (this.isDesktop) this.adjustWidgetsWidth();
const adjustTitlePosition = () => {
this.$refs.title.style.left = (this.$refs.main.getBoundingClientRect().left - this.$refs.nav.offsetWidth) + 'px';
};
adjustTitlePosition();
const ro = new ResizeObserver((entries, observer) => {
adjustTitlePosition();
});
setInterval(() => {
this.$refs.title.style.left = (this.$refs.main.getBoundingClientRect().left - this.$refs.nav.offsetWidth) + 'px';
}, 1000);
ro.observe(this.$refs.contents);
// https://stackoverflow.com/questions/33891709/when-flexbox-items-wrap-in-column-mode-container-does-not-grow-its-width
if (this.enableWidgets) {
setInterval(() => {
const width = this.$refs.widgetsEditButton.offsetLeft + 300;
this.$refs.widgets.style.width = width + 'px';
}, 1000);
window.addEventListener('resize', adjustTitlePosition, { passive: true });
if (!this.isDesktop) {
window.addEventListener('resize', () => {
if (window.innerWidth >= DESKTOP_THRESHOLD) this.isDesktop = true;
}, { passive: true });
}
},
methods: {
adjustWidgetsWidth() {
// https://stackoverflow.com/questions/33891709/when-flexbox-items-wrap-in-column-mode-container-does-not-grow-its-width
const adjust = () => {
const lastChild = this.$refs.widgets.children[this.$refs.widgets.children.length - 1];
if (lastChild == null) return;
const width = lastChild.offsetLeft + 300 + 16;
this.$refs.widgets.style.width = width + 'px';
};
setInterval(adjust, 1000);
setTimeout(adjust, 100);
},
top() {
window.scroll({ top: 0, behavior: 'smooth' });
},
help() {
this.$router.push('/docs/keyboard-shortcut');
},
back() {
if (this.canBack) window.history.back();
},
onTransition() {
if (window._scroll) window._scroll();
},
post() {
this.$root.post();
},
@ -284,74 +368,13 @@ export default Vue.extend({
}
},
showNav(ev) {
this.$root.menu({
items: [{
text: this.$t('search'),
icon: faSearch,
action: this.search,
}, null, this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? {
text: this.$t('instance'),
icon: faServer,
action: () => this.oepnInstanceMenu(ev),
} : undefined, {
type: 'link',
text: this.$t('announcements'),
to: '/announcements',
icon: faBroadcastTower,
indicate: this.$store.state.i.hasUnreadAnnouncement,
}, {
type: 'link',
text: this.$t('featured'),
to: '/featured',
icon: faFireAlt,
}, {
type: 'link',
text: this.$t('explore'),
to: '/explore',
icon: faHashtag,
}, {
type: 'link',
text: this.$t('messaging'),
to: '/my/messaging',
icon: faComments,
indicate: this.$store.state.i.hasUnreadMessagingMessage,
}, this.$store.state.i.isLocked ? {
type: 'link',
text: this.$t('followRequests'),
to: '/my/follow-requests',
icon: faUserClock,
indicate: this.$store.state.i.pendingReceivedFollowRequestsCount > 0,
} : undefined, {
type: 'link',
text: this.$t('drive'),
to: '/my/drive',
icon: faCloud,
}, {
text: this.$t('more'),
icon: faEllipsisH,
action: () => this.more(ev),
indicate: this.$store.state.i.hasUnreadMentions || this.$store.state.i.hasUnreadSpecifiedNotes
}, null, {
type: 'user',
user: this.$store.state.i,
action: () => this.openAccountMenu(ev),
}],
direction: 'up',
align: 'left',
fixed: true,
width: 200,
source: ev.currentTarget || ev.target,
});
},
async openAccountMenu(ev) {
const accounts = (await this.$root.api('users/show', { userIds: this.$store.state.device.accounts.map(x => x.id) })).filter(x => x.id !== this.$store.state.i.id);
const accountItems = accounts.map(account => ({
type: 'user',
user: account,
action: () => { this.switchAccount(account) }
action: () => { this.switchAccount(account); }
}));
this.$root.menu({
@ -362,15 +385,28 @@ export default Vue.extend({
avatar: this.$store.state.i,
}, {
type: 'link',
text: this.$t('settings'),
text: this.$t('accountSettings'),
to: '/my/settings',
icon: faCog,
}, null, {
type: 'item',
text: this.$t('addAcount'),
}, null, ...accountItems, {
icon: faPlus,
action: () => { this.addAcount() },
}], ...accountItems],
text: this.$t('addAcount'),
action: () => {
this.$root.menu({
items: [{
text: this.$t('existingAcount'),
action: () => { this.addAcount(); },
}, {
text: this.$t('createAccount'),
action: () => { this.createAccount(); },
}],
align: 'left',
fixed: true,
width: 240,
source: ev.currentTarget || ev.target,
});
},
}]],
align: 'left',
fixed: true,
width: 240,
@ -382,9 +418,14 @@ export default Vue.extend({
this.$root.menu({
items: [{
type: 'link',
text: this.$t('statistics'),
to: '/instance/stats',
icon: faChartBar,
text: this.$t('dashboard'),
to: '/instance',
icon: faTachometerAlt,
}, null, {
type: 'link',
text: this.$t('settings'),
to: '/instance/settings',
icon: faCog,
}, {
type: 'link',
text: this.$t('customEmojis'),
@ -400,11 +441,6 @@ export default Vue.extend({
text: this.$t('files'),
to: '/instance/files',
icon: faCloud,
}, {
type: 'link',
text: this.$t('monitor'),
to: '/instance/monitor',
icon: faTachometerAlt,
}, {
type: 'link',
text: this.$t('jobQueue'),
@ -420,11 +456,6 @@ export default Vue.extend({
text: this.$t('announcements'),
to: '/instance/announcements',
icon: faBroadcastTower,
}, null, {
type: 'link',
text: this.$t('general'),
to: '/instance',
icon: faCog,
}],
align: 'left',
fixed: true,
@ -440,6 +471,11 @@ export default Vue.extend({
text: this.$t('lists'),
to: '/my/lists',
icon: faListUl,
}, {
type: 'link',
text: this.$t('groups'),
to: '/my/groups',
icon: faUsers,
}, {
type: 'link',
text: this.$t('antennas'),
@ -474,9 +510,19 @@ export default Vue.extend({
icon: faGamepad,
}, null] : []), {
type: 'link',
text: this.$t('about'),
text: this.$t('help'),
to: '/docs',
icon: faQuestionCircle,
}, {
type: 'link',
text: this.$t('aboutX', { x: instanceName || host }),
to: '/about',
icon: faInfoCircle,
}, {
type: 'link',
text: this.$t('aboutMisskey'),
to: '/about-misskey',
icon: faInfoCircle,
}],
align: 'left',
fixed: true,
@ -495,14 +541,31 @@ export default Vue.extend({
});
},
async switchAccount(account) {
const token = this.$store.state.device.accounts.find(x => x.id === account.id).token;
this.$root.api('i', {}, token).then(i => {
async createAccount() {
this.$root.new(await import('./components/signup-dialog.vue').then(m => m.default)).$once('signup', res => {
this.$store.dispatch('addAcount', res);
this.switchAccountWithToken(res.i);
});
},
async switchAccount(account: any) {
const token = this.$store.state.device.accounts.find((x: any) => x.id === account.id).token;
this.switchAccountWithToken(token);
},
switchAccountWithToken(token: string) {
this.$root.dialog({
type: 'waiting',
iconOnly: true
});
this.$root.api('i', {}, token).then((i: any) => {
this.$store.dispatch('switchAccount', {
...i,
token: token
}).then(() => {
location.reload();
});
location.reload();
});
},
@ -543,13 +606,16 @@ export default Vue.extend({
'calendar',
'rss',
'trends',
'clock',
'activity',
'photos',
];
this.$root.menu({
items: widgets.map(widget => ({
text: this.$t('_widgets.' + widget),
action: () => {
this.$store.dispatch('settings/addWidget', {
this.$store.commit('deviceUser/addWidget', {
name: widget,
id: uuid(),
data: {}
@ -561,23 +627,17 @@ export default Vue.extend({
},
removeWidget(widget) {
this.$store.dispatch('settings/removeWidget', widget);
this.$store.commit('deviceUser/removeWidget', widget);
},
saveHome() {
this.$store.dispatch('settings/setWidgets', this.widgets);
this.$store.commit('deviceUser/setWidgets', this.widgets);
}
}
});
</script>
<style lang="scss" scoped>
@keyframes blink {
0% { opacity: 1; }
30% { opacity: 1; }
90% { opacity: 0; }
}
.header-enter-active, .header-leave-active {
transition: opacity 0.5s, transform 0.5s !important;
}
@ -602,6 +662,28 @@ export default Vue.extend({
transform: translateY(32px);
}
.nav-enter-active,
.nav-leave-active {
opacity: 1;
transform: translateX(0);
transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.nav-enter,
.nav-leave-active {
opacity: 0;
transform: translateX(-240px);
}
.nav-back-enter-active,
.nav-back-leave-active {
opacity: 1;
transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.nav-back-enter,
.nav-back-leave-active {
opacity: 0;
}
.mk-app {
$header-height: 60px;
$nav-width: 250px;
@ -701,6 +783,8 @@ export default Vue.extend({
> .sub {
$post-button-size: 42px;
$post-button-margin: (($header-height - $post-button-size) / 2);
display: flex;
align-items: center;
position: absolute;
top: 0;
right: 16px;
@ -710,43 +794,68 @@ export default Vue.extend({
display: none;
}
> [data-icon] {
position: absolute;
top: 0;
left: 16px;
height: $header-height;
pointer-events: none;
font-size: 16px;
> .edit {
padding: 16px;
&.active {
color: var(--accent);
}
}
> .search {
$margin: 8px;
width: calc(100% - #{$post-button-size + $post-button-margin + $margin});
box-sizing: border-box;
margin-right: $margin;
padding: 0 12px 0 42px;
font-size: 1rem;
line-height: 38px;
border: none;
border-radius: 38px;
color: var(--fg);
background: var(--bg);
position: relative;
&:focus {
outline: none;
> input {
width: 220px;
box-sizing: border-box;
margin-right: 8px;
padding: 0 12px 0 42px;
font-size: 1rem;
line-height: 38px;
border: none;
border-radius: 38px;
color: var(--fg);
background: var(--bg);
&:focus {
outline: none;
}
}
> [data-icon] {
position: absolute;
top: 0;
left: 16px;
height: 100%;
pointer-events: none;
font-size: 16px;
}
}
> .post {
width: $post-button-size;
height: $post-button-size;
margin: $post-button-margin 0 $post-button-margin $post-button-margin;
margin-left: $post-button-margin;
border-radius: 100%;
font-size: 16px;
}
> .clock {
margin-left: 8px;
}
}
}
> .nav-back {
position: fixed;
top: 0;
left: 0;
z-index: 1001;
width: 100%;
height: 100%;
background: var(--modalBg);
}
> .nav {
$avatar-size: 32px;
$avatar-margin: ($header-height - $avatar-size) / 2;
@ -761,7 +870,14 @@ export default Vue.extend({
}
@media (max-width: $nav-hide-threshold) {
display: none;
position: fixed;
top: 0;
left: 0;
z-index: 1001;
}
@media (min-width: $nav-hide-threshold + 1px) {
display: block !important;
}
> div {
@ -771,13 +887,27 @@ export default Vue.extend({
z-index: 1001;
width: $nav-width;
height: 100vh;
padding-top: 16px;
padding: 16px 0;
padding-bottom: calc(3.7rem + 24px);
box-sizing: border-box;
overflow: auto;
background: var(--navBg);
border-right: solid 1px var(--divider);
@media (max-width: $nav-icon-only-threshold) {
> .divider {
margin: 16px 0;
border-top: solid 1px var(--divider);
}
@media (max-width: $nav-icon-only-threshold) and (min-width: $nav-hide-threshold + 1px) {
width: $nav-icon-only-width;
padding: 8px 0;
padding-bottom: calc(3.7rem + 24px);
> .divider {
margin: 8px auto;
width: calc(100% - 32px);
}
}
> .item {
@ -785,7 +915,6 @@ export default Vue.extend({
display: block;
padding-left: 32px;
font-size: $ui-font-size;
font-weight: bold;
line-height: 3.2rem;
text-overflow: ellipsis;
overflow: hidden;
@ -795,22 +924,6 @@ export default Vue.extend({
box-sizing: border-box;
color: var(--navFg);
&:not(.active) {
opacity: 0.85;
&:hover {
opacity: 1;
> [data-icon] {
opacity: 1;
}
}
> [data-icon] {
opacity: 0.85;
}
}
> [data-icon] {
width: ($header-height - ($avatar-margin * 2));
}
@ -837,13 +950,25 @@ export default Vue.extend({
&:hover {
text-decoration: none;
color: var(--navHoverFg);
}
&.active {
color: var(--navActive);
}
@media (max-width: $nav-icon-only-threshold) {
&:last-child {
position: fixed;
bottom: 0;
width: inherit;
padding-top: 8px;
padding-bottom: 8px;
background: var(--navBg);
border-top: solid 1px var(--divider);
border-right: solid 1px var(--divider);
}
@media (max-width: $nav-icon-only-threshold) and (min-width: $nav-hide-threshold + 1px) {
padding-left: 0;
width: 100%;
text-align: center;
@ -864,6 +989,13 @@ export default Vue.extend({
}
}
}
@media (max-width: $nav-hide-threshold) {
> .index,
> .notifications {
display: none;
}
}
}
}
@ -872,6 +1004,10 @@ export default Vue.extend({
margin: 0 auto;
min-width: 0;
&.wallpaper {
background: var(--wallpaperOverlay);
}
> main {
width: $main-width;
min-width: $main-width;
@ -954,12 +1090,6 @@ export default Vue.extend({
margin: 0 auto;
}
> .edit {
display: block;
font-size: 0.9em;
margin: 0 auto;
}
.customize-container {
margin: 8px 0;
background: #fff;
@ -967,10 +1097,10 @@ export default Vue.extend({
> header {
position: relative;
line-height: 32px;
background: #eee;
> .handle {
padding: 0 8px;
cursor: move;
}
> .remove {
@ -1074,7 +1204,7 @@ export default Vue.extend({
position: absolute;
top: 0;
left: 0;
color: var(--accent);
color: var(--indicator);
font-size: 16px;
animation: blink 1s infinite;
}

BIN
src/client/assets/fedi.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Misskey API</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!--
ReDoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='/api.json'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -0,0 +1,156 @@
<template>
<svg class="mbcofsoe" viewBox="0 0 10 10" preserveAspectRatio="none">
<circle v-for="(angle, i) in graduations"
:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))"
:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))"
:r="i % 5 == 0 ? 0.125 : 0.05"
:fill="i % 5 == 0 ? majorGraduationColor : minorGraduationColor"
:key="i"/>
<line
:x1="5 - (Math.sin(sAngle) * (sHandLengthRatio * handsTailLength))"
:y1="5 + (Math.cos(sAngle) * (sHandLengthRatio * handsTailLength))"
:x2="5 + (Math.sin(sAngle) * ((sHandLengthRatio * 5) - handsPadding))"
:y2="5 - (Math.cos(sAngle) * ((sHandLengthRatio * 5) - handsPadding))"
:stroke="sHandColor"
stroke-width="0.05"/>
<line
:x1="5 - (Math.sin(mAngle) * (mHandLengthRatio * handsTailLength))"
:y1="5 + (Math.cos(mAngle) * (mHandLengthRatio * handsTailLength))"
:x2="5 + (Math.sin(mAngle) * ((mHandLengthRatio * 5) - handsPadding))"
:y2="5 - (Math.cos(mAngle) * ((mHandLengthRatio * 5) - handsPadding))"
:stroke="mHandColor"
stroke-width="0.1"/>
<line
:x1="5 - (Math.sin(hAngle) * (hHandLengthRatio * handsTailLength))"
:y1="5 + (Math.cos(hAngle) * (hHandLengthRatio * handsTailLength))"
:x2="5 + (Math.sin(hAngle) * ((hHandLengthRatio * 5) - handsPadding))"
:y2="5 - (Math.cos(hAngle) * ((hHandLengthRatio * 5) - handsPadding))"
:stroke="hHandColor"
stroke-width="0.1"/>
</svg>
</template>
<script lang="ts">
import Vue from 'vue';
import * as tinycolor from 'tinycolor2';
export default Vue.extend({
props: {
smooth: {
type: Boolean,
default: false
}
},
data() {
return {
now: new Date(),
enabled: true,
graduationsPadding: 0.5,
handsPadding: 1,
handsTailLength: 0.7,
hHandLengthRatio: 0.75,
mHandLengthRatio: 1,
sHandLengthRatio: 1,
computedStyle: getComputedStyle(document.documentElement)
};
},
computed: {
dark(): boolean {
return tinycolor(this.computedStyle.getPropertyValue('--bg')).isDark();
},
majorGraduationColor(): string {
return this.dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
},
minorGraduationColor(): string {
return this.dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
},
sHandColor(): string {
return this.dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
},
mHandColor(): string {
return tinycolor(this.computedStyle.getPropertyValue('--fg')).toHexString();
},
hHandColor(): string {
return tinycolor(this.computedStyle.getPropertyValue('--accent')).toHexString();
},
ms(): number {
return this.now.getMilliseconds() * (this.smooth ? 1 : 0);
},
s(): number {
return this.now.getSeconds();
},
m(): number {
return this.now.getMinutes();
},
h(): number {
return this.now.getHours();
},
hAngle(): number {
return Math.PI * (this.h % 12 + (this.m + (this.s + this.ms / 1000) / 60) / 60) / 6;
},
mAngle(): number {
return Math.PI * (this.m + (this.s + this.ms / 1000) / 60) / 30;
},
sAngle(): number {
return Math.PI * (this.s + this.ms / 1000) / 30;
},
graduations(): any {
const angles = [];
for (let i = 0; i < 60; i++) {
const angle = Math.PI * i / 30;
angles.push(angle);
}
return angles;
}
},
mounted() {
const update = () => {
if (this.enabled) {
this.tick();
requestAnimationFrame(update);
}
};
update();
this.$store.subscribe((mutation, state) => {
if (mutation.type !== 'device/set') return;
if (mutation?.payload?.key !== 'theme') return;
setTimeout(() => {
this.computedStyle = getComputedStyle(document.documentElement);
}, 250);
});
},
beforeDestroy() {
this.enabled = false;
},
methods: {
tick() {
this.now = new Date();
}
}
});
</script>
<style lang="scss" scoped>
.mbcofsoe {
display: block;
}
</style>

View File

@ -2,7 +2,7 @@
<div class="swhvrteh" @contextmenu.prevent="() => {}">
<ol class="users" ref="suggests" v-if="type === 'user'">
<li v-for="user in users" @click="complete(type, user)" @keydown="onKeydown" tabindex="-1" class="user">
<img class="avatar" :src="user.avatarUrl" alt=""/>
<img class="avatar" :src="user.avatarUrl"/>
<span class="name">
<mk-user-name :user="user" :key="user.id"/>
</span>
@ -18,7 +18,7 @@
<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="$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-if="!useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span class="emoji" v-else>{{ emoji.emoji }}</span>
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
@ -130,8 +130,8 @@ export default Vue.extend({
return (this.$refs.suggests as Element).children;
},
useOsDefaultEmojis(): boolean {
return this.$store.state.device.useOsDefaultEmojis;
useOsNativeEmojis(): boolean {
return this.$store.state.device.useOsNativeEmojis;
}
},
@ -143,7 +143,7 @@ export default Vue.extend({
this.setPosition();
//#region Construct Emoji DB
const customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
const customEmojis = this.$store.state.instance.meta.emojis;
const emojiDefinitions: EmojiDef[] = [];
for (const x of customEmojis) {

View File

@ -1,6 +1,8 @@
<template>
<div>
<mk-avatar v-for="user in us" :user="user" :key="user.id" style="width:32px;height:32px;"/>
<div v-for="user in us" :key="user.id" style="display:inline-block;width:32px;height:32px;margin-right:8px;">
<mk-avatar :user="user" style="width:32px;height:32px;"/>
</div>
</div>
</template>

View File

@ -1,8 +1,8 @@
<template>
<sequential-entrance class="sqadhkmv" ref="list" :direction="direction">
<sequential-entrance class="sqadhkmv" ref="list" :direction="direction" :reversed="reversed">
<template v-for="(item, i) in items">
<slot :item="item" :i="i"></slot>
<div class="separator" :key="item.id + '_date'" :data-index="i" v-if="i != items.length - 1 && new Date(item.createdAt).getDate() != new Date(items[i + 1].createdAt).getDate()">
<div class="separator" :key="item.id + '_date'" v-if="showDate(i, item)">
<p class="date">
<span><fa class="icon" :icon="faAngleUp"/>{{ getDateText(item.createdAt) }}</span>
<span>{{ getDateText(items[i + 1].createdAt) }}<fa class="icon" :icon="faAngleDown"/></span>
@ -28,6 +28,11 @@ export default Vue.extend({
direction: {
type: String,
required: false
},
reversed: {
type: Boolean,
required: false,
default: false
}
},
@ -47,6 +52,16 @@ export default Vue.extend({
});
},
showDate(i, item) {
return (
i != this.items.length - 1 &&
new Date(item.createdAt).getDate() != new Date(this.items[i + 1].createdAt).getDate() &&
!item._prId_ &&
!this.items[i + 1]._prId_ &&
!item._featuredId_ &&
!this.items[i + 1]._featuredId_);
},
focus() {
this.$refs.list.focus();
}

View File

@ -1,9 +1,9 @@
<template>
<div class="mk-dialog" :class="{ iconOnly }">
<transition name="bg-fade" appear>
<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
<div class="bg" ref="bg" @click="onBgClick" v-if="show"></div>
</transition>
<transition name="dialog" appear @after-leave="() => { destroyDom(); }">
<transition :name="$store.state.device.animation ? 'dialog' : ''" appear @after-leave="() => { destroyDom(); }">
<div class="main" ref="main" v-if="show">
<template v-if="type == 'signin'">
<mk-signin/>

View File

@ -12,7 +12,7 @@
preload="metadata"
controls
v-else-if="detail && is === 'video'"/>
<img :src="file.thumbnailUrl" alt="" @load="onThumbnailLoaded" :style="`object-fit: ${ fit }`" v-else-if="isThumbnailAvailable"/>
<img :src="file.thumbnailUrl" @load="onThumbnailLoaded" :style="`object-fit: ${ fit }`" v-else-if="isThumbnailAvailable"/>
<fa :icon="faFileImage" class="icon" v-else-if="is === 'image'"/>
<fa :icon="faFileVideo" class="icon" v-else-if="is === 'video'"/>
@ -133,6 +133,7 @@ export default Vue.extend({
<style lang="scss" scoped>
.zdjebgpv {
display: flex;
position: relative;
> img,
> .icon {

View File

@ -83,17 +83,14 @@ export default Vue.extend({
} else {
this.$root.menu({
items: [{
type: 'item',
text: this.$t('rename'),
icon: faICursor,
action: this.rename
}, {
type: 'item',
text: this.file.isSensitive ? this.$t('unmarkAsSensitive') : this.$t('markAsSensitive'),
icon: this.file.isSensitive ? faEye : faEyeSlash,
action: this.toggleSensitive
}, null, {
type: 'item',
text: this.$t('copyUrl'),
icon: faLink,
action: this.copyUrl
@ -105,7 +102,6 @@ export default Vue.extend({
icon: faDownload,
download: this.file.name
}, null, {
type: 'item',
text: this.$t('delete'),
icon: faTrashAlt,
action: this.deleteFile
@ -113,11 +109,9 @@ export default Vue.extend({
type: 'nest',
text: this.$t('contextmenu.else-files'),
menu: [{
type: 'item',
text: this.$t('contextmenu.set-as-avatar'),
action: this.setAsAvatar
}, {
type: 'item',
text: this.$t('contextmenu.set-as-banner'),
action: this.setAsBanner
}]

View File

@ -19,7 +19,7 @@
{{ folder.name }}
</p>
<p class="upload" v-if="$store.state.settings.uploadFolder == folder.id">
{{ $t('upload-folder') }}
{{ $t('uploadFolder') }}
</p>
</div>
</template>

View File

@ -23,13 +23,13 @@
<x-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="n in 16"></div>
<mk-button v-if="moreFolders">{{ $t('@.load-more') }}</mk-button>
<mk-button v-if="moreFolders">{{ $t('loadMore') }}</mk-button>
</div>
<div class="files" ref="filesContainer" v-if="files.length > 0">
<x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="selectMode"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="n in 16"></div>
<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('@.load-more') }}</mk-button>
<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('loadMore') }}</mk-button>
</div>
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
<p v-if="draghover">{{ $t('empty-draghover') }}</p>
@ -319,6 +319,49 @@ export default Vue.extend({
});
},
renameFolder(folder) {
this.$root.dialog({
title: this.$t('contextmenu.rename-folder'),
input: {
placeholder: this.$t('contextmenu.input-new-folder-name'),
default: folder.name
}
}).then(({ canceled, result: name }) => {
if (canceled) return;
this.$root.api('drive/folders/update', {
folderId: folder.id,
name: name
}).then(folder => {
// FIXME: 画面を更新するために自分自身に移動
this.move(folder);
});
});
},
deleteFolder(folder) {
this.$root.api('drive/folders/delete', {
folderId: folder.id
}).then(() => {
// 削除時に親フォルダに移動
this.move(folder.parentId);
}).catch(err => {
switch(err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
this.$root.dialog({
type: 'error',
title: this.$t('unable-to-delete'),
text: this.$t('has-child-files-or-folders')
});
break;
default:
this.$root.dialog({
type: 'error',
text: this.$t('unable-to-delete')
});
}
});
},
onChangeFileInput() {
for (const file of Array.from((this.$refs.fileInput as any).files)) {
this.upload(file, this.folder);

View File

@ -140,7 +140,7 @@ export default Vue.extend({
},
created() {
let local = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
let local = this.$store.state.instance.meta.emojis;
local = groupByX(local, (x: any) => x.category || '');
this.customEmojis = local;
},

View File

@ -1,7 +1,7 @@
<template>
<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt"/>
<img v-else-if="char && !useOsDefaultEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt"/>
<span v-else-if="char && useOsDefaultEmojis">{{ char }}</span>
<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt"/>
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
<span v-else>:{{ name }}:</span>
</template>
@ -53,40 +53,37 @@ export default Vue.extend({
return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
},
useOsDefaultEmojis(): boolean {
return this.$store.state.device.useOsDefaultEmojis && !this.isReaction;
useOsNativeEmojis(): boolean {
return this.$store.state.device.useOsNativeEmojis && !this.isReaction;
},
ce() {
let ce = [];
if (this.customEmojis) ce = ce.concat(this.customEmojis);
if (this.$store.state.instance.meta && this.$store.state.instance.meta.emojis) ce = ce.concat(this.$store.state.instance.meta.emojis);
return ce;
}
},
watch: {
customEmojis() {
if (this.name) {
const customEmoji = this.customEmojis.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
ce: {
handler() {
if (this.name) {
const customEmoji = this.ce.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
}
}
}
},
immediate: true
},
},
created() {
if (this.name) {
const customEmoji = this.customEmojis.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
} else {
//const emoji = lib[this.name];
//if (emoji) {
// this.char = emoji.char;
//}
}
} else {
if (!this.name) {
this.char = this.emoji;
}

View File

@ -1,5 +1,6 @@
<template>
<div class="mjndxjcg _panel">
<img src="https://xn--931a.moe/assets/error.png" class="_ghost"/>
<p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p>
<mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button>
</div>
@ -38,5 +39,12 @@ export default Vue.extend({
> .button {
margin: 0 auto;
}
> img {
vertical-align: bottom;
height: 150px;
margin-bottom: 16px;
border-radius: 16px;
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="mk-google">
<input type="search" v-model="query" :placeholder="q">
<button @click="search"><fa icon="search"/> {{ $t('@.search') }}</button>
<button @click="search"><fa icon="search"/> {{ $t('search') }}</button>
</div>
</template>

View File

@ -0,0 +1,101 @@
<template>
<div class="eqryymyo">
<div class="header">
<time ref="time" class="_ghost">
<span class="yyyymmdd">{{ yyyy }}/{{ mm }}/{{ dd }}</span>
<br>
<span class="hhnn">{{ hh }}<span :style="{ visibility: now.getSeconds() % 2 == 0 ? 'visible' : 'hidden' }">:</span>{{ nn }}</span>
</time>
</div>
<div class="content _panel _ghost">
<mk-clock/>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import MkClock from './analog-clock.vue';
export default Vue.extend({
components: {
MkClock
},
data() {
return {
now: new Date(),
clock: null
};
},
computed: {
yyyy(): number {
return this.now.getFullYear();
},
mm(): string {
return ('0' + (this.now.getMonth() + 1)).slice(-2);
},
dd(): string {
return ('0' + this.now.getDate()).slice(-2);
},
hh(): string {
return ('0' + this.now.getHours()).slice(-2);
},
nn(): string {
return ('0' + this.now.getMinutes()).slice(-2);
}
},
mounted() {
this.tick();
this.clock = setInterval(this.tick, 1000);
},
beforeDestroy() {
clearInterval(this.clock);
},
methods: {
tick() {
this.now = new Date();
}
}
});
</script>
<style lang="scss" scoped>
.eqryymyo {
display: inline-block;
overflow: visible;
> .header {
padding: 0 12px;
padding-top: 4px;
text-align: center;
font-size: 12px;
font-family: Lucida Console, Courier, monospace;
&:hover + .content {
opacity: 1;
}
> time {
display: table-cell;
vertical-align: middle;
height: 48px;
> .yyyymmdd {
opacity: 0.7;
}
}
}
> .content {
opacity: 0;
display: block;
position: absolute;
top: auto;
right: 0;
margin: 16px 0 0 0;
padding: 16px;
width: 230px;
transition: opacity 0.2s ease;
}
}
</style>

View File

@ -0,0 +1,54 @@
<template>
<x-modal ref="modal" @closed="() => { $emit('closed'); destroyDom(); }">
<img class="xubzgfga" ref="img" :src="image.url" :alt="image.name" :title="image.name" @click="close" tabindex="-1"/>
</x-modal>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XModal from './modal.vue';
export default Vue.extend({
i18n,
components: {
XModal,
},
props: {
image: {
type: Object,
required: true
},
},
mounted() {
this.$nextTick(() => {
this.$refs.img.focus();
});
},
methods: {
close() {
this.$refs.modal.close();
},
}
});
</script>
<style lang="scss" scoped>
.xubzgfga {
position: fixed;
z-index: 2;
top: 0;
right: 0;
bottom: 0;
left: 0;
max-width: 100%;
max-height: 100%;
margin: auto;
cursor: zoom-out;
image-orientation: from-image;
}
</style>

View File

@ -1,8 +1,91 @@
<template>
<div class="mk-instance-stats">
<div class="zbcjwnqg">
<div class="stats" v-if="info">
<div class="_panel">
<div>
<b><fa :icon="faUser"/>{{ $t('users') }}</b>
<small>{{ $t('local') }}</small>
</div>
<div>
<dl class="total">
<dt>{{ $t('total') }}</dt>
<dd>{{ info.originalUsersCount | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: usersLocalDoD > 0 }">
<dt>{{ $t('dayOverDayChanges') }}</dt>
<dd>{{ usersLocalDoD | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: usersLocalWoW > 0 }">
<dt>{{ $t('weekOverWeekChanges') }}</dt>
<dd>{{ usersLocalWoW | number }}</dd>
</dl>
</div>
</div>
<div class="_panel">
<div>
<b><fa :icon="faUser"/>{{ $t('users') }}</b>
<small>{{ $t('remote') }}</small>
</div>
<div>
<dl class="total">
<dt>{{ $t('total') }}</dt>
<dd>{{ (info.usersCount - info.originalUsersCount) | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: usersRemoteDoD > 0 }">
<dt>{{ $t('dayOverDayChanges') }}</dt>
<dd>{{ usersRemoteDoD | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: usersRemoteWoW > 0 }">
<dt>{{ $t('weekOverWeekChanges') }}</dt>
<dd>{{ usersRemoteWoW | number }}</dd>
</dl>
</div>
</div>
<div class="_panel">
<div>
<b><fa :icon="faPencilAlt"/>{{ $t('notes') }}</b>
<small>{{ $t('local') }}</small>
</div>
<div>
<dl class="total">
<dt>{{ $t('total') }}</dt>
<dd>{{ info.originalNotesCount | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: notesLocalDoD > 0 }">
<dt>{{ $t('dayOverDayChanges') }}</dt>
<dd>{{ notesLocalDoD | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: notesLocalWoW > 0 }">
<dt>{{ $t('weekOverWeekChanges') }}</dt>
<dd>{{ notesLocalWoW | number }}</dd>
</dl>
</div>
</div>
<div class="_panel">
<div>
<b><fa :icon="faPencilAlt"/>{{ $t('notes') }}</b>
<small>{{ $t('remote') }}</small>
</div>
<div>
<dl class="total">
<dt>{{ $t('total') }}</dt>
<dd>{{ (info.notesCount - info.originalNotesCount) | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: notesRemoteDoD > 0 }">
<dt>{{ $t('dayOverDayChanges') }}</dt>
<dd>{{ notesRemoteDoD | number }}</dd>
</dl>
<dl class="diff" :class="{ inc: notesRemoteWoW > 0 }">
<dt>{{ $t('weekOverWeekChanges') }}</dt>
<dd>{{ notesRemoteWoW | number }}</dd>
</dl>
</div>
</div>
</div>
<section class="_card">
<div class="_title"><fa :icon="faChartBar"/> {{ $t('statistics') }}</div>
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
<div class="_content" style="margin-top: -8px;">
<div class="selects" style="display: flex;">
<mk-select v-model="chartSrc" style="margin: 0; flex: 1;">
<optgroup :label="$t('federation')">
@ -40,10 +123,10 @@
<script lang="ts">
import Vue from 'vue';
import { faChartBar } from '@fortawesome/free-solid-svg-icons';
import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import Chart from 'chart.js';
import i18n from '../../i18n';
import MkSelect from '../../components/ui/select.vue';
import i18n from '../i18n';
import MkSelect from './ui/select.vue';
const chartLimit = 90;
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
@ -59,24 +142,27 @@ const alpha = (hex, a) => {
export default Vue.extend({
i18n,
metaInfo() {
return {
title: `${this.$t('statistics')} | ${this.$t('instance')}`
};
},
components: {
MkSelect
},
data() {
return {
info: null,
notesLocalWoW: 0,
notesLocalDoD: 0,
notesRemoteWoW: 0,
notesRemoteDoD: 0,
usersLocalWoW: 0,
usersLocalDoD: 0,
usersRemoteWoW: 0,
usersRemoteDoD: 0,
now: null,
chart: null,
chartInstance: null,
chartSrc: 'notes',
chartSpan: 'hour',
faChartBar
faChartBar, faUser, faPencilAlt
}
},
@ -121,6 +207,8 @@ export default Vue.extend({
},
async created() {
this.info = await this.$root.api('stats');
this.now = new Date();
const [perHour, perDay] = await Promise.all([Promise.all([
@ -154,6 +242,15 @@ export default Vue.extend({
}
};
this.notesLocalWoW = this.info.originalNotesCount - chart.perDay.notes.local.total[7];
this.notesLocalDoD = this.info.originalNotesCount - chart.perDay.notes.local.total[1];
this.notesRemoteWoW = (this.info.notesCount - this.info.originalNotesCount) - chart.perDay.notes.remote.total[7];
this.notesRemoteDoD = (this.info.notesCount - this.info.originalNotesCount) - chart.perDay.notes.remote.total[1];
this.usersLocalWoW = this.info.originalUsersCount - chart.perDay.users.local.total[7];
this.usersLocalDoD = this.info.originalUsersCount - chart.perDay.users.local.total[1];
this.usersRemoteWoW = (this.info.usersCount - this.info.originalUsersCount) - chart.perDay.users.remote.total[7];
this.usersRemoteDoD = (this.info.usersCount - this.info.originalUsersCount) - chart.perDay.users.remote.total[1];
this.chart = chart;
this.renderChart();
@ -489,3 +586,80 @@ export default Vue.extend({
}
});
</script>
<style lang="scss" scoped>
.zbcjwnqg {
> .stats {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin: calc(0px - var(--margin) / 2);
margin-bottom: calc(var(--margin) / 2);
> div {
display: flex;
flex: 1 0 213px;
margin: calc(var(--margin) / 2);
box-sizing: border-box;
padding: 16px 20px;
> div {
width: 50%;
&:first-child {
> b {
display: block;
> [data-icon] {
width: 16px;
margin-right: 8px;
}
}
> small {
margin-left: 16px + 8px;
opacity: 0.7;
}
}
&:last-child {
> dl {
display: flex;
margin: 0;
line-height: 1.5em;
> dt,
> dd {
width: 50%;
margin: 0;
}
> dt {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
&.total {
> dt,
> dd {
font-weight: bold;
}
}
&.diff.inc {
> dd {
color: #82c11c;
&:before {
content: "+";
}
}
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,88 @@
<template>
<component :is="self ? 'router-link' : 'a'" class="xlcxczvw _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
:title="url"
>
<slot></slot>
<fa :icon="faExternalLinkSquareAlt" v-if="target === '_blank'" class="icon"/>
</component>
</template>
<script lang="ts">
import Vue from 'vue';
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { url as local } from '../config';
import XUrlPreview from './url-preview-popup.vue';
export default Vue.extend({
props: {
url: {
type: String,
required: true,
},
rel: {
type: String,
required: false,
}
},
data() {
const self = this.url.startsWith(local);
return {
local,
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
showTimer: null,
hideTimer: null,
preview: null,
faExternalLinkSquareAlt
};
},
methods: {
showPreview() {
if (!document.body.contains(this.$el)) return;
if (this.preview) return;
this.preview = new XUrlPreview({
parent: this,
propsData: {
url: this.url,
source: this.$el
}
}).$mount();
document.body.appendChild(this.preview.$el);
},
closePreview() {
if (this.preview) {
this.preview.destroyDom();
this.preview = null;
}
},
onMouseover() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
}
}
});
</script>
<style lang="scss" scoped>
.xlcxczvw {
word-break: break-all;
> .icon {
padding-left: 2px;
font-size: .9em;
font-weight: 400;
font-style: normal;
}
}
</style>

View File

@ -1,30 +1,63 @@
<template>
<div class="yxspomdl">
<fa :icon="faSpinner" pulse fixed-width class="icon"/>
<div class="yxspomdl" :class="{ inline }">
<div class="ring"></div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
data() {
return {
faSpinner
};
},
props: {
inline: {
type: Boolean,
required: false,
default: false
}
}
});
</script>
<style lang="scss" scoped>
@keyframes ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.yxspomdl {
padding: 32px;
text-align: center;
> .icon {
font-size: 32px;
opacity: 0.5;
&.inline {
display: inline;
padding: 0;
> .ring:after {
width: 32px;
height: 32px;
}
}
> .ring {
display: inline-block;
opacity: 0.7;
vertical-align: middle;
}
> .ring:after {
content: " ";
display: block;
box-sizing: border-box;
width: 48px;
height: 48px;
border-radius: 50%;
border: solid 4px;
border-color: currentColor transparent transparent transparent;
animation: ring 0.5s linear infinite;
}
}
</style>

View File

@ -20,6 +20,7 @@ import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
import ImageViewer from './image-viewer.vue';
export default Vue.extend({
i18n,
@ -60,7 +61,16 @@ export default Vue.extend({
},
methods: {
onClick() {
window.open(this.image.url, '_blank');
if (this.$store.state.device.imageNewTab) {
window.open(this.image.url, '_blank');
} else {
const viewer = this.$root.new(ImageViewer, {
image: this.image
});
this.$once('hook:beforeDestroy', () => {
viewer.close();
});
}
}
}
});

View File

@ -1,27 +1,27 @@
<template>
<x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }">
<sequential-entrance class="rrevdjwt" :class="{ left: align === 'left' }" :delay="15" :direction="direction">
<x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
<sequential-entrance class="rrevdjwt" :class="{ left: align === 'left' }" :delay="15" :direction="direction" ref="items">
<template v-for="(item, i) in items.filter(item => item !== undefined)">
<div v-if="item === null" class="divider" :key="i" :data-index="i"></div>
<span v-else-if="item.type === 'label'" class="label item" :key="i" :data-index="i">
<div v-if="item === null" class="divider" :key="i"></div>
<span v-else-if="item.type === 'label'" class="label item" :key="i">
<span>{{ item.text }}</span>
</span>
<router-link v-else-if="item.type === 'link'" :to="item.to" @click.native="close()" :tabindex="i" class="_button item" :key="i" :data-index="i">
<router-link v-else-if="item.type === 'link'" :to="item.to" @click.native="close()" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<mk-avatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</router-link>
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" @click="close()" :tabindex="i" class="_button item" :key="i" :data-index="i">
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" @click="close()" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</a>
<button v-else-if="item.type === 'user'" @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i" :data-index="i">
<button v-else-if="item.type === 'user'" @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i">
<mk-avatar :user="item.user" class="avatar"/><mk-user-name :user="item.user"/>
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button>
<button v-else @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i" :data-index="i">
<button v-else @click="clicked(item.action)" :tabindex="i" class="_button item" :key="i">
<fa v-if="item.icon" :icon="item.icon" fixed-width/>
<mk-avatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
@ -36,6 +36,7 @@
import Vue from 'vue';
import { faCircle } from '@fortawesome/free-solid-svg-icons';
import XPopup from './popup.vue';
import { focusPrev, focusNext } from '../scripts/focus';
export default Vue.extend({
components: {
@ -69,12 +70,31 @@ export default Vue.extend({
type: String,
required: false
},
viaKeyboard: {
type: Boolean,
required: false
},
},
data() {
return {
faCircle
};
},
computed: {
keymap(): any {
return {
'up|k|shift+tab': this.focusUp,
'down|j|tab': this.focusDown,
};
},
},
mounted() {
if (this.viaKeyboard) {
this.$nextTick(() => {
focusNext(this.$refs.items.$slots.default[0].elm, true);
});
}
},
methods: {
clicked(fn) {
fn();
@ -82,18 +102,18 @@ export default Vue.extend({
},
close() {
this.$refs.popup.close();
},
focusUp() {
focusPrev(document.activeElement);
},
focusDown() {
focusNext(document.activeElement);
}
}
});
</script>
<style lang="scss" scoped>
@keyframes blink {
0% { opacity: 1; }
30% { opacity: 1; }
90% { opacity: 0; }
}
.rrevdjwt {
padding: 8px 0;
@ -105,11 +125,13 @@ export default Vue.extend({
> .item {
display: block;
position: relative;
padding: 8px 16px;
width: 100%;
box-sizing: border-box;
white-space: nowrap;
font-size: 0.9em;
line-height: 20px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
@ -125,6 +147,10 @@ export default Vue.extend({
background: var(--accentDarken);
}
&:not(:active):focus {
box-shadow: 0 0 0 2px var(--focus) inset;
}
&.label {
pointer-events: none;
font-size: 0.7em;
@ -150,7 +176,7 @@ export default Vue.extend({
position: absolute;
top: 5px;
left: 13px;
color: var(--accent);
color: var(--indicator);
font-size: 12px;
animation: blink 1s infinite;
}

View File

@ -2,6 +2,7 @@ import Vue, { VNode } from 'vue';
import { MfmForest } from '../../mfm/types';
import { parse, parsePlain } from '../../mfm/parse';
import MkUrl from './url.vue';
import MkLink from './link.vue';
import MkMention from './mention.vue';
import { concat } from '../../prelude/array';
import MkFormula from './formula.vue';
@ -81,10 +82,10 @@ export default Vue.component('misskey-flavored-markdown', {
attrs: {
style: `display: inline-block; font-size: 150%;`
},
directives: [this.$store.state.settings.disableAnimatedMfm ? {} : {
directives: [this.$store.state.device.animatedMfm ? {
name: 'animate-css',
value: { classes: 'tada', iteration: 'infinite' }
}]
}: {}]
}, genEl(token.children));
}
@ -109,10 +110,10 @@ export default Vue.component('misskey-flavored-markdown', {
attrs: {
style: 'display: inline-block;'
},
directives: [this.$store.state.settings.disableAnimatedMfm ? {} : {
directives: [this.$store.state.device.animatedMfm ? {
name: 'animate-css',
value: { classes: 'rubberBand', iteration: 'infinite' }
}]
} : {}]
}, genEl(token.children));
}
@ -121,9 +122,8 @@ export default Vue.component('misskey-flavored-markdown', {
token.node.props.attr == 'left' ? 'reverse' :
token.node.props.attr == 'alternate' ? 'alternate' :
'normal';
const style = (this.$store.state.settings.disableAnimatedMfm)
? ''
: `animation: spin 1.5s linear infinite; animation-direction: ${direction};`;
const style = this.$store.state.device.animatedMfm
? `animation: spin 1.5s linear infinite; animation-direction: ${direction};` : '';
return (createElement as any)('span', {
attrs: {
style: 'display: inline-block;' + style
@ -134,7 +134,7 @@ export default Vue.component('misskey-flavored-markdown', {
case 'jump': {
return (createElement as any)('span', {
attrs: {
style: (this.$store.state.settings.disableAnimatedMfm) ? 'display: inline-block;' : 'display: inline-block; animation: jump 0.75s linear infinite;'
style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: jump 0.75s linear infinite;' : 'display: inline-block;'
},
}, genEl(token.children));
}
@ -154,22 +154,16 @@ export default Vue.component('misskey-flavored-markdown', {
url: token.node.props.url,
rel: 'nofollow noopener',
},
attrs: {
style: 'color:var(--link);'
}
})];
}
case 'link': {
return [createElement('a', {
attrs: {
class: 'link',
href: token.node.props.url,
return [createElement(MkLink, {
key: Math.random(),
props: {
url: token.node.props.url,
rel: 'nofollow noopener',
target: '_blank',
title: token.node.props.url,
style: 'color:var(--link);'
}
},
}, genEl(token.children))];
}
@ -239,7 +233,6 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'emoji': {
const customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
return [createElement('mk-emoji', {
key: Math.random(),
attrs: {
@ -247,7 +240,7 @@ export default Vue.component('misskey-flavored-markdown', {
name: token.node.props.name
},
props: {
customEmojis: this.customEmojis || customEmojis,
customEmojis: this.customEmojis,
normal: this.plain
}
})];

View File

@ -1,5 +1,5 @@
<template>
<mfm-core v-bind="$attrs" class="havbbuyv" :class="{ nowrap: $attrs['nowrap'] }" v-once/>
<mfm-core v-bind="$attrs" class="havbbuyv" :class="{ nowrap: $attrs['nowrap'] }"/>
</template>
<script lang="ts">
@ -36,5 +36,10 @@ export default Vue.extend({
::v-deep pre {
font-size: 0.8em;
}
::v-deep .title {
text-align: center;
border-bottom: solid 1px var(--divider);
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="mk-modal">
<transition name="bg-fade" appear>
<div class="mk-modal" v-hotkey.global="keymap">
<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
<div class="bg" ref="bg" v-if="show" @click="close()"></div>
</transition>
<transition name="modal" appear @after-leave="() => { $emit('closed'); destroyDom(); }">
<transition :name="$store.state.device.animation ? 'modal' : ''" appear @after-leave="() => { $emit('closed'); destroyDom(); }">
<div class="content" ref="content" v-if="show" @click.self="close()"><slot></slot></div>
</transition>
</div>
@ -20,6 +20,13 @@ export default Vue.extend({
show: true,
};
},
computed: {
keymap(): any {
return {
'esc': this.close,
};
},
},
methods: {
close() {
this.show = false;

View File

@ -77,23 +77,19 @@ export default Vue.extend({
> .admin,
> .moderator {
margin-right: 0.5em;
color: var(--badge);
}
> .username {
margin: 0 .5em 0 0;
overflow: hidden;
text-overflow: ellipsis;
color: var(--noteHeaderAcct);
}
> .info {
margin-left: auto;
font-size: 0.9em;
> * {
color: var(--noteHeaderInfo);
}
> .mobile {
margin-right: 8px;
}

View File

@ -1,200 +0,0 @@
<template>
<x-menu :source="source" :items="items" @closed="closed"/>
</template>
<script lang="ts">
import Vue from 'vue';
import { faStar, faLink, faThumbtack, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import { url } from '../config';
import copyToClipboard from '../scripts/copy-to-clipboard';
import XMenu from './menu.vue';
export default Vue.extend({
i18n,
components: {
XMenu
},
props: ['note', 'source'],
data() {
return {
isFavorited: false,
isWatching: false
};
},
computed: {
items(): any[] {
if (this.$store.getters.isSignedIn) {
return [{
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent
}, {
icon: faLink,
text: this.$t('copyLink'),
action: this.copyLink
}, this.note.uri ? {
icon: faExternalLinkSquareAlt,
text: this.$t('showOnRemote'),
action: () => {
window.open(this.note.uri, '_blank');
}
} : undefined,
null,
this.isFavorited ? {
icon: faStar,
text: this.$t('unfavorite'),
action: () => this.toggleFavorite(false)
} : {
icon: faStar,
text: this.$t('favorite'),
action: () => this.toggleFavorite(true)
},
this.note.userId != this.$store.state.i.id ? this.isWatching ? {
icon: faEyeSlash,
text: this.$t('unwatch'),
action: () => this.toggleWatch(false)
} : {
icon: faEye,
text: this.$t('watch'),
action: () => this.toggleWatch(true)
} : undefined,
this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? {
icon: faThumbtack,
text: this.$t('unpin'),
action: () => this.togglePin(false)
} : {
icon: faThumbtack,
text: this.$t('pin'),
action: () => this.togglePin(true)
} : undefined,
...(this.note.userId == this.$store.state.i.id ? [
null,
{
icon: faTrashAlt,
text: this.$t('delete'),
action: this.del
}]
: []
)]
.filter(x => x !== undefined);
} else {
return [{
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent
}, {
icon: faLink,
text: this.$t('copyLink'),
action: this.copyLink
}, this.note.uri ? {
icon: faExternalLinkSquareAlt,
text: this.$t('showOnRemote'),
action: () => {
window.open(this.note.uri, '_blank');
}
} : undefined]
.filter(x => x !== undefined);
}
}
},
created() {
this.$root.api('notes/state', {
noteId: this.note.id
}).then(state => {
this.isFavorited = state.isFavorited;
this.isWatching = state.isWatching;
});
},
methods: {
copyContent() {
copyToClipboard(this.note.text);
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
},
copyLink() {
copyToClipboard(`${url}/notes/${this.note.id}`);
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
},
togglePin(pin: boolean) {
this.$root.api(pin ? 'i/pin' : 'i/unpin', {
noteId: this.note.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
this.$emit('closed');
this.destroyDom();
}).catch(e => {
if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
this.$root.dialog({
type: 'error',
text: this.$t('pinLimitExceeded')
});
}
});
},
del() {
this.$root.dialog({
type: 'warning',
text: this.$t('noteDeleteConfirm'),
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) return;
this.$root.api('notes/delete', {
noteId: this.note.id
}).then(() => {
this.$emit('closed');
this.destroyDom();
});
});
},
toggleFavorite(favorite: boolean) {
this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
noteId: this.note.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
this.$emit('closed');
this.destroyDom();
});
},
toggleWatch(watch: boolean) {
this.$root.api(watch ? 'notes/watching/create' : 'notes/watching/delete', {
noteId: this.note.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
this.$emit('closed');
this.destroyDom();
});
},
closed() {
this.$emit('closed');
this.$nextTick(() => {
this.destroyDom();
});
}
}
});
</script>

View File

@ -9,7 +9,9 @@
>
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
<x-sub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
<div class="pinned" v-if="pinned"><fa :icon="faThumbtack"/> {{ $t('pinnedNote') }}</div>
<div class="info" v-if="pinned"><fa :icon="faThumbtack"/> {{ $t('pinnedNote') }}</div>
<div class="info" v-if="appearNote._prId_"><fa :icon="faBullhorn"/> {{ $t('promotion') }}<button class="_textButton hide" @click="readPromo()">{{ $t('hideThisNote') }} <fa :icon="faTimes"/></button></div>
<div class="info" v-if="appearNote._featuredId_"><fa :icon="faBolt"/> {{ $t('featured') }}</div>
<div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/>
<fa :icon="faRetweet"/>
@ -19,7 +21,7 @@
</router-link>
</i18n>
<div class="info">
<button class="_button time" @click="showRenoteMenu"><mk-time :time="note.createdAt"/></button>
<button class="_button time" @click="showRenoteMenu()" ref="renoteTime"><mk-time :time="note.createdAt"/></button>
<span class="visibility" v-if="note.visibility != 'public'">
<fa v-if="note.visibility == 'home'" :icon="faHome"/>
<fa v-if="note.visibility == 'followers'" :icon="faUnlock"/>
@ -58,7 +60,7 @@
<template v-else><fa :icon="faReply"/></template>
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
<button v-if="['public', 'home'].includes(appearNote.visibility)" @click="renote()" class="button _button" ref="renoteButton">
<button v-if="canRenote" @click="renote()" class="button _button" ref="renoteButton">
<fa :icon="faRetweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button>
<button v-else class="button _button">
@ -83,7 +85,8 @@
<script lang="ts">
import Vue from 'vue';
import { faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array';
import i18n from '../i18n';
@ -95,21 +98,11 @@ import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
import XPoll from './poll.vue';
import XUrlPreview from './url-preview.vue';
import MkNoteMenu from './note-menu.vue';
import MkReactionPicker from './reaction-picker.vue';
import MkRenotePicker from './renote-picker.vue';
import pleaseLogin from '../scripts/please-login';
function focus(el, fn) {
const target = fn(el);
if (target) {
if (target.hasAttribute('tabindex')) {
target.focus();
} else {
focus(target, fn);
}
}
}
import { focusPrev, focusNext } from '../scripts/focus';
import { url } from '../config';
import copyToClipboard from '../scripts/copy-to-clipboard';
export default Vue.extend({
i18n,
@ -149,8 +142,7 @@ export default Vue.extend({
replies: [],
showContent: false,
hideThisNote: false,
openingMenu: false,
faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan
faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan
};
},
@ -200,16 +192,16 @@ export default Vue.extend({
return this.$store.getters.isSignedIn && (this.$store.state.i.id === this.appearNote.userId);
},
canRenote(): boolean {
return ['public', 'home'].includes(this.appearNote.visibility) || this.isMyNote;
},
reactionsCount(): number {
return this.appearNote.reactions
? sum(Object.values(this.appearNote.reactions))
: 0;
},
title(): string {
return '';
},
urls(): string[] {
if (this.appearNote.text) {
const ast = parse(this.appearNote.text);
@ -273,9 +265,16 @@ export default Vue.extend({
},
methods: {
readPromo() {
(this as any).$root.api('promo/read', {
noteId: this.appearNote.id
});
this.hideThisNote = true;
},
capture(withHandler = false) {
if (this.$store.getters.isSignedIn) {
this.connection.send('sn', { id: this.appearNote.id });
this.connection.send(document.body.contains(this.$el) ? 'sn' : 's', { id: this.appearNote.id });
if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated);
}
},
@ -370,13 +369,30 @@ export default Vue.extend({
});
},
renote() {
renote(viaKeyboard = false) {
pleaseLogin(this.$root);
this.blur();
this.$root.new(MkRenotePicker, {
this.$root.menu({
items: [{
text: this.$t('renote'),
icon: faRetweet,
action: () => {
(this as any).$root.api('notes/create', {
renoteId: this.appearNote.id
});
}
}, {
text: this.$t('quote'),
icon: faQuoteRight,
action: () => {
this.$root.post({
renote: this.appearNote,
});
}
}]
source: this.$refs.renoteButton,
note: this.appearNote,
}).$once('closed', this.focus);
viaKeyboard
}).then(this.focus);
},
renoteDirectly() {
@ -444,21 +460,124 @@ export default Vue.extend({
});
},
menu(viaKeyboard = false) {
if (this.openingMenu) return;
this.openingMenu = true;
const w = this.$root.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.appearNote,
animation: !viaKeyboard
}).$once('closed', () => {
this.openingMenu = false;
this.focus();
toggleFavorite(favorite: boolean) {
this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
noteId: this.appearNote.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
});
},
showRenoteMenu(ev) {
if (!this.isMyNote) return;
toggleWatch(watch: boolean) {
this.$root.api(watch ? 'notes/watching/create' : 'notes/watching/delete', {
noteId: this.appearNote.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
});
},
async menu(viaKeyboard = false) {
let menu;
if (this.$store.getters.isSignedIn) {
const state = await this.$root.api('notes/state', {
noteId: this.appearNote.id
});
menu = [{
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent
}, {
icon: faLink,
text: this.$t('copyLink'),
action: this.copyLink
}, this.appearNote.uri ? {
icon: faExternalLinkSquareAlt,
text: this.$t('showOnRemote'),
action: () => {
window.open(this.appearNote.uri, '_blank');
}
} : undefined,
null,
state.isFavorited ? {
icon: faStar,
text: this.$t('unfavorite'),
action: () => this.toggleFavorite(false)
} : {
icon: faStar,
text: this.$t('favorite'),
action: () => this.toggleFavorite(true)
},
this.appearNote.userId != this.$store.state.i.id ? state.isWatching ? {
icon: faEyeSlash,
text: this.$t('unwatch'),
action: () => this.toggleWatch(false)
} : {
icon: faEye,
text: this.$t('watch'),
action: () => this.toggleWatch(true)
} : undefined,
this.appearNote.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.appearNote.id) ? {
icon: faThumbtack,
text: this.$t('unpin'),
action: () => this.togglePin(false)
} : {
icon: faThumbtack,
text: this.$t('pin'),
action: () => this.togglePin(true)
} : undefined,
...(this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [
null,
{
icon: faBullhorn,
text: this.$t('promote'),
action: this.promote
}]
: []
),
...(this.appearNote.userId == this.$store.state.i.id ? [
null,
{
icon: faTrashAlt,
text: this.$t('delete'),
action: this.del
}]
: []
)]
.filter(x => x !== undefined);
} else {
menu = [{
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent
}, {
icon: faLink,
text: this.$t('copyLink'),
action: this.copyLink
}, this.appearNote.uri ? {
icon: faExternalLinkSquareAlt,
text: this.$t('showOnRemote'),
action: () => {
window.open(this.appearNote.uri, '_blank');
}
} : undefined]
.filter(x => x !== undefined);
}
this.$root.menu({
items: menu,
source: this.$refs.menuButton,
viaKeyboard
}).then(this.focus);
},
showRenoteMenu(viaKeyboard = false) {
if (!this.$store.getters.isSignedIn || (this.$store.state.i.id !== this.note.userId)) return;
this.$root.menu({
items: [{
text: this.$t('unrenote'),
@ -470,7 +589,8 @@ export default Vue.extend({
Vue.set(this.note, 'deletedAt', new Date());
}
}],
source: ev.currentTarget || ev.target,
source: this.$refs.renoteTime,
viaKeyboard: viaKeyboard
});
},
@ -478,6 +598,64 @@ export default Vue.extend({
this.showContent = !this.showContent;
},
copyContent() {
copyToClipboard(this.appearNote.text);
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
},
copyLink() {
copyToClipboard(`${url}/notes/${this.appearNote.id}`);
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
},
togglePin(pin: boolean) {
this.$root.api(pin ? 'i/pin' : 'i/unpin', {
noteId: this.appearNote.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
this.$root.dialog({
type: 'error',
text: this.$t('pinLimitExceeded')
});
}
});
},
async promote() {
const { canceled, result: days } = await this.$root.dialog({
title: this.$t('numberOfDays'),
input: { type: 'number' }
});
if (canceled) return;
this.$root.api('admin/promo/create', {
noteId: this.appearNote.id,
expiresAt: Date.now() + (86400000 * days)
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
},
focus() {
this.$el.focus();
},
@ -487,11 +665,11 @@ export default Vue.extend({
},
focusBefore() {
focus(this.$el, e => e.previousElementSibling);
focusPrev(this.$el);
},
focusAfter() {
focus(this.$el, e => e.nextElementSibling);
focusNext(this.$el);
}
}
});
@ -574,7 +752,9 @@ export default Vue.extend({
border-radius: 0 0 var(--radius) var(--radius);
}
> .pinned {
> .info {
display: flex;
align-items: center;
padding: 16px 32px 8px 32px;
line-height: 24px;
font-size: 90%;
@ -588,9 +768,14 @@ export default Vue.extend({
> [data-icon] {
margin-right: 4px;
}
> .hide {
margin-left: auto;
color: inherit;
}
}
> .pinned + .article {
> .info + .article {
padding-top: 8px;
}
@ -727,7 +912,7 @@ export default Vue.extend({
}
&:hover {
color: var(--mkykhqkw);
color: var(--fgHighlighted);
}
> .count {

View File

@ -1,54 +1,49 @@
<template>
<div class="mk-notes" v-size="[{ max: 500 }]">
<div class="empty" v-if="empty">{{ $t('noNotes') }}</div>
<div class="empty" v-if="empty">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div>{{ $t('noNotes') }}</div>
</div>
<mk-error v-if="error" @retry="init()"/>
<x-list ref="notes" class="notes" :items="notes" v-slot="{ item: note, i }">
<x-note :note="note" :detail="detail" :key="note.id" :data-index="i"/>
<div class="more" v-if="more && reversed" style="margin-bottom: var(--margin);">
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template>
</mk-button>
</div>
<x-list ref="notes" class="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
<x-note :note="note" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/>
</x-list>
<footer v-if="more">
<button @click="fetchMore()" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" class="_buttonPrimary">
<div class="more" v-if="more && !reversed" style="margin-top: var(--margin);">
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
</button>
</footer>
<template v-if="moreFetching"><mk-loading inline/></template>
</mk-button>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import paging from '../scripts/paging';
import XNote from './note.vue';
import XList from './date-separated-list.vue';
import getUserName from '../../misc/get-user-name';
import getNoteSummary from '../../misc/get-note-summary';
import MkButton from './ui/button.vue';
export default Vue.extend({
i18n,
components: {
XNote, XList
XNote, XList, MkButton
},
mixins: [
paging({
onPrepend: (self, note) => {
// タブが非表示なら通知
if (document.hidden) {
if ('Notification' in window && Notification.permission === 'granted') {
new Notification(getUserName(note.user), {
body: getNoteSummary(note),
icon: note.user.avatarUrl,
tag: 'newNote'
});
}
}
},
before: (self) => {
self.$emit('before');
},
@ -75,16 +70,14 @@ export default Vue.extend({
}
},
data() {
return {
faSpinner
};
},
computed: {
notes(): any[] {
return this.extract ? this.extract(this.items) : this.items;
},
reversed(): boolean {
return this.pagination.reversed;
}
},
methods: {
@ -98,47 +91,36 @@ export default Vue.extend({
<style lang="scss" scoped>
.mk-notes {
> .empty {
margin: 0 auto;
padding: 32px;
text-align: center;
background: rgba(0, 0, 0, 0.3);
color: #fff;
-webkit-backdrop-filter: blur(16px);
backdrop-filter: blur(16px);
border-radius: 6px;
> img {
vertical-align: bottom;
height: 128px;
margin-bottom: 16px;
border-radius: 16px;
}
}
> .notes {
> ::v-deep * {
> ::v-deep *:not(:last-child) {
margin-bottom: var(--marginFull);
}
}
&.max-width_500px {
> .notes {
> ::v-deep * {
> ::v-deep *:not(:last-child) {
margin-bottom: var(--marginHalf);
}
}
}
> footer {
text-align: center;
&:empty {
display: none;
}
> button {
margin: 0;
padding: 16px;
width: 100%;
border-radius: var(--radius);
&:disabled {
opacity: 0.7;
}
}
> .more > .button {
margin-left: auto;
margin-right: auto;
height: 48px;
width: 100%;
}
}
</style>

View File

@ -6,6 +6,7 @@
<fa :icon="faPlus" v-if="notification.type === 'follow'"/>
<fa :icon="faClock" v-if="notification.type === 'receiveFollowRequest'"/>
<fa :icon="faCheck" v-if="notification.type === 'followRequestAccepted'"/>
<fa :icon="faIdCardAlt" v-if="notification.type === 'groupInvited'"/>
<fa :icon="faRetweet" v-if="notification.type === 'renote'"/>
<fa :icon="faReply" v-if="notification.type === 'reply'"/>
<fa :icon="faAt" v-if="notification.type === 'mention'"/>
@ -40,13 +41,14 @@
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $t('youGotNewFollower') }}<div v-if="full"><mk-follow-button :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $t('followRequestAccepted') }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $t('receiveFollowRequest') }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $t('reject') }}</button></div></span>
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ $t('groupInvited') }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ $t('reject') }}</button></div></span>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck } from '@fortawesome/free-solid-svg-icons';
import { faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck } from '@fortawesome/free-solid-svg-icons';
import { faClock } from '@fortawesome/free-regular-svg-icons';
import getNoteSummary from '../../misc/get-note-summary';
import XReactionIcon from './reaction-icon.vue';
@ -78,7 +80,8 @@ export default Vue.extend({
return {
getNoteSummary,
followRequestDone: false,
faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck
groupInviteDone: false,
faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck
};
},
methods: {
@ -90,6 +93,18 @@ export default Vue.extend({
this.followRequestDone = true;
this.$root.api('following/requests/reject', { userId: this.notification.user.id });
},
acceptGroupInvitation() {
this.groupInviteDone = true;
this.$root.api('users/groups/invitations/accept', { invitationId: this.notification.invitation.id });
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
},
rejectGroupInvitation() {
this.groupInviteDone = true;
this.$root.api('users/groups/invitations/reject', { invitationId: this.notification.invitation.id });
},
}
});
</script>
@ -149,12 +164,12 @@ export default Vue.extend({
height: 100%;
}
&.follow, &.followRequestAccepted, &.receiveFollowRequest {
&.follow, &.followRequestAccepted, &.receiveFollowRequest, &.groupInvited {
padding: 3px;
background: #36aed2;
}
&.retweet {
&.renote {
padding: 3px;
background: #36d298;
}

View File

@ -2,7 +2,7 @@
<div class="mk-notifications">
<div class="contents">
<x-list class="notifications" :items="items" v-slot="{ item: notification, i }">
<x-notification :notification="notification" :with-time="true" :full="true" class="notification" :key="notification.id" :data-index="i"/>
<x-notification :notification="notification" :with-time="true" :full="true" class="notification" :key="notification.id"/>
</x-list>
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">

View File

@ -1,6 +1,6 @@
<template>
<div class="lzyxtsnt">
<img v-if="image" :src="image.url" alt=""/>
<img v-if="image" :src="image.url"/>
</div>
</template>

View File

@ -1,9 +1,9 @@
<template>
<div class="mk-popup">
<transition name="bg-fade" appear>
<div class="mk-popup" v-hotkey.global="keymap">
<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
<div class="bg" ref="bg" @click="close()" v-if="show"></div>
</transition>
<transition name="popup" appear @after-leave="() => { $emit('closed'); destroyDom(); }">
<transition :name="$store.state.device.animation ? 'popup' : ''" appear @after-leave="() => { $emit('closed'); destroyDom(); }">
<div class="content" :class="{ fixed }" ref="content" v-if="show" :style="{ width: width ? width + 'px' : 'auto' }"><slot></slot></div>
</transition>
</div>
@ -35,6 +35,13 @@ export default Vue.extend({
show: true,
};
},
computed: {
keymap(): any {
return {
'esc': this.close,
};
},
},
mounted() {
this.$nextTick(() => {
const popover = this.$refs.content as any;
@ -96,8 +103,8 @@ export default Vue.extend({
methods: {
close() {
this.show = false;
(this.$refs.bg as any).style.pointerEvents = 'none';
(this.$refs.content as any).style.pointerEvents = 'none';
if (this.$refs.bg) (this.$refs.bg as any).style.pointerEvents = 'none';
if (this.$refs.content) (this.$refs.content as any).style.pointerEvents = 'none';
}
}
});
@ -134,7 +141,7 @@ export default Vue.extend({
position: absolute;
z-index: 10001;
background: var(--panel);
border-radius: 4px;
border-radius: 8px;
box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15);
overflow: hidden;
transform-origin: center top;

View File

@ -1,10 +1,10 @@
<template>
<div class="ulveipglmagnxfgvitaxyszerjwiqmwl">
<transition name="form-fade" appear>
<transition :name="$store.state.device.animation ? 'form-fade' : ''" appear>
<div class="bg" ref="bg" v-if="show" @click="close()"></div>
</transition>
<div class="main" ref="main" @click.self="close()" @keydown="onKeydown">
<transition name="form" appear
<transition :name="$store.state.device.animation ? 'form' : ''" appear
@after-leave="destroyDom"
>
<x-post-form ref="form"

View File

@ -8,20 +8,20 @@
<header>
<button class="cancel _button" @click="cancel"><fa :icon="faTimes"/></button>
<div>
<span class="text-count" :class="{ over: trimmedLength(text) > 500 }">{{ 500 - trimmedLength(text) }}</span>
<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
<button class="_button visibility" @click="setVisibility" ref="visibilityButton">
<span v-if="visibility === 'public'"><fa :icon="faGlobe"/></span>
<span v-if="visibility === 'home'"><fa :icon="faHome"/></span>
<span v-if="visibility === 'followers'"><fa :icon="faUnlock"/></span>
<span v-if="visibility === 'specified'"><fa :icon="faEnvelope"/></span>
</button>
<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}</button>
<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<fa :icon="reply ? faReply : renote ? faQuoteRight : faPaperPlane"/></button>
</div>
</header>
<div class="form">
<x-note-preview class="preview" v-if="reply" :note="reply"/>
<x-note-preview class="preview" v-if="renote" :note="renote"/>
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('@.post-form.quote-attached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('quoteAttached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
<div v-if="visibility === 'specified'" class="to-specified">
<span style="margin-right: 8px;">{{ $t('recipient') }}</span>
<div class="visibleUsers">
@ -52,7 +52,7 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard } from '@fortawesome/free-solid-svg-icons';
import { faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard } from '@fortawesome/free-solid-svg-icons';
import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz';
@ -130,7 +130,7 @@ export default Vue.extend({
draghover: false,
quoteId: null,
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard
faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faChartPie, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard
};
},
@ -153,7 +153,7 @@ export default Vue.extend({
this.$t('_postForm._placeholders.f')
];
const x = xs[Math.floor(Math.random() * xs.length)];
return this.renote
? this.$t('_postForm.quotePlaceholder')
: this.reply
@ -163,23 +163,27 @@ export default Vue.extend({
submitText(): string {
return this.renote
? this.$t('renote')
? this.$t('quote')
: this.reply
? this.$t('reply')
: this.$t('post');
: this.$t('note');
},
canPost(): boolean {
return !this.posting &&
(1 <= this.text.length || 1 <= this.files.length || this.poll || this.renote) &&
(length(this.text.trim()) <= 500) &&
(length(this.text.trim()) <= this.max) &&
(!this.poll || this.pollChoices.length >= 2);
},
max(): number {
return this.$store.state.instance.meta ? this.$store.state.instance.meta.maxNoteTextLength : 1000;
}
},
watch: {
localOnly() {
this.$store.commit('device/setLocalOnly', this.localOnly);
this.$store.commit('deviceUser/setLocalOnly', this.localOnly);
}
},
@ -215,9 +219,9 @@ export default Vue.extend({
}
// デフォルト公開範囲
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? this.$store.state.device.visibility : this.$store.state.settings.defaultNoteVisibility);
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : this.$store.state.settings.defaultNoteVisibility);
this.localOnly = this.$store.state.settings.rememberNoteVisibility ? this.$store.state.device.localOnly : false;
this.localOnly = this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.localOnly : this.$store.state.settings.defaultNoteLocalOnly;
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {
@ -398,8 +402,7 @@ export default Vue.extend({
},
applyVisibility(v: string) {
if (!['public', 'home', 'followers', 'specified'].includes(v)) v = 'public'; // v11互換性のため
this.visibility = v;
this.visibility = ['public', 'home', 'followers', 'specified'].includes(v) ? v : 'public'; // v11互換性のため
},
addVisibleUser() {
@ -442,7 +445,7 @@ export default Vue.extend({
this.$root.dialog({
type: 'info',
text: this.$t('@.post-form.quote-question'),
text: this.$t('quoteQuestion'),
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) {
@ -607,6 +610,7 @@ export default Vue.extend({
right: 0;
> .text-count {
opacity: 0.7;
line-height: 66px;
@media (max-width: 500px) {
@ -622,8 +626,9 @@ export default Vue.extend({
> .submit {
margin: 16px 16px 16px 0;
padding: 0 16px;
padding: 0 12px;
line-height: 34px;
font-weight: bold;
vertical-align: bottom;
border-radius: 4px;
@ -634,6 +639,10 @@ export default Vue.extend({
&:disabled {
opacity: 0.7;
}
> [data-icon] {
margin-left: 6px;
}
}
}
}
@ -709,7 +718,7 @@ export default Vue.extend({
border-radius: 0;
background: transparent;
color: var(--fg);
font-family: initial;
font-family: inherit;
@media (max-width: 500px) {
padding: 0 16px;

View File

@ -1,5 +1,5 @@
<template>
<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :is-reaction="true" :custom-emojis="customEmojis" :normal="true" :no-style="noStyle"/>
<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :is-reaction="true" :normal="true" :no-style="noStyle"/>
</template>
<script lang="ts">
@ -18,15 +18,5 @@ export default Vue.extend({
default: false
},
},
data() {
return {
customEmojis: []
};
},
created() {
this.$root.getMeta().then(meta => {
if (meta && meta.emojis) this.customEmojis = meta.emojis;
});
},
});
</script>

View File

@ -13,7 +13,7 @@
mode="out-in"
appear
>
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :data-index="i" :tabindex="i + 1" :title="/^[a-z]+$/.test(reaction) ? $t('@.reactions.' + reaction) : reaction"><x-reaction-icon :reaction="reaction"/></button>
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction"><x-reaction-icon :reaction="reaction"/></button>
</transition-group>
<input class="text" v-model="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
</div>

View File

@ -1,94 +0,0 @@
<template>
<x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
<div class="rdfaahpc">
<button class="_button" @click="quote()"><fa :icon="faQuoteRight"/></button>
<button class="_button" @click="renote()"><fa :icon="faRetweet"/></button>
</div>
</x-popup>
</template>
<script lang="ts">
import Vue from 'vue';
import { faQuoteRight, faRetweet } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import XPopup from './popup.vue';
export default Vue.extend({
i18n,
components: {
XPopup,
},
props: {
note: {
type: Object,
required: true
},
source: {
required: true
},
},
data() {
return {
faQuoteRight, faRetweet
};
},
computed: {
keymap(): any {
return {
'esc': this.close,
};
}
},
methods: {
renote() {
(this as any).$root.api('notes/create', {
renoteId: this.note.id
}).then(() => {
this.$emit('closed');
this.destroyDom();
});
},
quote() {
this.$emit('closed');
this.destroyDom();
this.$root.post({
renote: this.note,
});
}
}
});
</script>
<style lang="scss" scoped>
.rdfaahpc {
padding: 4px;
> button {
padding: 0;
width: 40px;
height: 40px;
font-size: 16px;
border-radius: 2px;
> * {
height: 1em;
}
&:hover {
background: rgba(0, 0, 0, 0.05);
}
&:active {
background: var(--accent);
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
}
}
}
</style>

View File

@ -1,16 +1,17 @@
<template>
<transition-group
name="staggered-fade"
<transition-group v-if="$store.state.device.animation"
class="uupnnhew"
:data-direction="direction"
:data-reversed="reversed ? 'true' : 'false'"
name="staggered"
tag="div"
:css="false"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
mode="out-in"
appear
>
<slot></slot>
</transition-group>
<div v-else>
<slot></slot>
</div>
</template>
<script lang="ts">
@ -27,37 +28,53 @@ export default Vue.extend({
type: String,
required: false,
default: 'down'
},
reversed: {
type: Boolean,
required: false,
default: false
}
},
methods: {
beforeEnter(el) {
el.style.opacity = 0;
el.style.transform = this.direction === 'down' ? 'translateY(-64px)' : 'translateY(64px)';
},
enter(el, done) {
el.style.transition = [getComputedStyle(el).transition, 'transform 0.7s cubic-bezier(0.23, 1, 0.32, 1)', 'opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1)'].filter(x => x != '').join(',');
setTimeout(() => {
el.style.opacity = 1;
el.style.transform = 'translateY(0px)';
setTimeout(done, 700);
}, this.delay * el.dataset.index)
},
leave(el, done) {
setTimeout(() => {
el.style.opacity = 0;
el.style.transform = this.direction === 'down' ? 'translateY(64px)' : 'translateY(-64px)';
setTimeout(done, 700);
}, this.delay * el.dataset.index)
},
focus() {
this.$slots.default[0].elm.focus();
}
}
},
});
</script>
<style lang="scss">
.staggered-fade-move {
transition: transform 0.7s !important;
.staggered-move {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) !important;
}
.uupnnhew[data-direction="up"] {
.staggered-enter {
opacity: 0;
transform: translateY(64px);
}
}
.uupnnhew[data-direction="down"] {
.staggered-enter {
opacity: 0;
transform: translateY(-64px);
}
}
.uupnnhew[data-reversed="true"] {
@for $i from 1 through 30 {
.staggered-enter-active:nth-last-child(#{$i}) {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1)), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1));
}
}
}
.uupnnhew[data-reversed="false"] {
@for $i from 1 through 30 {
.staggered-enter-active:nth-child(#{$i}) {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1)), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1));
}
}
}
</style>

View File

@ -12,15 +12,15 @@
<template #prefix><fa :icon="faLock"/></template>
</mk-input>
<mk-button type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</mk-button>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p>
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p>
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="faTwitter"/> {{ $t('signinWith', { x: 'Twitter' }) }}</a></p>
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="faGithub"/> {{ $t('signinWith', { x: 'GitHub' }) }}</a></p>
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="faDiscord"/> {{ $t('signinWith', { x: 'Discord' }) }}</a></p>
</div>
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
<p>{{ $t('tap-key') }}</p>
<p>{{ $t('tapSecurityKey') }}</p>
<mk-button @click="queryKey" v-if="!queryingKey">
{{ $t('@.error.retry') }}
{{ $t('retry') }}
</mk-button>
</div>
<div class="or-hr" v-if="user && user.securityKeys">
@ -46,6 +46,7 @@
import Vue from 'vue';
import { toUnicode } from 'punycode';
import { faLock, faGavel } from '@fortawesome/free-solid-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
import i18n from '../i18n';
@ -82,20 +83,21 @@ export default Vue.extend({
token: '',
apiUrl,
host: toUnicode(host),
meta: null,
totpLogin: false,
credential: null,
challengeData: null,
queryingKey: false,
faLock, faGavel
faLock, faGavel, faTwitter, faDiscord, faGithub
};
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
created() {
if (this.autoSet) {
this.$once('login', res => {
localStorage.setItem('i', res.i);

View File

@ -1,7 +1,7 @@
<template>
<x-window @closed="() => { $emit('closed'); destroyDom(); }">
<x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }">
<template #header>{{ $t('signup') }}</template>
<x-signup/>
<x-signup :auto-set="autoSet" @signup="onSignup"/>
</x-window>
</template>
@ -18,5 +18,20 @@ export default Vue.extend({
XSignup,
XWindow,
},
props: {
autoSet: {
type: Boolean,
required: false,
default: false,
}
},
methods: {
onSignup(res) {
this.$emit('signup', res);
this.$refs.window.close();
}
}
});
</script>

View File

@ -2,9 +2,8 @@
<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
<template v-if="meta">
<mk-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
<span>{{ $t('invitation-code') }}</span>
<template #prefix><fa icon="id-card-alt"/></template>
<template #desc v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainerEmail)"></template>
<span>{{ $t('invitationCode') }}</span>
<template #prefix><fa :icon="faKey"/></template>
</mk-input>
<mk-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername">
<span>{{ $t('username') }}</span>
@ -15,26 +14,26 @@
<span v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('available') }}</span>
<span v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('unavailable') }}</span>
<span v-if="usernameState == 'error'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('error') }}</span>
<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('invalid-format') }}</span>
<span v-if="usernameState == 'min-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('too-short') }}</span>
<span v-if="usernameState == 'max-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('too-long') }}</span>
<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('usernameInvalidFormat') }}</span>
<span v-if="usernameState == 'min-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('tooShort') }}</span>
<span v-if="usernameState == 'max-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('tooLong') }}</span>
</template>
</mk-input>
<mk-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword">
<span>{{ $t('password') }}</span>
<template #prefix><fa :icon="faLock"/></template>
<template #desc>
<p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('weak-password') }}</p>
<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('normal-password') }}</p>
<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('strong-password') }}</p>
<p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('weakPassword') }}</p>
<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('normalPassword') }}</p>
<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('strongPassword') }}</p>
</template>
</mk-input>
<mk-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
<span>{{ $t('password') }} ({{ $t('retype') }})</span>
<template #prefix><fa :icon="faLock"/></template>
<template #desc>
<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('password-matched') }}</p>
<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('password-not-matched') }}</p>
<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('passwordMatched') }}</p>
<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('passwordNotMatched') }}</p>
</template>
</mk-input>
<mk-switch v-model="ToSAgreement" v-if="meta.tosUrl">
@ -50,7 +49,7 @@
<script lang="ts">
import Vue from 'vue';
import { faLock, faExclamationTriangle, faSpinner, faCheck } from '@fortawesome/free-solid-svg-icons';
import { faLock, faExclamationTriangle, faSpinner, faCheck, faKey } from '@fortawesome/free-solid-svg-icons';
const getPasswordStrength = require('syuilo-password-strength');
import { toUnicode } from 'punycode';
import i18n from '../i18n';
@ -79,14 +78,25 @@ export default Vue.extend({
usernameState: null,
passwordStrength: '',
passwordRetypeState: null,
meta: {},
submitting: false,
ToSAgreement: false,
faLock, faExclamationTriangle, faSpinner, faCheck
faLock, faExclamationTriangle, faSpinner, faCheck, faKey
}
},
props: {
autoSet: {
type: Boolean,
required: false,
default: false,
}
},
computed: {
meta() {
return this.$store.state.instance.meta;
},
shouldShowProfileUrl(): boolean {
return (this.username != '' &&
this.usernameState != 'invalid-format' &&
@ -96,9 +106,12 @@ export default Vue.extend({
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
if (this.autoSet) {
this.$once('signup', res => {
localStorage.setItem('i', res.i);
location.reload();
});
}
},
mounted() {
@ -170,15 +183,14 @@ export default Vue.extend({
username: this.username,
password: this.password
}).then(res => {
localStorage.setItem('i', res.i);
location.href = '/';
this.$emit('signup', res);
});
}).catch(() => {
this.submitting = false;
this.$root.dialog({
type: 'error',
text: this.$t('some-error')
text: this.$t('error')
});
if (this.meta.enableRecaptcha) {

View File

@ -48,7 +48,7 @@ export default Vue.extend({
ago >= 10 ? this.$t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
ago >= -1 ? this.$t('_ago.justNow') :
ago < -1 ? this.$t('_ago.future') :
this.$t('@.time.unknown'));
this.$t('_ago.unknown'));
}
},
created() {

View File

@ -27,6 +27,7 @@ export default Vue.extend({
data() {
return {
connection: null,
connection2: null,
pagination: null,
baseQuery: {
includeMyRenotes: this.$store.state.settings.showMyRenotes,
@ -40,6 +41,7 @@ export default Vue.extend({
created() {
this.$once('hook:beforeDestroy', () => {
this.connection.dispose();
if (this.connection2) this.connection2.dispose();
});
const prepend = note => {
@ -54,6 +56,12 @@ export default Vue.extend({
(this.$refs.tl as any).reload();
};
const onChangeFollowing = () => {
if (!this.$refs.tl.backed) {
this.$refs.tl.reload();
}
};
let endpoint;
if (this.src == 'antenna') {
@ -67,13 +75,12 @@ export default Vue.extend({
this.connection.on('note', prepend);
} else if (this.src == 'home') {
endpoint = 'notes/timeline';
const onChangeFollowing = () => {
this.fetch();
};
this.connection = this.$root.stream.useSharedConnection('homeTimeline');
this.connection.on('note', prepend);
this.connection.on('follow', onChangeFollowing);
this.connection.on('unfollow', onChangeFollowing);
this.connection2 = this.$root.stream.useSharedConnection('main');
this.connection2.on('follow', onChangeFollowing);
this.connection2.on('unfollow', onChangeFollowing);
} else if (this.src == 'local') {
endpoint = 'notes/local-timeline';
this.connection = this.$root.stream.useSharedConnection('localTimeline');

View File

@ -105,7 +105,7 @@ export default Vue.extend({
padding: 8px 14px;
text-align: center;
font-weight: normal;
font-size: 14px;
font-size: 0.9em;
line-height: 24px;
box-shadow: none;
text-decoration: none;
@ -124,6 +124,7 @@ export default Vue.extend({
&.primary {
color: #fff;
background: var(--accent);
box-shadow: 0 6px 16px var(--accentShadow);
&:not(:disabled):hover {
background: var(--jkhztclx);

View File

@ -8,9 +8,16 @@
<template v-else><fa :icon="faAngleDown"/></template>
</button>
</header>
<div v-show="showBody">
<slot></slot>
</div>
<transition name="container-toggle"
@enter="enter"
@after-enter="afterEnter"
@leave="leave"
@after-leave="afterLeave"
>
<div v-show="showBody">
<slot></slot>
</div>
</transition>
</div>
</template>
@ -51,12 +58,42 @@ export default Vue.extend({
toggleContent(show: boolean) {
if (!this.bodyTogglable) return;
this.showBody = show;
}
},
enter(el) {
const elementHeight = el.getBoundingClientRect().height;
el.style.height = 0;
el.offsetHeight; // reflow
el.style.height = elementHeight + 'px';
},
afterEnter(el) {
el.style.height = null;
},
leave(el) {
const elementHeight = el.getBoundingClientRect().height;
el.style.height = elementHeight + 'px';
el.offsetHeight; // reflow
el.style.height = 0;
},
afterLeave(el) {
el.style.height = null;
},
}
});
</script>
<style lang="scss" scoped>
.container-toggle-enter-active, .container-toggle-leave-active {
overflow-y: hidden;
transition: opacity 0.5s, height 0.5s !important;
}
.container-toggle-enter {
opacity: 0;
}
.container-toggle-leave-to {
opacity: 0;
}
.ukygtjoj {
position: relative;
overflow: hidden;
@ -72,6 +109,7 @@ export default Vue.extend({
> header {
position: relative;
box-shadow: 0 1px 0 0 var(--divider);
> .title {
margin: 0;

View File

@ -239,6 +239,14 @@ export default Vue.extend({
position: relative;
margin: 32px 0;
&:not(.inline):first-child {
margin-top: 8px;
}
&:not(.inline):last-child {
margin-bottom: 8px;
}
> .icon {
position: absolute;
top: 0;
@ -254,7 +262,7 @@ export default Vue.extend({
> .input {
position: relative;
&:before {
content: '';
display: block;
@ -327,14 +335,16 @@ export default Vue.extend({
}
> input {
$height: 32px;
display: block;
height: $height;
width: 100%;
margin: 0;
padding: 0;
font: inherit;
font-weight: normal;
font-size: 16px;
line-height: 32px;
line-height: $height;
color: var(--inputText);
background: transparent;
border: none;

View File

@ -5,9 +5,9 @@
<slot name="empty"></slot>
</div>
<div class="more" v-if="more" key="_more_">
<mk-button :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()">
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
<template v-if="moreFetching"><mk-loading inline/></template>
</mk-button>
</div>
</sequential-entrance>
@ -15,7 +15,6 @@
<script lang="ts">
import Vue from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import MkButton from './button.vue';
import paging from '../../scripts/paging';
@ -37,12 +36,6 @@ export default Vue.extend({
default: true
}
},
data() {
return {
faSpinner
};
},
});
</script>
@ -55,5 +48,12 @@ export default Vue.extend({
margin-bottom: 8px;
}
}
> .more > .button {
margin-left: auto;
margin-right: auto;
height: 48px;
min-width: 150px;
}
}
</style>

View File

@ -77,6 +77,14 @@ export default Vue.extend({
position: relative;
margin: 32px 0;
&:not(.inline):first-child {
margin-top: 8px;
}
&:not(.inline):last-child {
margin-bottom: 8px;
}
> .icon {
position: absolute;
top: 0;

View File

@ -129,7 +129,6 @@ export default Vue.extend({
> .label {
margin-left: 8px;
display: block;
font-size: 16px;
cursor: pointer;
transition: inherit;
color: var(--fg);

View File

@ -85,6 +85,10 @@ export default Vue.extend({
margin: 42px 0 32px 0;
position: relative;
&:first-child {
margin-top: 16px;
}
&:last-child {
margin-bottom: 0;
}

View File

@ -0,0 +1,56 @@
<template>
<div class="fgmtyycl _panel" :style="{ top: top + 'px', left: left + 'px' }">
<x-url-preview :url="url"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import XUrlPreview from './url-preview.vue';
export default Vue.extend({
i18n,
components: {
XUrlPreview
},
props: {
url: {
type: String,
required: true
},
source: {
required: true
}
},
data() {
return {
u: null,
top: 0,
left: 0,
};
},
mounted() {
const rect = this.source.getBoundingClientRect();
const x = ((rect.left + (this.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
const y = rect.top + this.source.offsetHeight + window.pageYOffset;
this.top = y;
this.left = x;
},
});
</script>
<style lang="scss" scoped>
.fgmtyycl {
position: absolute;
z-index: 11000;
width: 500px;
overflow: hidden;
pointer-events: none;
}
</style>

View File

@ -10,7 +10,7 @@
</div>
<div v-else class="mk-url-preview" v-size="[{ max: 400 }, { max: 350 }]">
<transition name="zoom" mode="out-in">
<component :is="hasRoute ? 'router-link' : 'a'" :class="{ compact }" :[attr]="hasRoute ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<component :is="self ? 'router-link' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
<button class="_button" v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enable-player')"><fa :icon="faPlayCircle"/></button>
</div>
@ -58,12 +58,7 @@ export default Vue.extend({
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute =
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/');
const self = this.url.startsWith(local);
return {
local,
fetching: true,
@ -79,10 +74,9 @@ export default Vue.extend({
},
tweetUrl: null,
playerEnabled: false,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
faPlayCircle
};
},

View File

@ -1,5 +1,8 @@
<template>
<component :is="hasRoute ? 'router-link' : 'a'" class="mk-url" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target">
<component :is="self ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
>
<template v-if="!self">
<span class="schema">{{ schema }}//</span>
<span class="hostname">{{ hostname }}</span>
@ -20,28 +23,35 @@ import Vue from 'vue';
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { toUnicode as decodePunycode } from 'punycode';
import { url as local } from '../config';
import XUrlPreview from './url-preview-popup.vue';
export default Vue.extend({
props: ['url', 'rel'],
props: {
url: {
type: String,
required: true,
},
rel: {
type: String,
required: false,
}
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute = isSelf && (
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/'));
const self = this.url.startsWith(local);
return {
local,
schema: null,
hostname: null,
port: null,
pathname: null,
query: null,
hash: null,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
schema: null as string | null,
hostname: null as string | null,
port: null as string | null,
pathname: null as string | null,
query: null as string | null,
hash: null as string | null,
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
showTimer: null,
hideTimer: null,
preview: null,
faExternalLinkSquareAlt
};
},
@ -53,12 +63,44 @@ export default Vue.extend({
this.pathname = decodeURIComponent(url.pathname);
this.query = decodeURIComponent(url.search);
this.hash = decodeURIComponent(url.hash);
},
methods: {
showPreview() {
if (!document.body.contains(this.$el)) return;
if (this.preview) return;
this.preview = new XUrlPreview({
parent: this,
propsData: {
url: this.url,
source: this.$el
}
}).$mount();
document.body.appendChild(this.preview.$el);
},
closePreview() {
if (this.preview) {
this.preview.destroyDom();
this.preview = null;
}
},
onMouseover() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
}
}
});
</script>
<style lang="scss" scoped>
.mk-url {
.ieqqeuvs {
word-break: break-all;
> .icon {

View File

@ -23,7 +23,7 @@
<mk-follow-button class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
</div>
<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMore()" :disabled="moreFetching">
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('@.loading') : $t('@.load-more') }}
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }}
</button>
</div>
</mk-container>

View File

@ -4,7 +4,7 @@
<script lang="ts">
import Vue from 'vue';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments } from '@fortawesome/free-solid-svg-icons';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import XMenu from './menu.vue';
@ -43,7 +43,11 @@ export default Vue.extend({
icon: faListUl,
text: this.$t('addToList'),
action: this.pushList
}] as any;
}, this.$store.state.i.id != this.user.id ? {
icon: faUsers,
text: this.$t('inviteToGroup'),
action: this.inviteGroup
} : undefined] as any;
if (this.$store.getters.isSignedIn && this.$store.state.i.id != this.user.id) {
menu = menu.concat([null, {
@ -118,6 +122,42 @@ export default Vue.extend({
});
},
async inviteGroup() {
const groups = await this.$root.api('users/groups/owned');
if (groups.length === 0) {
this.$root.dialog({
type: 'error',
text: this.$t('youHaveNoGroups')
});
return;
}
const { canceled, result: groupId } = await this.$root.dialog({
type: null,
title: this.$t('group'),
select: {
items: groups.map(group => ({
value: group.id, text: group.name
}))
},
showCancelButton: true
});
if (canceled) return;
this.$root.api('users/groups/invite', {
groupId: groupId,
userId: this.user.id
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
},
async toggleMute() {
this.$root.api(this.user.isMuted ? 'mute/delete' : 'mute/create', {
userId: this.user.id

View File

@ -3,7 +3,7 @@
<template #header><mk-user-name :user="user"/></template>
<div class="vrcsvlkm">
<mk-button @click="resetPassword()" primary>{{ $t('resetPassword') }}</mk-button>
<mk-switch v-if="$store.state.i.isAdmin" @change="toggleModerator()" v-model="moderator">{{ $t('moderator') }}</mk-switch>
<mk-switch v-if="$store.state.i.isAdmin && !user.isAdmin" @change="toggleModerator()" v-model="moderator">{{ $t('moderator') }}</mk-switch>
<mk-switch @change="toggleSilence()" v-model="silenced">{{ $t('silence') }}</mk-switch>
<mk-switch @change="toggleSuspend()" v-model="suspended">{{ $t('suspend') }}</mk-switch>
</div>
@ -47,7 +47,7 @@ export default Vue.extend({
type: 'waiting',
iconOnly: true
});
this.$root.api('admin/reset-password', {
userId: this.user.id,
}).then(({ password }) => {

View File

@ -84,7 +84,7 @@ export default Vue.extend({
methods: {
close() {
this.show = false;
(this.$refs.content as any).style.pointerEvents = 'none';
if (this.$refs.content) (this.$refs.content as any).style.pointerEvents = 'none';
}
}
});

View File

@ -7,7 +7,7 @@
</div>
<sequential-entrance class="users">
<router-link v-for="(item, i) in items" class="user" :key="item.id" :data-index="i" :to="extract ? extract(item) : item | userPage">
<router-link v-for="(item, i) in items" class="user" :key="item.id" :to="extract ? extract(item) : item | userPage">
<mk-avatar :user="extract ? extract(item) : item" class="avatar" :disable-link="true"/>
<div class="body">
<mk-user-name :user="extract ? extract(item) : item" class="name"/>

View File

@ -56,14 +56,14 @@ export default Vue.extend({
},
data() {
return {
v: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.device.visibility : (this.currentVisibility || this.$store.state.settings.defaultNoteVisibility),
v: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : (this.currentVisibility || this.$store.state.settings.defaultNoteVisibility),
faGlobe, faUnlock, faEnvelope, faHome
}
},
methods: {
choose(visibility) {
if (this.$store.state.settings.rememberNoteVisibility) {
this.$store.commit('device/setVisibility', visibility);
this.$store.commit('deviceUser/setVisibility', visibility);
}
this.$emit('chosen', visibility);
this.destroyDom();

View File

@ -3,7 +3,7 @@ declare const _VERSION_: string;
declare const _ENV_: string;
const address = new URL(location.href);
const siteName = document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement;
const siteName = (document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement)?.content;
export const host = address.host;
export const hostname = address.hostname;
@ -15,4 +15,4 @@ export const langs = _LANGS_;
export const locale = JSON.parse(localStorage.getItem('locale'));
export const version = _VERSION_;
export const env = _ENV_;
export const instanceName = siteName && siteName.content ? siteName.content : 'Misskey';
export const instanceName = siteName === 'Misskey' ? null : siteName;

View File

@ -1,3 +1,5 @@
import { ResizeObserver } from '@juggle/resize-observer';
export default {
inserted(el, binding, vn) {
const query = binding.value;
@ -52,13 +54,18 @@ export default {
calc();
el._sizeResizeCb_ = calc;
window.addEventListener('resize', calc);
vn.context.$on('hook:activated', calc);
const ro = new ResizeObserver((entries, observer) => {
calc();
});
ro.observe(el);
el._ro_ = ro;
},
unbind(el, binding, vn) {
window.removeEventListener('resize', el._sizeResizeCb_);
el._ro_.unobserve(el);
}
};

View File

@ -61,20 +61,22 @@ if (localStorage.getItem('theme') == null) {
}
//#region Detect the user language
let lang = null;
let lang = localStorage.getItem('lang');
if (langs.map(x => x[0]).includes(navigator.language)) {
lang = navigator.language;
} else {
lang = langs.map(x => x[0]).find(x => x.split('-')[0] == navigator.language);
if (lang == null) {
if (langs.map(x => x[0]).includes(navigator.language)) {
lang = navigator.language;
} else {
lang = langs.map(x => x[0]).find(x => x.split('-')[0] == navigator.language);
if (lang == null) {
// Fallback
lang = 'en-US';
if (lang == null) {
// Fallback
lang = 'en-US';
}
}
}
localStorage.setItem('lang', lang);
localStorage.setItem('lang', lang);
}
//#endregion
// Detect the user agent
@ -134,8 +136,6 @@ document.body.innerHTML = '<div id="app"></div>';
const os = new MiOS();
os.init(async () => {
if (os.store.state.settings.wallpaper) document.documentElement.style.backgroundImage = `url(${os.store.state.settings.wallpaper})`;
if ('Notification' in window && os.store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
@ -147,7 +147,7 @@ os.init(async () => {
store: os.store,
metaInfo: {
title: null,
titleTemplate: title => title ? `${title} | ${instanceName}` : instanceName
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
},
data() {
return {
@ -157,8 +157,6 @@ os.init(async () => {
},
methods: {
api: os.api,
getMeta: os.getMeta,
getMetaSync: os.getMetaSync,
signout: os.signout,
new(vm, props) {
const x = new vm({

View File

@ -17,16 +17,6 @@ let pending = 0;
* Misskey Operating System
*/
export default class MiOS extends EventEmitter {
/**
* Misskeyの /meta で取得できるメタ情報
*/
private meta: {
data: { [x: string]: any };
chachedAt: Date;
};
private isMetaFetching = false;
public app: Vue;
public store: ReturnType<typeof initStore>;
@ -53,6 +43,15 @@ export default class MiOS extends EventEmitter {
*/
@autobind
public async init(callback) {
const finish = () => {
callback();
this.store.dispatch('instance/fetch').then(() => {
// Init service worker
if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
});
};
this.store = initStore(this);
// ユーザーをフェッチしてコールバックする
@ -60,7 +59,7 @@ export default class MiOS extends EventEmitter {
let me = null;
// Return when not signed in
if (token == null) {
if (token == null || token === 'null') {
return done();
}
@ -88,7 +87,7 @@ export default class MiOS extends EventEmitter {
// When failure
.catch(() => {
// Render the error screen
document.body.innerHTML = '<div id="err">Error</div>';
document.body.innerHTML = '<div id="err">Oops!</div>';
Progress.done();
});
@ -105,12 +104,7 @@ export default class MiOS extends EventEmitter {
this.initStream();
// Finish init
callback();
// Init service worker
this.getMeta().then(data => {
if (data.swPublickey) this.registerSw(data.swPublickey);
});
finish();
};
// キャッシュがあったとき
@ -139,7 +133,7 @@ export default class MiOS extends EventEmitter {
this.initStream();
// Finish init
callback();
finish();
}
});
}
@ -233,7 +227,6 @@ export default class MiOS extends EventEmitter {
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
alert(locale['common']['my-token-regenerated']);
this.signout();
});
}
@ -350,49 +343,6 @@ export default class MiOS extends EventEmitter {
return promise;
}
/**
* Misskeyのメタ情報を取得します
*/
@autobind
public getMetaSync() {
return this.meta ? this.meta.data : null;
}
/**
* Misskeyのメタ情報を取得します
* @param force キャッシュを無視するか否か
*/
@autobind
public getMeta(force = false) {
return new Promise<{ [x: string]: any }>(async (res, rej) => {
if (this.isMetaFetching) {
this.once('_meta_fetched_', () => {
res(this.meta.data);
});
return;
}
const expire = 1000 * 60; // 1min
// forceが有効, meta情報を保持していない or 期限切れ
if (force || this.meta == null || Date.now() - this.meta.chachedAt.getTime() > expire) {
this.isMetaFetching = true;
const meta = await this.api('meta', {
detail: false
});
this.meta = {
data: meta,
chachedAt: new Date()
};
this.isMetaFetching = false;
this.emit('_meta_fetched_');
res(meta);
} else {
res(this.meta.data);
}
});
}
}
/**

View File

@ -0,0 +1,79 @@
<template>
<div class="znqjceqz">
<portal to="title">🍀 {{ $t('aboutMisskey') }}</portal>
<section class="_card">
<div class="_title">🍀 {{ $t('aboutMisskey') }}</div>
<div class="_content">
<div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div>
<div>🛠 {{ $t('misskeyMembers') }}</div>
<ul class="members">
<li><mk-link url="https://github.com/syuilo" class="at">@syuilo</mk-link></li>
<li><mk-link url="https://github.com/AyaMorisawa" class="at">@AyaMorisawa</mk-link></li>
<li><mk-link url="https://github.com/mei23" class="at">@mei23</mk-link></li>
<li><mk-link url="https://github.com/acid-chicken" class="at">@acid-chicken</mk-link></li>
<li><mk-link url="https://github.com/tamaina" class="at">@tamaina</mk-link></li>
<li><mk-link url="https://github.com/rinsuki" class="at">@rinsuki</mk-link></li>
</ul>
<div style="margin-top: 1em;">📦 {{ $t('misskeySource') }}</div>
<mk-url url="https://github.com/syuilo/misskey"/>
<div style="margin-top: 1em;">🌏 {{ $t('misskeyTranslation') }}</div>
<mk-url url="https://crowdin.com/project/misskey"/>
<div style="margin-top: 1em;">💴 {{ $t('misskeyDonate') }}</div>
<mk-url url="https://www.patreon.com/syuilo"/>
</div>
<div class="_content">
<span><mfm text="<motion>❤</motion>"/> {{ $t('patrons') }}</span>
<ul>
<li>Gargron</li>
<li>Satsuki Yanagi</li>
<li>noellabo</li>
<li>naga_rus</li>
<li>Melilot</li>
<li>AureoleArk</li>
<li>Peter G.</li>
<li>motcha</li>
<li>Atsuko Tominaga</li>
<li>dansup</li>
<li>Nokotaro Takeda</li>
<li>YUKIMOCHI</li>
<li>nanami kan</li>
<li>Hekovic</li>
<li>wara</li>
<li>Takashi Shibuya</li>
<li>Noizeman</li>
</ul>
<span>{{ $t('morePatrons') }}</span>
</div>
</section>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { version } from '../config';
import i18n from '../i18n';
import MkLink from '../components/link.vue';
export default Vue.extend({
i18n,
components: {
MkLink
},
metaInfo() {
return {
title: this.$t('aboutMisskey') as string
};
},
data() {
return {
version,
faInfoCircle
}
},
});
</script>

View File

@ -6,63 +6,18 @@
<section class="_card info" v-if="meta">
<div class="_title"><fa :icon="faInfoCircle"/> {{ $t('instanceInfo') }}</div>
<div class="_content" v-if="meta.description">
<div>{{ meta.description }}</div>
<div v-html="meta.description"></div>
</div>
<div class="_content table">
<div><b>{{ $t('administrator') }}</b><span>{{ meta.maintainerName }}</span></div>
<div><b></b><span>{{ meta.maintainerEmail }}</span></div>
</div>
<div class="_content table" v-if="stats">
<div><b>{{ $t('users') }}</b><span>{{ stats.originalUsersCount | number }}</span></div>
<div><b>{{ $t('notes') }}</b><span>{{ stats.originalNotesCount | number }}</span></div>
</div>
<div class="_content table">
<div><b>Misskey</b><span>v{{ version }}</span></div>
</div>
</section>
<section class="_card aboutMisskey">
<div class="_title"><fa :icon="faInfoCircle"/> {{ $t('aboutMisskey') }}</div>
<div class="_content">
<div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div>
<div>{{ $t('misskeyMembers') }}</div>
<span class="members">
<a href="https://github.com/syuilo" target="_blank">@syuilo</a>
<a href="https://github.com/AyaMorisawa" target="_blank">@AyaMorisawa</a>
<a href="https://github.com/mei23" target="_blank">@mei23</a>
<a href="https://github.com/acid-chicken" target="_blank">@acid-chicken</a>
<a href="https://github.com/tamaina" target="_blank">@tamaina</a>
<a href="https://github.com/rinsuki" target="_blank">@rinsuki</a>
</span>
<div style="margin-top: 1em;">{{ $t('misskeySource') }}</div>
<a href="https://github.com/syuilo/misskey" target="_blank" style="color: var(--link);">https://github.com/syuilo/misskey</a>
<div style="margin-top: 1em;">{{ $t('misskeyDonate') }}</div>
<a href="https://www.patreon.com/syuilo" target="_blank" style="color: var(--link);">https://www.patreon.com/syuilo</a>
</div>
<div class="_content">
<span><mfm text="<motion>❤</motion>"/> {{ $t('patrons') }}</span>
<ul>
<li>Gargron</li>
<li>Satsuki Yanagi</li>
<li>noellabo</li>
<li>naga_rus</li>
<li>Melilot</li>
<li>AureoleArk</li>
<li>Peter G.</li>
<li>motcha</li>
<li>Atsuko Tominaga</li>
<li>dansup</li>
<li>Nokotaro Takeda</li>
<li>YUKIMOCHI</li>
<li>nanami kan</li>
<li>Hekovic</li>
<li>wara</li>
<li>Takashi Shibuya</li>
<li>Noizeman</li>
</ul>
<span>{{ $t('morePatrons') }}</span>
</div>
</section>
<mk-instance-stats style="margin-top: var(--margin);"/>
</div>
</template>
@ -71,6 +26,7 @@ import Vue from 'vue';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { version } from '../config';
import i18n from '../i18n';
import MkInstanceStats from '../components/instance-stats.vue';
export default Vue.extend({
i18n,
@ -81,24 +37,22 @@ export default Vue.extend({
};
},
components: {
MkInstanceStats
},
data() {
return {
version,
meta: null,
stats: null,
serverInfo: null,
faInfoCircle
}
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
this.$root.api('stats').then(res => {
this.stats = res;
});
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
});
</script>
@ -116,16 +70,5 @@ export default Vue.extend({
}
}
}
> .aboutMisskey {
> ._content {
> .members {
> a {
color: var(--link);
margin-right: 0.5em;
}
}
}
}
}
</style>

View File

@ -4,11 +4,11 @@
<portal to="title">{{ $t('announcements') }}</portal>
<mk-pagination :pagination="pagination" #default="{items}" class="ruryvtyk" ref="list">
<section class="_card announcement" v-for="(announcement, i) in items" :key="announcement.id" :data-index="i">
<section class="_card announcement" v-for="(announcement, i) in items" :key="announcement.id">
<div class="_title"><span v-if="$store.getters.isSignedIn && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
<div class="_content">
<mfm :text="announcement.text"/>
<img v-if="announcement.imageUrl" :src="announcement.imageUrl" alt=""/>
<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
</div>
<div class="_footer" v-if="$store.getters.isSignedIn && !announcement.isRead">
<mk-button @click="read(announcement)" primary><fa :icon="faCheck"/> {{ $t('gotIt') }}</mk-button>

146
src/client/pages/doc.vue Normal file
View File

@ -0,0 +1,146 @@
<template>
<div>
<portal to="icon"><fa :icon="faFileAlt"/></portal>
<portal to="title">{{ title }}</portal>
<main class="_card">
<div class="_title"><fa :icon="faFileAlt"/> {{ title }}</div>
<div class="_content">
<div v-html="body" class="qyqbqfal"></div>
</div>
<div class="_footer">
<mk-link :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${doc}.ja-JP.md`" class="at">{{ $t('docSource') }}</mk-link>
</div>
</main>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faFileAlt } from '@fortawesome/free-solid-svg-icons'
import MarkdownIt from 'markdown-it';
import i18n from '../i18n';
import { url, lang } from '../config';
import MkLink from '../components/link.vue';
const markdown = MarkdownIt({
html: true
});
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.title,
};
},
components: {
MkLink
},
props: {
doc: {
type: String,
required: true
}
},
watch: {
doc: {
handler() {
this.fetchDoc();
},
immediate: true,
}
},
data() {
return {
faFileAlt,
title: '',
body: '',
markdown: '',
}
},
methods: {
fetchDoc() {
fetch(`${url}/assets/docs/${this.doc}.${lang}.md`).then(res => res.text()).then(md => {
this.parse(md);
});
},
parse(md: string) {
// markdown の全容をパースする
const parsed = markdown.parse(md, {});
if (parsed.length === 0) return;
const buf = [...parsed];
const headingTokens = [];
let headingStart = 0;
// もっとも上にある見出しを抽出する
while (buf[0].type !== 'heading_open') {
buf.shift();
headingStart++;
}
buf.shift();
while (buf[0].type as string !== 'heading_close') {
const token = buf.shift();
if (token) {
headingTokens.push(token);
}
}
// 抽出した見出しを除く部分をbodyとして抽出する
const bodyTokens = [...parsed];
bodyTokens.splice(headingStart, headingTokens.length + 2);
// 各々レンダーする
this.title = markdown.renderer.render(headingTokens, {}, {});
this.body = markdown.renderer.render(bodyTokens, {}, {});
}
}
});
</script>
<style lang="scss" scoped>
.qyqbqfal {
> *:first-child {
margin-top: 0;
}
> *:last-child {
margin-bottom: 0;
}
::v-deep h2 {
font-size: 1.25em;
padding: 0 0 0.5em 0;
border-bottom: solid 1px var(--divider);
}
::v-deep table {
width: 100%;
max-width: 100%;
overflow: auto;
}
::v-deep kbd.group {
display: inline-block;
padding: 2px;
border: 1px solid var(--divider);
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
::v-deep kbd.key {
display: inline-block;
padding: 6px 8px;
border: solid 1px var(--divider);
border-radius: 4px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
}
</style>

42
src/client/pages/docs.vue Normal file
View File

@ -0,0 +1,42 @@
<template>
<div>
<portal to="icon"><fa :icon="faQuestionCircle"/></portal>
<portal to="title">{{ $t('help') }}</portal>
<main class="_card">
<div class="_content">
<ul>
<li v-for="doc in docs" :key="doc.path">
<router-link :to="`/docs/${doc.path}`">{{ doc.title }}</router-link>
</li>
</ul>
</div>
</main>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'
import { url, lang } from '../config';
export default Vue.extend({
metaInfo() {
return {
title: this.$t('help') as string,
};
},
data() {
return {
docs: [],
faQuestionCircle
}
},
created() {
fetch(`${url}/docs.json?lang=${lang}`).then(res => res.json()).then(docs => {
this.docs = docs;
});
},
});
</script>

View File

@ -57,11 +57,11 @@ export default Vue.extend({
}, this.folder ? {
text: this.$t('renameFolder'),
icon: faICursor,
action: () => { this.$refs.drive.renameFolder(); }
action: () => { this.$refs.drive.renameFolder(this.folder); }
} : undefined, this.folder ? {
text: this.$t('deleteFolder'),
icon: faTrashAlt,
action: () => { this.$refs.drive.deleteFolder(); }
action: () => { this.$refs.drive.deleteFolder(this.folder); }
} : undefined, {
text: this.$t('createFolder'),
icon: faFolderPlus,

View File

@ -115,13 +115,15 @@ export default Vue.extend({
tagsLocal: [],
tagsRemote: [],
stats: null,
meta: null,
num: Vue.filter('number'),
faBookmark, faChartLine, faCommentAlt, faPlus, faHashtag, faRocket
};
},
computed: {
meta() {
return this.$store.state.instance.meta;
},
tagUsers(): any {
return {
endpoint: 'hashtags/users',
@ -159,9 +161,6 @@ export default Vue.extend({
this.$root.api('stats').then(stats => {
this.stats = stats;
});
this.$root.getMeta().then(meta => {
this.meta = meta;
});
},
});
</script>

View File

@ -1,27 +1,40 @@
<template>
<mk-pagination :pagination="pagination" #default="{items}" class="mk-follow-requests" ref="list">
<div class="user _panel" v-for="(req, i) in items" :key="req.id" :data-index="i">
<mk-avatar class="avatar" :user="req.follower"/>
<div class="body">
<div class="name">
<router-link class="name" :to="req.follower | userPage" v-user-preview="req.follower.id"><mk-user-name :user="req.follower"/></router-link>
<p class="acct">@{{ req.follower | acct }}</p>
<div>
<portal to="icon"><fa :icon="faUserClock"/></portal>
<portal to="title">{{ $t('followRequests') }}</portal>
<mk-pagination :pagination="pagination" class="mk-follow-requests" ref="list">
<template #empty>
<div class="tkdrhpxr">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div>{{ $t('noFollowRequests') }}</div>
</div>
<div class="description" v-if="req.follower.description" :title="req.follower.description">
<mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$store.state.i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/>
</template>
<template #default="{items}">
<div class="user _panel" v-for="req in items" :key="req.id">
<mk-avatar class="avatar" :user="req.follower"/>
<div class="body">
<div class="name">
<router-link class="name" :to="req.follower | userPage" v-user-preview="req.follower.id"><mk-user-name :user="req.follower"/></router-link>
<p class="acct">@{{ req.follower | acct }}</p>
</div>
<div class="description" v-if="req.follower.description" :title="req.follower.description">
<mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$store.state.i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/>
</div>
<div class="actions">
<button class="_button" @click="accept(req.follower)"><fa :icon="faCheck"/></button>
<button class="_button" @click="reject(req.follower)"><fa :icon="faTimes"/></button>
</div>
</div>
</div>
<div class="actions">
<button class="_button" @click="accept(req.follower)"><fa :icon="faCheck"/></button>
<button class="_button" @click="reject(req.follower)"><fa :icon="faTimes"/></button>
</div>
</div>
</div>
</mk-pagination>
</template>
</mk-pagination>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faCheck, faTimes } from '@fortawesome/free-solid-svg-icons';
import { faUserClock, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons';
import MkPagination from '../components/ui/pagination.vue';
export default Vue.extend({
@ -41,7 +54,7 @@ export default Vue.extend({
endpoint: 'following/requests/list',
limit: 10,
},
faCheck, faTimes
faCheck, faTimes, faUserClock
};
},
@ -62,6 +75,18 @@ export default Vue.extend({
<style lang="scss" scoped>
.mk-follow-requests {
.tkdrhpxr {
padding: 32px;
text-align: center;
> img {
vertical-align: bottom;
height: 128px;
margin-bottom: 16px;
border-radius: 16px;
}
}
> .user {
display: flex;
padding: 16px;

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