Compare commits

...

294 Commits

Author SHA1 Message Date
f05cb79604 10.21.3 2018-10-16 22:26:43 +09:00
46d3293edd Fix #2920 2018-10-16 22:21:08 +09:00
9703d613cf 10.21.2 2018-10-16 20:59:49 +09:00
704e217dbb オブジェクトストレージのURLに拡張子を含めるように 2018-10-16 20:59:36 +09:00
a103032d94 10.21.1 2018-10-16 20:34:32 +09:00
c7207a4bd7 Fix #2919 2018-10-16 20:33:13 +09:00
35c65fe589 Clean up 2018-10-16 20:29:35 +09:00
6d5bd0c484 10.21.0 2018-10-16 20:19:55 +09:00
cfbb6e8092 オブジェクトストレージのURLにファイル名を含めるのを廃止 2018-10-16 20:14:06 +09:00
feef4a933e Update src/tools/clean-remote-files.ts 2018-10-16 20:10:46 +09:00
468bc67569 Improve test 2018-10-16 19:52:14 +09:00
0d517fa52f Greenkeeper/monorepo.fortawesome 5.4.1 (#2917)
* fix(package): update @fortawesome/fontawesome-svg-core to version 1.2.6

Closes #2861

* fix(package): update @fortawesome/free-regular-svg-icons to version 5.4.1

Closes #2861

* fix(package): update @fortawesome/free-solid-svg-icons to version 5.4.1

Closes #2861
2018-10-16 12:28:45 +09:00
d9054367c1 fix(package): update @fortawesome/free-brands-svg-icons to version 5.4.1 (#2916)
Closes #2859
2018-10-16 12:27:03 +09:00
1213373027 Use cache with RSS (#2915) 2018-10-16 12:24:54 +09:00
100a525507 Update autogen.sh (#2914)
refs: 8b98c08a81 (commitcomment-30911933)
2018-10-16 11:45:11 +09:00
1bec4e2d12 10.20.0 2018-10-16 11:44:35 +09:00
03cd1d27bf New Crowdin translations (#2910)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)
2018-10-16 11:38:43 +09:00
9427a756c9 Update mongodb 2018-10-16 11:38:09 +09:00
d32b2a8ce5 fix(package): update @types/node to version 10.12.0 (#2912) 2018-10-16 10:46:45 +09:00
15473b4368 fix(package): update @types/webpack to version 4.4.17 (#2911) 2018-10-16 10:46:38 +09:00
54de0dc4a7 Update config for CI 2018-10-16 10:36:27 +09:00
0162eaf826 Update signin.ts 2018-10-16 10:33:05 +09:00
572cfafbe1 Add some API tests 2018-10-16 10:18:47 +09:00
4d6335ce9a Add some tests and fix 2018-10-16 09:45:36 +09:00
1c9c4af9f1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-16 08:58:54 +09:00
a6844ebc9d Add some tests 2018-10-16 08:58:45 +09:00
072492c29b Implement /api/v1/instance/peers (#2913)
* Implement /api/v1/instance/peers

* Use punycode

* Remove Cache-Control

* Rename
2018-10-16 08:55:55 +09:00
99da4f9839 Add some tests and some fixes 2018-10-16 08:54:36 +09:00
88664486af Refactor 2018-10-16 08:27:20 +09:00
80daf7c749 Implement API tests 2018-10-16 06:37:21 +09:00
beb2f7e558 fix(package): update @types/sharp to version 0.21.0 (#2908) 2018-10-16 05:21:07 +09:00
6243184c95 fix(package): update @types/gulp-uglify to version 3.0.6 (#2906) 2018-10-16 05:20:57 +09:00
1b3baef966 Merge pull request #2898 from syuilo/l10n_develop
New Crowdin translations
2018-10-16 05:20:40 +09:00
98f38ee29b fix(package): update vue-svg-inline-loader to version 1.2.1 (#2909) 2018-10-16 05:20:17 +09:00
09b82bfea4 fix(package): update chart.js to version 2.7.3 (#2907) 2018-10-16 05:20:02 +09:00
937f686264 New translations ja-JP.yml (Russian) 2018-10-15 20:53:38 +09:00
9bc9cbac21 New translations ja-JP.yml (English) 2018-10-15 18:22:42 +09:00
6024550158 New translations ja-JP.yml (Norwegian) 2018-10-15 18:14:29 +09:00
4ae5f82171 New translations ja-JP.yml (Dutch) 2018-10-15 18:14:23 +09:00
6d2c9dcee9 New translations ja-JP.yml (Japanese, Kansai) 2018-10-15 18:14:18 +09:00
0f1b0e1870 New translations ja-JP.yml (Spanish) 2018-10-15 18:14:11 +09:00
81c682cdc8 New translations ja-JP.yml (Russian) 2018-10-15 18:14:05 +09:00
ab9fa67d9f New translations ja-JP.yml (Portuguese) 2018-10-15 18:14:00 +09:00
9537fce335 New translations ja-JP.yml (Polish) 2018-10-15 18:13:53 +09:00
9d97e7e348 New translations ja-JP.yml (Korean) 2018-10-15 18:13:48 +09:00
ebe7939412 New translations ja-JP.yml (Italian) 2018-10-15 18:13:43 +09:00
807e3e8ca7 New translations ja-JP.yml (German) 2018-10-15 18:13:37 +09:00
a59faf9117 New translations ja-JP.yml (French) 2018-10-15 18:13:32 +09:00
d786036155 New translations ja-JP.yml (English) 2018-10-15 18:13:25 +09:00
61d6ed5489 New translations ja-JP.yml (Chinese Simplified) 2018-10-15 18:13:21 +09:00
b38200d48a New translations ja-JP.yml (Catalan) 2018-10-15 18:13:16 +09:00
a0c396a842 10.19.0 2018-10-15 18:03:28 +09:00
88fbc53e37 Resolve #2314 2018-10-15 18:02:57 +09:00
a2206b2d52 🎨 2018-10-15 17:55:59 +09:00
a95ff447d7 🎨 2018-10-15 17:43:25 +09:00
49dbd7f9d2 Fix following from Preroma does not complete (#2905)
* In Follow Accept/Reject, send previous received id

* In Follow Accept/Reject, send Activity.actor
2018-10-15 16:51:22 +09:00
2ad2779096 10.18.0 2018-10-15 06:03:50 +09:00
23045369aa 🎨 2018-10-15 06:03:15 +09:00
116faf26e6 10.17.0 2018-10-15 05:29:58 +09:00
2582b8d132 🎨 2018-10-15 05:28:35 +09:00
63f7941073 🎨 2018-10-15 05:18:39 +09:00
676f026085 🎨 2018-10-15 04:36:31 +09:00
a13319fd86 New translations ja-JP.yml (Norwegian) 2018-10-14 19:52:13 +09:00
be8765278c New translations ja-JP.yml (Dutch) 2018-10-14 19:52:08 +09:00
c8bb3dc209 New translations ja-JP.yml (Japanese, Kansai) 2018-10-14 19:52:03 +09:00
ea16befb73 New translations ja-JP.yml (Spanish) 2018-10-14 19:51:59 +09:00
20b1bb7681 New translations ja-JP.yml (Russian) 2018-10-14 19:51:55 +09:00
bd10eb50eb New translations ja-JP.yml (Portuguese) 2018-10-14 19:51:50 +09:00
d47c0eb31a New translations ja-JP.yml (Polish) 2018-10-14 19:51:45 +09:00
177e8bb19f New translations ja-JP.yml (Korean) 2018-10-14 19:51:41 +09:00
d156111637 New translations ja-JP.yml (Italian) 2018-10-14 19:51:37 +09:00
8c13d3e50b New translations ja-JP.yml (German) 2018-10-14 19:51:33 +09:00
6ff01016f0 New translations ja-JP.yml (French) 2018-10-14 19:51:30 +09:00
5d659da012 New translations ja-JP.yml (English) 2018-10-14 19:51:26 +09:00
28e7552a1a New translations ja-JP.yml (Chinese Simplified) 2018-10-14 19:51:21 +09:00
53d264814b New translations ja-JP.yml (Catalan) 2018-10-14 19:51:15 +09:00
2d6b20d34b 10.16.0 2018-10-14 19:45:51 +09:00
99073b56df Resolve #2900 2018-10-14 19:44:30 +09:00
5dce81c0db 非ASCIIなドメインへのメンションの修正 (#2903)
* punycodeでされたmentionのラベルをunicodeとして表示する

* post-form mentionはpunycodeにする

* mentionの表示はURLもAPI向けもunicodeにする
2018-10-14 16:56:19 +09:00
be82d845a4 expose user recommendation config in /api/meta (#2902) 2018-10-14 16:54:09 +09:00
f49ccd0cd3 10.15.0 2018-10-14 10:17:04 +09:00
69d83f535d Clean up 2018-10-14 10:16:07 +09:00
c7988fb6f5 🎨 2018-10-14 10:16:02 +09:00
3961fd08c9 Fix #2901 2018-10-14 10:06:10 +09:00
e3faf64061 10.14.0 2018-10-14 09:49:16 +09:00
ed83993e15 Fix 2018-10-14 09:48:47 +09:00
0f8847bb74 Resolve #2618 2018-10-14 09:47:38 +09:00
a72cfa7535 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-14 06:46:49 +09:00
514b74a19d Clean up 2018-10-14 06:44:20 +09:00
a2c124306f Update mios.ts 2018-10-14 05:26:36 +09:00
273f67e268 Fix bug 2018-10-13 23:12:48 +09:00
2870a7e463 10.13.0 2018-10-13 20:12:28 +09:00
935b074a7a New translations ja-JP.yml (Norwegian) 2018-10-13 20:12:19 +09:00
9d9c609bfb New translations ja-JP.yml (Dutch) 2018-10-13 20:12:15 +09:00
f6a664f181 New translations ja-JP.yml (Japanese, Kansai) 2018-10-13 20:12:10 +09:00
fce68d1f75 New translations ja-JP.yml (Spanish) 2018-10-13 20:12:06 +09:00
88739c2444 New translations ja-JP.yml (Russian) 2018-10-13 20:12:02 +09:00
7e2f10fce3 New translations ja-JP.yml (Portuguese) 2018-10-13 20:11:58 +09:00
a494c3a5cc New translations ja-JP.yml (Polish) 2018-10-13 20:11:53 +09:00
d6bb702883 New translations ja-JP.yml (Korean) 2018-10-13 20:11:48 +09:00
d15a972c68 New translations ja-JP.yml (Italian) 2018-10-13 20:11:44 +09:00
2ae7d31725 New translations ja-JP.yml (German) 2018-10-13 20:11:40 +09:00
2e329b1888 New translations ja-JP.yml (French) 2018-10-13 20:11:36 +09:00
522d40328b New translations ja-JP.yml (English) 2018-10-13 20:11:32 +09:00
2ecbff45bf New translations ja-JP.yml (Chinese Simplified) 2018-10-13 20:11:28 +09:00
b6f7282c13 New translations ja-JP.yml (Catalan) 2018-10-13 20:11:24 +09:00
65e5cfa68e Resolve #2853 2018-10-13 20:11:00 +09:00
10e59957d1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-13 19:26:22 +09:00
4f74373df3 Better id 2018-10-13 19:25:59 +09:00
2d414bbf86 Merge pull request #2897 from syuilo/greenkeeper/reconnecting-websocket-4.1.8
Update reconnecting-websocket to the latest version 🚀
2018-10-13 19:25:23 +09:00
a199969b81 fix(package): update reconnecting-websocket to version 4.1.8 2018-10-13 10:22:45 +00:00
3aef5e6748 Better id 2018-10-13 19:16:47 +09:00
2b536a7443 connectedイベントはpongパラメータがtrueの時だけ発行するように 2018-10-13 19:14:05 +09:00
20fe68de05 10.12.1 2018-10-13 18:11:24 +09:00
c7684b59de 🎨 2018-10-13 18:08:30 +09:00
a7237d157a Resolve #2600 2018-10-13 17:57:40 +09:00
35f91fa280 10.12.0 2018-10-13 13:25:48 +09:00
299ac32225 🎨 2018-10-13 13:25:07 +09:00
a038738d72 🎨 2018-10-13 13:22:14 +09:00
2b0a919fb5 Resolve #2894 2018-10-13 13:13:15 +09:00
946c706913 Better design 2018-10-13 12:51:01 +09:00
89b5d976ee Add some keyboard shortcuts of note 2018-10-13 12:48:33 +09:00
6f679bb6b4 Merge pull request #2896 from syuilo/greenkeeper/reconnecting-websocket-4.1.7
Update reconnecting-websocket to the latest version 🚀
2018-10-13 11:11:31 +09:00
db4e7b0e16 Merge pull request #2893 from syuilo/l10n_develop
New Crowdin translations
2018-10-13 11:11:16 +09:00
9ca942490d New translations ja-JP.yml (English) 2018-10-13 10:50:54 +09:00
ebcf249c8b New translations ja-JP.yml (Japanese, Kansai) 2018-10-13 10:41:05 +09:00
939c487503 New translations ja-JP.yml (English) 2018-10-13 10:41:01 +09:00
981a8b267e New translations ja-JP.yml (Japanese, Kansai) 2018-10-13 10:31:22 +09:00
9531da80a0 fix(package): update reconnecting-websocket to version 4.1.7 2018-10-12 23:51:14 +00:00
e1109b168c Clean up 2018-10-13 04:48:09 +09:00
b7c70039aa Merge pull request #2895 from syuilo/greenkeeper/reconnecting-websocket-4.1.6
Update reconnecting-websocket to the latest version 🚀
2018-10-13 02:13:32 +09:00
17b6f6cf2a fix(package): update reconnecting-websocket to version 4.1.6 2018-10-12 17:09:56 +00:00
dd88483ba4 10.11.1 2018-10-13 01:33:20 +09:00
0ff27f65b3 Fix bug 2018-10-13 01:33:00 +09:00
b1655740df Improve perforance 2018-10-13 01:17:23 +09:00
6d562aece1 New translations ja-JP.yml (Norwegian) 2018-10-13 01:05:49 +09:00
2182c3372b New translations ja-JP.yml (Dutch) 2018-10-13 01:05:44 +09:00
d3331bfe82 New translations ja-JP.yml (Japanese, Kansai) 2018-10-13 01:05:39 +09:00
cfc4a2e8b4 New translations ja-JP.yml (Spanish) 2018-10-13 01:05:35 +09:00
36c41c8eb3 New translations ja-JP.yml (Russian) 2018-10-13 01:05:31 +09:00
d255157e6e New translations ja-JP.yml (Portuguese) 2018-10-13 01:05:27 +09:00
c12e07277d New translations ja-JP.yml (Polish) 2018-10-13 01:05:21 +09:00
06b4fb5095 New translations ja-JP.yml (Korean) 2018-10-13 01:05:17 +09:00
8fafdcb428 New translations ja-JP.yml (Italian) 2018-10-13 01:05:10 +09:00
537a606bb6 New translations ja-JP.yml (German) 2018-10-13 01:05:06 +09:00
3dc7a4463c New translations ja-JP.yml (French) 2018-10-13 01:05:02 +09:00
fd6ff05b60 New translations ja-JP.yml (English) 2018-10-13 01:04:58 +09:00
1a159e41b8 New translations ja-JP.yml (Chinese Simplified) 2018-10-13 01:04:53 +09:00
23533cdd16 New translations ja-JP.yml (Catalan) 2018-10-13 01:04:47 +09:00
2f598b8fa1 10.11.0 2018-10-13 01:04:29 +09:00
bca349fec1 Improve performance 2018-10-13 01:00:43 +09:00
719fac6480 お気に入りを解除できるように 2018-10-13 00:54:30 +09:00
1012b2b2c7 10.10.1 2018-10-12 21:44:39 +09:00
5149be4b1b Fix bug 2018-10-12 21:44:04 +09:00
d12deeb0d8 Merge pull request #2889 from syuilo/greenkeeper/@types/elasticsearch-5.0.27
Update @types/elasticsearch to the latest version 🚀
2018-10-12 20:08:14 +09:00
9df81d1939 10.10.0 2018-10-12 14:38:21 +09:00
3be0079868 Merge pull request #2890 from syuilo/l10n_develop
New Crowdin translations
2018-10-12 14:36:54 +09:00
9b253ccb3a Refactor 2018-10-12 14:34:54 +09:00
dded76099c Refactor and usability improvements 2018-10-12 14:28:48 +09:00
41a7ec7d3d Fix bug 2018-10-12 13:53:40 +09:00
168c773ba0 Fix user recommendation query (last activity) (#2892) 2018-10-12 13:48:26 +09:00
9abed92196 New translations ja-JP.yml (French) 2018-10-12 05:26:16 +09:00
4a75e3602a New translations ja-JP.yml (French) 2018-10-12 05:13:53 +09:00
1a689f6641 削除された投稿はタイムライン上で表示しないようにする (#2887)
* Excepts deleted notes on query

* Hide deleted notes

* Use v-show
2018-10-12 05:10:40 +09:00
08d7ae11d6 fix(package): update @types/elasticsearch to version 5.0.27 2018-10-11 19:58:29 +00:00
9535759787 trim filename 2018-10-12 04:01:45 +09:00
f8fc31f14a スクロール時に新着情報を取得した際に、アイコンが被るのを修正 (#2888) 2018-10-12 03:28:37 +09:00
b74bf97761 10.9.2 2018-10-11 23:52:18 +09:00
a090b908bd Fix bug 2018-10-11 23:52:11 +09:00
3046821026 10.9.1 2018-10-11 23:09:12 +09:00
e94c73efe2 Fix 2018-10-11 23:07:20 +09:00
e85f9f4aa5 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視するように 2018-10-11 23:01:57 +09:00
ad67886f96 Resolve #543 2018-10-11 22:35:34 +09:00
5df0e102fd Fix 2018-10-11 22:17:27 +09:00
a04f0e3545 10.9.0 2018-10-11 21:27:33 +09:00
dff9c7ac48 Clean up and fix 2018-10-11 21:25:55 +09:00
3a80b59986 並列に処理するように 2018-10-11 21:14:20 +09:00
07560a4fdd 10.8.0 2018-10-11 18:38:31 +09:00
7edca21c05 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-11 18:35:50 +09:00
34105abd9d Fix 2018-10-11 18:35:19 +09:00
1bbca48a0b Update setup.ja.md 2018-10-11 18:20:27 +09:00
21f6a86772 Update setup.ja.md 2018-10-11 18:18:15 +09:00
6559197c55 Clean up 2018-10-11 18:10:41 +09:00
05f9ad11bb Redisがインストールされているときはイベントの共有にRedisのpub/subを使うように 2018-10-11 18:09:41 +09:00
f06d586680 10.7.2 2018-10-11 17:27:39 +09:00
4f45e8125c Fix 2018-10-11 17:25:53 +09:00
cc2843503d 10.7.1 2018-10-11 17:00:48 +09:00
324a974dec Fix bug 2018-10-11 17:00:22 +09:00
4d4ffd70ac 10.7.0 2018-10-11 15:58:39 +09:00
bf98a11b65 Reduce memory usage in file migration tool 2018-10-11 15:56:18 +09:00
1117ce4b54 Redisをオプションにしたり 2018-10-11 15:50:27 +09:00
57e93b9b4e fix(package): update @types/node to version 10.11.7 (#2885) 2018-10-11 05:41:01 +09:00
9e4b061ed0 Fix bug 2018-10-11 05:26:51 +09:00
1067bef7d6 Fix bug 2018-10-11 05:26:09 +09:00
8bff529acd Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-11 02:48:32 +09:00
4b08677839 ✌️ 2018-10-11 02:48:19 +09:00
70997cb551 fix(package): update vue-color to version 2.7.0 (#2884) 2018-10-11 02:22:57 +09:00
bf0ef17e23 Improve error logs 2018-10-11 02:19:21 +09:00
7dae5107f8 Improve fault tolerance 2018-10-11 02:11:12 +09:00
2dea88a147 Update stream.ts 2018-10-10 22:13:32 +09:00
f44c2a3e4f 10.6.0 2018-10-10 21:24:32 +09:00
1fad3cbaae 🎨 2018-10-10 21:23:38 +09:00
40d2e3e97c 🎨 2018-10-10 21:04:53 +09:00
2efabe612e fix(package): update @types/node to version 10.11.6 (#2876) 2018-10-10 21:01:25 +09:00
3107cbd6b9 fix(package): update @types/koa-logger to version 3.1.1 (#2877) 2018-10-10 21:01:18 +09:00
3a061ed1c3 New Crowdin translations (#2878)
* 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)
2018-10-10 21:01:08 +09:00
d4f0e6461a fix(package): update @types/webpack to version 4.4.16 (#2880) 2018-10-10 21:01:00 +09:00
3285687652 fix(package): update url-loader to version 1.1.2 (#2883) 2018-10-10 21:00:47 +09:00
51c53f64d0 Fix #2881, Fix #2879 2018-10-10 20:59:10 +09:00
1d582f5ad2 Fix syntax error 2018-10-10 20:58:42 +09:00
8a62748e39 🎨 2018-10-10 20:02:56 +09:00
b9290a021b New translations ja-JP.yml (French) 2018-10-10 07:51:07 +09:00
129ce93868 New translations ja-JP.yml (French) 2018-10-10 07:41:10 +09:00
5f41e5d6d0 New translations ja-JP.yml (French) 2018-10-10 07:31:22 +09:00
c706d030ea New translations ja-JP.yml (French) 2018-10-10 07:21:14 +09:00
34716a34f8 New translations ja-JP.yml (French) 2018-10-10 07:02:04 +09:00
6db3d6dfb6 fix(package): update @types/mongodb to version 3.1.12 (#2874) 2018-10-10 03:47:12 +09:00
38e2853dcf 10.5.0 2018-10-10 03:38:39 +09:00
ba5a540ca3 Log misskey version 2018-10-10 03:37:51 +09:00
fb1e05c2e9 ストリーミングAPIでチャンネルに接続したときにconnectedメッセージを返すように 2018-10-10 03:28:11 +09:00
aba84612a7 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-10 03:24:18 +09:00
9bebbf4e03 Add alias 2018-10-10 03:24:09 +09:00
e41b3f9c10 Merge pull request #2873 from mei23/mei-1010-pull-recquery
Fix users recommendation query
2018-10-10 02:18:31 +09:00
890dc05022 lint fix 2018-10-10 02:08:26 +09:00
375f86ec82 Fix users recommendation query 2018-10-10 02:04:16 +09:00
db248a69c8 10.4.0 2018-10-09 23:02:38 +09:00
5951288159 Merge pull request #2863 from syuilo/l10n_develop
New Crowdin translations
2018-10-09 23:01:51 +09:00
17b92c9db2 Fix bug 2018-10-09 22:48:45 +09:00
962d1060d9 New translations ja-JP.yml (Korean) 2018-10-09 22:32:14 +09:00
cb2640d961 New translations ja-JP.yml (Korean) 2018-10-09 22:11:31 +09:00
29aeb0f082 fix(package): update @types/webpack to version 4.4.15 (#2868) 2018-10-09 15:14:22 +09:00
990347f856 Fix 2018-10-09 15:13:06 +09:00
7a406c1f13 Docker (#2867)
* Dockerize Misskey

* Add a new line at EOF

* Add support Elasticsearch

* /

* Add setup document for docker

* Add english document

* Edit docs

* docker -> Docker

* Arrange format

* Update docker.en.md

* Modify title
2018-10-09 15:09:50 +09:00
9432af2ab5 10.3.0 2018-10-09 15:09:24 +09:00
136b13e7ca Fix bug and refactor 2018-10-09 15:08:31 +09:00
ba1c823fb1 🎨 2018-10-09 14:58:37 +09:00
f1301a4780 fix(package): update @types/redis to version 2.8.7 (#2866) 2018-10-09 09:38:47 +09:00
7957cd4963 fix(package): update @types/node to version 10.11.5 (#2865) 2018-10-09 09:38:40 +09:00
ee6590d03f fix(package): update @types/mongodb to version 3.1.11 (#2864) 2018-10-09 09:38:31 +09:00
f2a1238b20 fix(package): update commander to version 2.19.0 (#2862) 2018-10-09 09:38:21 +09:00
e9ce84f368 New translations ja-JP.yml (Norwegian) 2018-10-09 07:04:09 +09:00
52e84decb4 New translations ja-JP.yml (Dutch) 2018-10-09 07:04:03 +09:00
e893002bb6 New translations ja-JP.yml (Japanese, Kansai) 2018-10-09 07:03:59 +09:00
3792103e80 New translations ja-JP.yml (Spanish) 2018-10-09 07:03:54 +09:00
7a861c9481 New translations ja-JP.yml (Russian) 2018-10-09 07:03:47 +09:00
942b565224 New translations ja-JP.yml (Portuguese) 2018-10-09 07:03:43 +09:00
88390d7a9a New translations ja-JP.yml (Polish) 2018-10-09 07:03:39 +09:00
966fc4c5d7 New translations ja-JP.yml (Korean) 2018-10-09 07:03:34 +09:00
84dbdf1196 New translations ja-JP.yml (Italian) 2018-10-09 07:03:29 +09:00
211e7f90d9 New translations ja-JP.yml (German) 2018-10-09 07:03:23 +09:00
e54b8e3fb2 New translations ja-JP.yml (French) 2018-10-09 07:03:18 +09:00
836c89ed33 New translations ja-JP.yml (English) 2018-10-09 07:03:12 +09:00
c7c73afea1 New translations ja-JP.yml (Chinese Simplified) 2018-10-09 07:03:06 +09:00
7b9ca63b1e New translations ja-JP.yml (Catalan) 2018-10-09 07:02:59 +09:00
c464183329 Improve theme manager 2018-10-09 06:46:52 +09:00
389f420cad Update src/tools/move-drive-files.ts 2018-10-09 05:46:21 +09:00
6b2888383c 10.2.1 2018-10-09 05:36:39 +09:00
3c38a867b4 Fix bug 2018-10-09 05:35:40 +09:00
7f5a69f4d8 Fix bug 2018-10-09 05:31:26 +09:00
bb9ab31d5e Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-09 05:15:48 +09:00
9def80af8a 10.2.0 2018-10-09 05:15:31 +09:00
9256bcdbe4 fix(package): update debug to version 4.1.0 (#2857) 2018-10-09 05:12:19 +09:00
9b775022bc Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-09 05:11:55 +09:00
32371ed2bd Fix #2858 2018-10-09 05:11:42 +09:00
8b98c08a81 Update README.md 2018-10-09 05:07:59 +09:00
7cf72f7447 Merge pull request #2860 from mei23/mei-1009-v10d
互換ストリーム / に main streamの内容も流す
2018-10-09 04:35:08 +09:00
913385b10d / に main stream も流す 2018-10-09 03:29:11 +09:00
7306468d08 Merge pull request #2856 from syuilo/greenkeeper/typescript-eslint-parser-20.0.0
Update typescript-eslint-parser to the latest version 🚀
2018-10-09 02:59:13 +09:00
11e5667778 fix(package): update typescript-eslint-parser to version 20.0.0 2018-10-08 17:15:23 +00:00
38cc02e261 Add tool 2018-10-09 02:14:03 +09:00
d52cf46cc1 10.1.0 2018-10-09 01:52:18 +09:00
c6110dd996 Fix bug
Closes #2855
2018-10-09 01:50:49 +09:00
51d8de2c38 Improve readability 2018-10-09 01:33:40 +09:00
4455a1aa9d Fix bug 2018-10-09 01:33:31 +09:00
040d395ddb 🎨 2018-10-09 01:26:04 +09:00
8296cac636 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-09 01:02:04 +09:00
3eafe8b87d Better api definition 2018-10-09 01:01:48 +09:00
c01512e261 Merge pull request #2854 from syuilo/greenkeeper/style-loader-0.23.1
Update style-loader to the latest version 🚀
2018-10-09 00:56:03 +09:00
e5cf3aecd5 Merge pull request #2830 from sei0o/fix-2346
fix #2346
2018-10-09 00:55:47 +09:00
a8f90b41b7 fix(package): update style-loader to version 0.23.1 2018-10-08 13:43:10 +00:00
b79169b975 Deleted dump.rdb 2018-10-08 22:26:26 +09:00
437d52e2ed Improve theme 2018-10-08 18:42:19 +09:00
1329721440 Merge pull request #2828 from hakaba-hitoyo/feature/external-user-recommendation
External user recommendation
2018-10-08 18:31:00 +09:00
6affb4fe97 Merge pull request #2851 from mei23/mei-1008-fix-apshow2
ap/showが返ってこないことがあるのを修正
2018-10-08 17:33:46 +09:00
15395686aa ap/showが返ってこないことがあるのを修正 2018-10-08 16:01:38 +09:00
5cf1956135 Added CSS variables for background of reactions-viewer 2018-10-06 22:14:30 +09:00
fff307d4bb fix #2346 2018-10-06 17:51:59 +09:00
2b7782ba03 replace var by const 2018-10-06 17:38:57 +09:00
96d961ee80 better readable code 2018-10-06 17:23:32 +09:00
9f064d76d9 better readable code 2018-10-06 17:21:27 +09:00
2e39106c4b better config handling 2018-10-06 17:19:41 +09:00
04650464f3 debug 2018-10-06 16:34:52 +09:00
3bc7e1e35c remove follow buttons in the friends-maker component/widget 2018-10-06 16:31:21 +09:00
7019ddbfc7 external user recommendation 2018-10-06 16:03:18 +09:00
193 changed files with 4151 additions and 1400 deletions

View File

@ -30,7 +30,7 @@ while :
touch patreon.cache && \ touch patreon.cache && \
rm patreon.cache && \ rm patreon.cache && \
cat patreon.raw.cache | \ cat patreon.raw.cache | \
jq -r '(.data|map(select(.relationships.currently_entitled_tiers.data[]))|map(.relationships.user.data.id))as$data|.included|map(select(.attributes.hide_pledges==false))|map(select(.id as$id|$data|contains([$id])))|map(.attributes|[.full_name,.thumb_url,.url]|@tsv)|.[]|@text' >> patreon.cache && \ jq -r '(.data|map(select(.relationships.currently_entitled_tiers.data[]))|map(.relationships.user.data.id))as$data|.included|map(select(.id as$id|$data|contains([$id])))|map(.attributes|[.full_name,.thumb_url,.url]|@tsv)|.[]|@text' >> patreon.cache && \
echo '<table><tr>' >> patreon.md.cache && \ echo '<table><tr>' >> patreon.md.cache && \
cat patreon.cache | \ cat patreon.cache | \
awk -F'\t' '{print $2,$1}' | \ awk -F'\t' '{print $2,$1}' | \

View File

@ -60,11 +60,6 @@ mongodb:
user: example-misskey-user user: example-misskey-user
pass: example-misskey-pass pass: example-misskey-pass
redis:
host: localhost
port: 6379
pass: example-pass
# Drive capacity of a local user (MB) # Drive capacity of a local user (MB)
localDriveCapacityMb: 256 localDriveCapacityMb: 256
@ -122,40 +117,50 @@ drive:
# Below settings are optional # Below settings are optional
# #
# Redis
#redis:
# host: localhost
# port: 6379
# pass: example-pass
# Elasticsearch # Elasticsearch
# elasticsearch: #elasticsearch:
# host: localhost # host: localhost
# port: 9200 # port: 9200
# pass: null # pass: null
# reCAPTCHA # reCAPTCHA
# recaptcha: #recaptcha:
# site_key: example-site-key # site_key: example-site-key
# secret_key: example-secret-key # secret_key: example-secret-key
# ServiceWorker # ServiceWorker
# sw: #sw:
# # Public key of VAPID # # Public key of VAPID
# public_key: example-sw-public-key # public_key: example-sw-public-key
#
# # Private key of VAPID # # Private key of VAPID
# private_key: example-sw-private-key # private_key: example-sw-private-key
# google_maps_api_key: example-google-maps-api-key
# Twitter integration # Twitter integration
# You need to set the oauth callback url as : https://<your-misskey-instance>/api/tw/cb # You need to set the oauth callback url as : https://<your-misskey-instance>/api/tw/cb
# twitter: #twitter:
# consumer_key: example-twitter-consumer-key # consumer_key: example-twitter-consumer-key
# consumer_secret: example-twitter-consumer-secret-key # consumer_secret: example-twitter-consumer-secret-key
# Ghost # Ghost
# Ghost account is an account used for the purpose of delegating # Ghost account is an account used for the purpose of delegating
# followers when putting users in the list. # followers when putting users in the list.
# ghost: user-id-of-your-ghost-account #ghost: user-id-of-your-ghost-account
# Clustering # Clustering
# clusterLimit: 1 #clusterLimit: 1
# Summaly proxy # Summaly proxy
# summalyProxy: "http://example.com" #summalyProxy: "http://example.com"
# User recommendation
#user_recommendation:
# external: true
# engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}
# timeout: 300000

View File

@ -0,0 +1,13 @@
var user = {
user: 'example-misskey-user',
pwd: 'example-misskey-pass',
roles: [
{
role: 'readWrite',
db: 'misskey'
}
]
};
db.createUser(user);

12
.dockerignore Executable file
View File

@ -0,0 +1,12 @@
.autogen
.git
.github
.travis
.vscode
Dockerfile
build/
docker-compose.yml
node_modules/
mongo/
redis/
elasticsearch/

4
.gitignore vendored
View File

@ -1,5 +1,6 @@
/.config/* /.config/*
!/.config/example.yml !/.config/example.yml
!/.config/mongo_initdb_example.js
/.vscode /.vscode
/node_modules /node_modules
/build /build
@ -12,3 +13,6 @@ npm-debug.log
run.bat run.bat
api-docs.json api-docs.json
*.log *.log
/redis
/mongo
/elasticsearch

View File

@ -1,12 +1,8 @@
maintainer: '@syuilo' maintainer:
url: 'https://misskey.xyz' name: syuilo
secondary_url: 'https://himasaku.net' url: 'https://syuilo.com'
url: 'http://misskey.local'
port: 80 port: 80
https:
enable: false
key: null
cert: null
ca: null
mongodb: mongodb:
host: localhost host: localhost
port: 27017 port: 27017
@ -21,6 +17,3 @@ elasticsearch:
host: localhost host: localhost
port: 9200 port: 9200
pass: '' pass: ''
recaptcha:
site_key: hima
secret_key: saku

View File

@ -1,12 +1,8 @@
maintainer: '@syuilo' maintainer:
url: 'https://misskey.xyz' name: syuilo
secondary_url: 'https://himasaku.net' url: 'https://syuilo.com'
url: 'http://misskey.local'
port: 80 port: 80
https:
enable: false
key: null
cert: null
ca: null
mongodb: mongodb:
host: localhost host: localhost
port: 27017 port: 27017
@ -21,6 +17,3 @@ elasticsearch:
host: localhost host: localhost
port: 9200 port: 9200
pass: '' pass: ''
recaptcha:
site_key: hima
secret_key: saku

28
Dockerfile Normal file
View File

@ -0,0 +1,28 @@
FROM alpine:latest AS base
ENV NODE_ENV=production
RUN apk add --no-cache nodejs nodejs-npm
RUN apk add vips fftw --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/
WORKDIR /misskey
COPY . ./
FROM base AS builder
RUN apk add --no-cache gcc g++ python autoconf automake file make nasm
RUN apk add vips-dev fftw-dev --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/
RUN npm install \
&& npm install -g node-gyp \
&& node-gyp configure \
&& node-gyp build \
&& npm run build
FROM base AS runner
COPY --from=builder /misskey/built ./built
COPY --from=builder /misskey/node_modules ./node_modules
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["npm", "start"]

View File

@ -71,6 +71,7 @@ Please see [Contribution guide](./CONTRIBUTING.md).
---------------------------------------------------------------- ----------------------------------------------------------------
<!-- PATREON_START --> <!-- PATREON_START -->
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=CXe9AqlZy9AsYfiWd3OBYVOzvODoN47Litz0Tu4BFpU%3D" alt="Gargron"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1?token-time=2145916800&token-hash=d6P5MWHHsCMxUuBAEPAoVc5wLUR19mIhqAq7Ma9h9rI%3D" alt="ne_moni"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1?token-time=2145916800&token-hash=d6P5MWHHsCMxUuBAEPAoVc5wLUR19mIhqAq7Ma9h9rI%3D" alt="ne_moni"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/2?token-time=2145916800&token-hash=mgPdX9TqZxEg4TTPuc477dxhIgYk9246qafjWZEqZ7g%3D" alt="Melilot"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/2?token-time=2145916800&token-hash=mgPdX9TqZxEg4TTPuc477dxhIgYk9246qafjWZEqZ7g%3D" alt="Melilot"></td>
@ -80,6 +81,7 @@ Please see [Contribution guide](./CONTRIBUTING.md).
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td> <td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
<td><a href="https://www.patreon.com/negao">negao</a></td> <td><a href="https://www.patreon.com/negao">negao</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td> <td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>

52
docker-compose.yml Normal file
View File

@ -0,0 +1,52 @@
version: "3"
services:
web:
build: .
restart: always
links:
- mongo
- redis
# - es
ports:
- "127.0.0.1:3000:3000"
networks:
- internal_network
- external_network
redis:
restart: always
image: redis:4.0-alpine
networks:
- internal_network
### Uncomment to enable Redis persistance
# volumes:
# - ./redis:/data
mongo:
restart: always
image: mongo:4.1-bionic
networks:
- internal_network
environment:
MONGO_INITDB_DATABASE: "misskey"
volumes:
- ./.config/mongo_initdb.js:/docker-entrypoint-initdb.d/mongo_initdb.js:ro
### Uncomment to enable MongoDB persistance
# - ./mongo:/data
# es:
# restart: always
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
# environment:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# networks:
# - internal_network
#### Uncomment to enable ES persistence
## volumes:
## - ./elasticsearch:/usr/share/elasticsearch/data
networks:
internal_network:
internal: true
external_network:

47
docs/docker.en.md Normal file
View File

@ -0,0 +1,47 @@
Docker Guide
================================================================
This guide describes how to install and setup Misskey with Docker.
[Japanese version also available - 日本語版もあります](./docker.ja.md)
----------------------------------------------------------------
*1.* Make configuration files
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`.
2. Edit `default.yml` and `mongo_initdb.js`.
*2.* Configure Docker
----------------------------------------------------------------
Edit `docker-compose.yml`.
*3.* Build Misskey
----------------------------------------------------------------
Build misskey with the following:
`docker-compose build`
*4.* That is it.
----------------------------------------------------------------
Well done! Now, you have an environment that run to Misskey.
### Launch normally
Just `docker-compose up -d`. GLHF!
### Way to Update to latest version of your Misskey
1. `git fetch`
2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
4. `git stash pop`
5. `docker-compose build`
6. Check [ChangeLog](../CHANGELOG.md) for migration information
7. `docker-compose stop && docker-compose up -d`
### Way to execute cli command:
`docker-compose run --rm web node cli/mark-admin @example`
----------------------------------------------------------------
If you have any questions or troubles, feel free to contact us!

48
docs/docker.ja.md Normal file
View File

@ -0,0 +1,48 @@
Dockerを使ったMisskey構築方法
================================================================
このガイドはDockerを使ったMisskeyセットアップ方法について解説します。
[英語版もあります - English version also available](./docker.en.md)
----------------------------------------------------------------
*1.* 設定ファイルを作成する
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` `.config/mongo_initdb_example.js`をコピーし名前を`mongo_initdb.js`にする
3. `default.yml``mongo_initdb.js`を編集する
*2.* Dockerの設定
----------------------------------------------------------------
`docker-compose.yml`を編集してください。
*3.* Misskeyのビルド
----------------------------------------------------------------
次のコマンドでMisskeyをビルドしてください:
`docker-compose build`
*4.* 以上です!
----------------------------------------------------------------
お疲れ様でした。これでMisskeyを動かす準備は整いました。
### 通常起動
`docker-compose up -d`するだけです。GLHF!
### Misskeyを最新バージョンにアップデートする方法:
1. `git fetch`
2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
4. `git stash pop`
5. `docker-compose build`
6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
7. `docker-compose stop && docker-compose up -d`
### cliコマンドを実行する方法:
`docker-compose run --rm web node cli/mark-admin @example`
----------------------------------------------------------------
なにかお困りのことがありましたらお気軽にご連絡ください。

View File

@ -24,12 +24,12 @@ Please install and setup these softwares:
#### Dependencies :package: #### Dependencies :package:
* **[Node.js](https://nodejs.org/en/)** * **[Node.js](https://nodejs.org/en/)**
* **[MongoDB](https://www.mongodb.com/)** >= 3.6 * **[MongoDB](https://www.mongodb.com/)** >= 3.6
* **[Redis](https://redis.io/)**
##### Optional ##### Optional
* [Redis](https://redis.io/)
* Redis is optional, but we strongly recommended to install it
* [Elasticsearch](https://www.elastic.co/) - used to provide searching feature instead of MongoDB * [Elasticsearch](https://www.elastic.co/) - used to provide searching feature instead of MongoDB
*3.* Setup MongoDB *3.* Setup MongoDB
---------------------------------------------------------------- ----------------------------------------------------------------
In root : In root :

View File

@ -24,10 +24,17 @@ adduser --disabled-password --disabled-login misskey
#### 依存関係 :package: #### 依存関係 :package:
* **[Node.js](https://nodejs.org/en/)** * **[Node.js](https://nodejs.org/en/)**
* **[MongoDB](https://www.mongodb.com/)** (3.6以上) * **[MongoDB](https://www.mongodb.com/)** (3.6以上)
* **[Redis](https://redis.io/)**
##### オプション ##### オプション
* [Elasticsearch](https://www.elastic.co/) - 検索機能を向上させるために用います。 * [Redis](https://redis.io/)
* Redisはオプションですが、インストールすることを強く推奨します。
* インストールしなくていいのは、あなたのインスタンスが自分専用のときだけとお考えください。
* 具体的には、Redisをインストールしないと、次の事が出来なくなります:
* Misskeyプロセスを複数起動しての負荷分散
* レートリミット
* Twitter連携
* [Elasticsearch](https://www.elastic.co/)
* 検索機能を有効にするためにはインストールが必要です。
*3.* MongoDBの設定 *3.* MongoDBの設定
---------------------------------------------------------------- ----------------------------------------------------------------

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -3,16 +3,16 @@ meta:
lang: "Deutsch" lang: "Deutsch"
divider: "" divider: ""
common: common:
misskey: "A ⭐ of fediverse" misskey: "Ein ⭐ des Fediversums"
about-title: "A ⭐ of fediverse." about-title: "Ein ⭐ des Fediversums."
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。" about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。"
intro: intro:
title: "Misskeyって?" title: "Was ist Misskey?"
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。" about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
features: "特徴" features: "Funktionen"
rich-contents: "投稿" rich-contents: "Notizen"
rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。" rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。"
reaction: "リアクション" reaction: "Reaktionen"
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。" reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
ui: "インターフェース" ui: "インターフェース"
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。" ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
@ -23,7 +23,7 @@ common:
detected: "広告ブロッカーを無効にしてください" detected: "広告ブロッカーを無効にしてください"
warning: "<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。" warning: "<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。"
application-authorization: "アプリの連携" application-authorization: "アプリの連携"
close: "閉じる" close: "Schließen"
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。" do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
got-it: "わかった" got-it: "わかった"
customization-tips: customization-tips:
@ -34,13 +34,13 @@ common:
paragraph4: "カスタマイズを終了するには、右上の「完了」をクリックします。" paragraph4: "カスタマイズを終了するには、右上の「完了」をクリックします。"
gotit: "Got it!" gotit: "Got it!"
notification: notification:
file-uploaded: "ファイルがアップロードされました" file-uploaded: "Datei hochgeladen!"
message-from: "{}さんからメッセージ:" message-from: "{}さんからメッセージ:"
reversi-invited: "対局への招待があります" reversi-invited: "対局への招待があります"
reversi-invited-by: "{}さんから" reversi-invited-by: "{}さんから"
notified-by: "{}さんから" notified-by: "Benachrichtigt von {}:"
reply-from: "{}さんから返信:" reply-from: "Antwort von {}:"
quoted-by: "{}さんが引用:" quoted-by: "Zitiert von {}:"
time: time:
unknown: "Unbekannt" unknown: "Unbekannt"
future: "Zukunft" future: "Zukunft"
@ -53,7 +53,7 @@ common:
months_ago: "vor {0} Monat{0:en}" months_ago: "vor {0} Monat{0:en}"
years_ago: "vor {} Jahr{0:en}" years_ago: "vor {} Jahr{0:en}"
month-and-day: "{month}月 {day}日" month-and-day: "{month}月 {day}日"
trash: "ゴミ箱" trash: "Papierkorb"
weekday-short: weekday-short:
sunday: "So" sunday: "So"
monday: "Mo" monday: "Mo"
@ -63,13 +63,13 @@ common:
friday: "Fr" friday: "Fr"
saturday: "Sa" saturday: "Sa"
weekday: weekday:
sunday: "日曜日" sunday: "Sonntag"
monday: "月曜日" monday: "Montag"
tuesday: "火曜日" tuesday: "Dienstag"
wednesday: "水曜日" wednesday: "Mittwoch"
thursday: "木曜日" thursday: "Donnerstag"
friday: "金曜日" friday: "Freitag"
saturday: "土曜日" saturday: "Samstag"
reactions: reactions:
like: "いいね" like: "いいね"
love: "Lieben" love: "Lieben"
@ -82,10 +82,10 @@ common:
rip: "RIP" rip: "RIP"
pudding: "Pudding" pudding: "Pudding"
note-visibility: note-visibility:
public: "公開" public: "Öffentlich"
home: "ホーム" home: "ホーム"
home-desc: "ホームタイムラインにのみ公開" home-desc: "ホームタイムラインにのみ公開"
followers: "フォロワー" followers: "Abonnenten"
followers-desc: "自分のフォロワーにのみ公開" followers-desc: "自分のフォロワーにのみ公開"
specified: "ダイレクト" specified: "ダイレクト"
specified-desc: "指定したユーザーにのみ公開" specified-desc: "指定したユーザーにのみ公開"
@ -122,9 +122,9 @@ common:
turn-of: "{}のターンです" turn-of: "{}のターンです"
past-turn-of: "{}のターン" past-turn-of: "{}のターン"
won: "{}の勝ち" won: "{}の勝ち"
black: "" black: "Schwarz"
white: "" white: "Weiß"
total: "合計" total: "Gesamt"
this-turn: "{}ターン目" this-turn: "{}ターン目"
widgets: widgets:
analog-clock: "Analoge Uhr" analog-clock: "Analoge Uhr"
@ -142,23 +142,23 @@ common:
broadcast: "ブロードキャスト" broadcast: "ブロードキャスト"
notifications: "Benachrichtigungen" notifications: "Benachrichtigungen"
users: "Empfohlene Benutzer" users: "Empfohlene Benutzer"
polls: "アンケート" polls: "Umfrage"
post-form: "Beitragsform" post-form: "Beitragsform"
messaging: "Nachrichten" messaging: "Nachrichten"
server: "Server-Info" server: "Server-Info"
donation: "Spenden" donation: "Spenden"
nav: "Navigation" nav: "Navigation"
tips: "Tipps" tips: "Tipps"
hashtags: "ハッシュタグ" hashtags: "Hashtags"
deck: deck:
widgets: "Widget hinzufügen:" widgets: "Widget hinzufügen:"
home: "Startseite" home: "Startseite"
local: "Lokal" local: "Lokal"
hybrid: "ソーシャル" hybrid: "ソーシャル"
hashtag: "ハッシュタグ" hashtag: "Hashtag"
global: "Global" global: "Global"
mentions: "あなた宛て" mentions: "Erwähnungen"
direct: "ダイレクト投稿" direct: "Direktnachrichten"
notifications: "Mitteilungen" notifications: "Mitteilungen"
list: "Listen" list: "Listen"
swap-left: "Nach links" swap-left: "Nach links"
@ -182,10 +182,10 @@ auth/views/form.vue:
drive-write: "ドライブを操作する。" drive-write: "ドライブを操作する。"
notification-read: "通知を見る。" notification-read: "通知を見る。"
notification-write: "通知を操作する。" notification-write: "通知を操作する。"
cancel: "キャンセル" cancel: "Abbrechen"
accept: "アクセスを許可" accept: "Zugriff erlauben."
auth/views/index.vue: auth/views/index.vue:
loading: "読み込み中" loading: "Lädt"
denied: "アプリケーションの連携をキャンセルしました。" denied: "アプリケーションの連携をキャンセルしました。"
denied-paragraph: "このアプリがあなたのアカウントにアクセスすることはありません。" denied-paragraph: "このアプリがあなたのアカウントにアクセスすることはありません。"
already-authorized: "このアプリは既に連携済みです" already-authorized: "このアプリは既に連携済みです"
@ -196,10 +196,10 @@ auth/views/index.vue:
sign-in: "サインインしてください" sign-in: "サインインしてください"
common/views/components/games/reversi/reversi.vue: common/views/components/games/reversi/reversi.vue:
matching: matching:
waiting-for: "{}を待っています" waiting-for: "Warten auf {}"
cancel: "キャンセル" cancel: "Abbrechen"
common/views/components/games/reversi/reversi.game.vue: common/views/components/games/reversi/reversi.game.vue:
surrender: "投了" surrender: "Aufgeben"
surrendered: "投了により" surrendered: "投了により"
is-llotheo: "石の少ない方が勝ち(ロセオ)" is-llotheo: "石の少ない方が勝ち(ロセオ)"
looped-map: "ループマップ" looped-map: "ループマップ"
@ -208,9 +208,9 @@ common/views/components/games/reversi/reversi.index.vue:
title: "Misskey Reversi" title: "Misskey Reversi"
sub-title: "他のMisskeyユーザーとリバーシで対戦しよう" sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
invite: "招待" invite: "招待"
rule: "遊び方" rule: "Spielanleitung"
rule-desc: "リバーシは、相手と交互に石をボードに置いて、相手の石を挟んで自分の色に変えてゆき、最終的に残った石が多い方が勝ちというボードゲームです。" rule-desc: "リバーシは、相手と交互に石をボードに置いて、相手の石を挟んで自分の色に変えてゆき、最終的に残った石が多い方が勝ちというボードゲームです。"
mode-invite: "招待" mode-invite: "Einladen"
mode-invite-desc: "指定したユーザーと対戦するモードです。" mode-invite-desc: "指定したユーザーと対戦するモードです。"
invitations: "対局の招待があります!" invitations: "対局の招待があります!"
my-games: "自分の対局" my-games: "自分の対局"
@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Diese Anmerkung favorisieren" favorite: "Diese Anmerkung favorisieren"
unfavorite: "お気に入り解除"
pin: "An die Profilseite pinnen" pin: "An die Profilseite pinnen"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Löschen" delete: "Löschen"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Werkzeuge" tools: "Werkzeuge"
task-manager: "Taskmanager" task-manager: "Taskmanager"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Profil aktualisieren" save: "Profil aktualisieren"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -265,37 +265,40 @@ common/views/components/media-banner.vue:
sensitive: "NSFW" sensitive: "NSFW"
click-to-show: "Click to show" click-to-show: "Click to show"
common/views/components/theme.vue: common/views/components/theme.vue:
light-theme: "非ダークモード時に使用するテーマ" light-theme: "Theme during non-dark mode"
dark-theme: "ダークモード時に使用するテーマ" dark-theme: "Theme during dark mode"
light-themes: "明るいテーマ" light-themes: "Light theme"
dark-themes: "暗いテーマ" dark-themes: "Dark theme"
install-a-theme: "テーマのインストール" install-a-theme: "Install a theme"
theme-code: "テーマコード" theme-code: "Theme code"
install: "インストール" install: "Install"
installed: "「{}」をインストールしました" installed: "\"{}\" has been installed"
create-a-theme: "テーマの作成" create-a-theme: "Create a theme"
save-created-theme: "テーマを保存" save-created-theme: "Save a theme"
primary-color: "プライマリ カラー" primary-color: "Primary color"
secondary-color: "セカンダリ カラー" secondary-color: "Secondary color"
text-color: "文字色" text-color: "Text color"
base-theme: "ベーステーマ" base-theme: "Base theme"
base-theme-light: "Light" base-theme-light: "Light"
base-theme-dark: "Dark" base-theme-dark: "Dark"
theme-name: "テーマ名" theme-name: "Theme name"
preview-created-theme: "プレビュー" preview-created-theme: "Preview"
invalid-theme: "テーマが正しくありません。" invalid-theme: "Not valid theme"
already-installed: "既にそのテーマはインストールされています。" already-installed: "This theme is already installed."
saved: "保存しました" saved: "Saved"
installed-themes: "インストールされたテーマ" manage-themes: "Themes manager"
select-theme: "テーマを選択してください" builtin-themes: "Standard themes"
uninstall: "アンインストール" my-themes: "My themes"
uninstalled: "「{}」をアンインストールしました" installed-themes: "Installed themes"
author: "作者" select-theme: "Select your theme"
desc: "説明" uninstall: "Uninstall"
export: "エクスポート" uninstalled: "\"{}\" has been uninstalled"
import: "インポート" author: "Author"
import-by-code: "またはコードをペースト" desc: "Description"
theme-name-required: "テーマ名は必須です。" export: "Export"
import: "Import"
import-by-code: "or paste code"
theme-name-required: "Theme name is required"
common/views/components/cw-button.vue: common/views/components/cw-button.vue:
hide: "Hide" hide: "Hide"
show: "See more" show: "See more"
@ -332,8 +335,9 @@ common/views/components/note-menu.vue:
detail: "Details" detail: "Details"
copy-link: "Copy link" copy-link: "Copy link"
favorite: "Favorite this note" favorite: "Favorite this note"
unfavorite: "Unfavorite"
pin: "Pin to your profile" pin: "Pin to your profile"
unpin: "ピン留め解除" unpin: "Unpin"
delete: "Delete" delete: "Delete"
delete-confirm: "Delete this post?" delete-confirm: "Delete this post?"
remote: "Show original note" remote: "Show original note"
@ -511,13 +515,13 @@ desktop/views/components/charts.vue:
notes: "The number of posts: increase/decrease (Combined)" notes: "The number of posts: increase/decrease (Combined)"
local-notes: "The number of posts: increase/decrease (Local)" local-notes: "The number of posts: increase/decrease (Local)"
remote-notes: "The number of posts: increase/decrease (Remote)" remote-notes: "The number of posts: increase/decrease (Remote)"
notes-total: "投稿の積算" notes-total: "Total posts"
users: "The number of users: increase/decrease" users: "The number of users: increase/decrease"
users-total: "ユーザーの積算" users-total: "Total users"
drive: "Capacity used as the storage: increase/decrease" drive: "Capacity used as the storage: increase/decrease"
drive-total: "ドライブ使用量の積算" drive-total: "Total usage of Drive"
drive-files: "The number of files on the storage: increase/decrease" drive-files: "The number of files on the storage: increase/decrease"
drive-files-total: "ドライブのファイル数の積算" drive-files-total: "Total number of files on Drive"
network-requests: "Requests" network-requests: "Requests"
network-time: "Response time" network-time: "Response time"
network-usage: "Traffic" network-usage: "Traffic"
@ -727,8 +731,8 @@ desktop/views/components/settings.vue:
choose-wallpaper: "Choose a background" choose-wallpaper: "Choose a background"
delete-wallpaper: "Remove background" delete-wallpaper: "Remove background"
dark-mode: "Dark Mode" dark-mode: "Dark Mode"
use-shadow: "UIに影を使用" use-shadow: "Use shadows in the UI"
rounded-corners: "UIの角を丸める" rounded-corners: "Round corners of UI"
circle-icons: "Use circle icons" circle-icons: "Use circle icons"
contrasted-acct: "Add contrast to username" contrasted-acct: "Add contrast to username"
post-form-on-timeline: "Display post form at the top of the timeline" post-form-on-timeline: "Display post form at the top of the timeline"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Tools" tools: "Tools"
task-manager: "Task Manager" task-manager: "Task Manager"
third-parties: "Third-parties" third-parties: "Third-parties"
navbar-position: "Navigation bar position"
navbar-position-top: "Top"
navbar-position-left: "Left"
navbar-position-right: "Right"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "If you set up 2-step verification, you will not only need a password at sign-in, but also a pre-registered physical device (such as your smartphone), which will improve security." intro: "If you set up 2-step verification, you will not only need a password at sign-in, but also a pre-registered physical device (such as your smartphone), which will improve security."
detail: "Details…" detail: "Details…"
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Update profile" save: "Update profile"
locked-account: "Protect your account" locked-account: "Protect your account"
is-locked: "Follow request needs approval" is-locked: "Follow request needs approval"
careful-bot: "Botからのフォローだけ承認制にする"
other: "Other" other: "Other"
is-bot: "This account is a Bot" is-bot: "This account is a Bot"
is-cat: "This account is a Cat" is-cat: "This account is a Cat"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "NSFW" nsfw: "NSFW"
mark-as-sensitive: "Mark as 'sensitive'"
unmark-as-sensitive: "Unmark as 'sensitive'"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "NSFW" sensitive: "NSFW"
click-to-show: "Click to show" click-to-show: "Click to show"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "Banner" banner: "Banner"
is-cat: "This account is a Cat" is-cat: "This account is a Cat"
is-locked: "Follow request needs approval" is-locked: "Follow request needs approval"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "Advanced" advanced: "Advanced"
privacy: "Privacy" privacy: "Privacy"
save: "Update profile" save: "Update profile"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "Detalles" detail: "Detalles"
copy-link: "Copiar enlace" copy-link: "Copiar enlace"
favorite: "Me gusta esta nota" favorite: "Me gusta esta nota"
unfavorite: "お気に入り解除"
pin: "Fijar en el perfil" pin: "Fijar en el perfil"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Borrar" delete: "Borrar"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Herramientas" tools: "Herramientas"
task-manager: "Navegador de tareas" task-manager: "Navegador de tareas"
third-parties: "Servicios externos" third-parties: "Servicios externos"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "Ver detalles..." detail: "Ver detalles..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Perfil actualizado" save: "Perfil actualizado"
locked-account: "Protege tu cuenta" locked-account: "Protege tu cuenta"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -34,7 +34,7 @@ common:
paragraph4: "Pour terminer la personnalisation, cliquez sur \"Terminer\" dans le coin supérieur droit." paragraph4: "Pour terminer la personnalisation, cliquez sur \"Terminer\" dans le coin supérieur droit."
gotit: "Compris !" gotit: "Compris !"
notification: notification:
file-uploaded: "Le fichier a été téléversé !" file-uploaded: "Le fichier a été transféré !"
message-from: "Message de {} :" message-from: "Message de {} :"
reversi-invited: "Invité à jouer" reversi-invited: "Invité à jouer"
reversi-invited-by: "Invité par {} :" reversi-invited-by: "Invité par {} :"
@ -83,17 +83,17 @@ common:
pudding: "Pudding" pudding: "Pudding"
note-visibility: note-visibility:
public: "Public" public: "Public"
home: "Accueil" home: "Principal"
home-desc: "Publier sur le fil local uniquement" home-desc: "Publier sur le fil principal uniquement"
followers: "Abonnés·es" followers: "Abonnés·es"
followers-desc: "Publier à vos abonnés·es uniquement" followers-desc: "Publier à vos abonnés·es uniquement"
specified: "Direct" specified: "Direct"
specified-desc: "Publier aux utilisateurs·trices mentionnés·es" specified-desc: "Publier uniquement aux utilisateurs·rices mentionnés·es"
private: "Privé" private: "Privé"
note-placeholders: note-placeholders:
a: "Que faites-vous maintenant ?" a: "Que faites-vous maintenant ?"
b: "Quoi de neuf ?" b: "Quoi de neuf ?"
c: "Qu'avez-vous en tête ?" c: "Quavez-vous en tête ?"
d: "Désirez-vous publier quelques mots ?" d: "Désirez-vous publier quelques mots ?"
e: "Écrivez ici" e: "Écrivez ici"
f: "En attente de vos écrits" f: "En attente de vos écrits"
@ -103,7 +103,7 @@ common:
ok: "OK" ok: "OK"
update-available-title: "Mise à jour disponible" update-available-title: "Mise à jour disponible"
update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour." update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour."
my-token-regenerated: "Votre token vient d'être généré, vous allez maintenant être déconnecté." my-token-regenerated: "Votre jeton vient dêtre généré, vous allez maintenant être déconnecté."
i-like-sushi: "Je préfère les sushis plutôt que le pudding" i-like-sushi: "Je préfère les sushis plutôt que le pudding"
show-reversi-board-labels: "Afficher les étiquettes des lignes et colonnes dans Reversi" show-reversi-board-labels: "Afficher les étiquettes des lignes et colonnes dans Reversi"
use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける" use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
@ -120,7 +120,7 @@ common:
my-turn: "Cest votre tour" my-turn: "Cest votre tour"
opponent-turn: "Tour de ladversaire" opponent-turn: "Tour de ladversaire"
turn-of: "Cest le tour de {}" turn-of: "Cest le tour de {}"
past-turn-of: "C'est au tour de {}" past-turn-of: "Cest au tour de {}"
won: "{} a gagné" won: "{} a gagné"
black: "Noirs" black: "Noirs"
white: "Blancs" white: "Blancs"
@ -136,7 +136,7 @@ common:
memo: "Pense-bête" memo: "Pense-bête"
trends: "Tendances" trends: "Tendances"
photo-stream: "Flux de photos" photo-stream: "Flux de photos"
posts-monitor: "Graphe des publications" posts-monitor: "Graph des publications"
slideshow: "Diaporama" slideshow: "Diaporama"
version: "Version" version: "Version"
broadcast: "Diffusion" broadcast: "Diffusion"
@ -267,8 +267,8 @@ common/views/components/media-banner.vue:
common/views/components/theme.vue: common/views/components/theme.vue:
light-theme: "非ダークモード時に使用するテーマ" light-theme: "非ダークモード時に使用するテーマ"
dark-theme: "ダークモード時に使用するテーマ" dark-theme: "ダークモード時に使用するテーマ"
light-themes: "明るいテーマ" light-themes: "Thème clair"
dark-themes: "暗いテーマ" dark-themes: "Thème sombre"
install-a-theme: "Installer un thème" install-a-theme: "Installer un thème"
theme-code: "Code du thème" theme-code: "Code du thème"
install: "Installation" install: "Installation"
@ -286,13 +286,16 @@ common/views/components/theme.vue:
invalid-theme: "Thème nest pas valide." invalid-theme: "Thème nest pas valide."
already-installed: "Le thème est déjà installé." already-installed: "Le thème est déjà installé."
saved: "enregistré" saved: "enregistré"
manage-themes: "Gestion des thèmes"
builtin-themes: "Thèmes standards"
my-themes: "Mes thèmes"
installed-themes: "Thèmes installés" installed-themes: "Thèmes installés"
select-theme: "Veuillez sélectionner un thème" select-theme: "Veuillez sélectionner un thème"
uninstall: "Désinstaller" uninstall: "Désinstaller"
uninstalled: "« {} » a été désinstallé" uninstalled: "« {} » a été désinstallé"
author: "Auteur" author: "Auteur"
desc: "Description" desc: "Description"
export: "エクスポート" export: "Exporter"
import: "Importer" import: "Importer"
import-by-code: "Ou coller du code" import-by-code: "Ou coller du code"
theme-name-required: "Nom du thème est obligatoire." theme-name-required: "Nom du thème est obligatoire."
@ -326,12 +329,13 @@ common/views/components/nav.vue:
wiki: "Wiki" wiki: "Wiki"
donors: "Donateur·rice·s" donors: "Donateur·rice·s"
repository: "Dépôt" repository: "Dépôt"
develop: "Développeur·se·s" develop: "Développeurs"
feedback: "Remarques" feedback: "Suggestions"
common/views/components/note-menu.vue: common/views/components/note-menu.vue:
detail: "Détails" detail: "Détails"
copy-link: "Copier le lien" copy-link: "Copier le lien"
favorite: "Mettre cette note en favoris" favorite: "Mettre cette note en favoris"
unfavorite: "お気に入り解除"
pin: "Épingler sur votre profil" pin: "Épingler sur votre profil"
unpin: "Désépingler" unpin: "Désépingler"
delete: "Supprimer" delete: "Supprimer"
@ -407,10 +411,10 @@ common/views/components/visibility-chooser.vue:
followers: "Abonné·e·s" followers: "Abonné·e·s"
followers-desc: "Publier à vos abonné·e·s uniquement" followers-desc: "Publier à vos abonné·e·s uniquement"
specified: "Direct" specified: "Direct"
specified-desc: "Publier aux utilisateur·rice·s mentionné·e·s" specified-desc: "Publier uniquement aux utilisateurs·rices mentionné·e·s"
private: "Privé" private: "Privé"
common/views/components/trends.vue: common/views/components/trends.vue:
count: "{} utilisateurs·trices mentionnés·es" count: "{} utilisateurs·rices mentionnés·es"
empty: "Aucune tendance" empty: "Aucune tendance"
common/views/widgets/broadcast.vue: common/views/widgets/broadcast.vue:
fetching: "Récupération" fetching: "Récupération"
@ -431,7 +435,7 @@ common/views/widgets/photo-stream.vue:
title: "Flux de photos" title: "Flux de photos"
no-photos: "Pas de photo" no-photos: "Pas de photo"
common/views/widgets/posts-monitor.vue: common/views/widgets/posts-monitor.vue:
title: "Graphe des publications" title: "Graph des publications"
toggle: "Basculer entre les vues" toggle: "Basculer entre les vues"
common/views/widgets/hashtags.vue: common/views/widgets/hashtags.vue:
title: "Hashtags" title: "Hashtags"
@ -511,7 +515,7 @@ desktop/views/components/charts.vue:
notes: "投稿の増減 (統合)" notes: "投稿の増減 (統合)"
local-notes: "投稿の増減 (ローカル)" local-notes: "投稿の増減 (ローカル)"
remote-notes: "投稿の増減 (リモート)" remote-notes: "投稿の増減 (リモート)"
notes-total: "投稿の積算" notes-total: "Total des notes"
users: "Nombre dutilisateurs·trices : augmentation/diminution" users: "Nombre dutilisateurs·trices : augmentation/diminution"
users-total: "ユーザーの積算" users-total: "ユーザーの積算"
drive: "ドライブ使用量の増減" drive: "ドライブ使用量の増減"
@ -579,7 +583,7 @@ desktop/views/components/drive.vue:
unable-to-process: "L'opération n'a pas pu être complétée" unable-to-process: "L'opération n'a pas pu être complétée"
circular-reference-detected: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer." circular-reference-detected: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer."
unhandled-error: "Erreur inconnue" unhandled-error: "Erreur inconnue"
url-upload: "Uploader d'un URL" url-upload: "Téléverser via une URL"
url-of-file: "URL de l'image que vous souhaitez uploader." url-of-file: "URL de l'image que vous souhaitez uploader."
url-upload-requested: "Upload requested" url-upload-requested: "Upload requested"
may-take-time: "L'upload de votre fichier peut prendre un certain temps." may-take-time: "L'upload de votre fichier peut prendre un certain temps."
@ -587,8 +591,8 @@ desktop/views/components/drive.vue:
folder-name: "Nom du dossier" folder-name: "Nom du dossier"
contextmenu: contextmenu:
create-folder: "Créer un dossier" create-folder: "Créer un dossier"
upload: "Uploader un fichier" upload: "Transférer un fichier"
url-upload: "Uploader d'un URL" url-upload: "Transférer à partir dune URL"
desktop/views/components/media-image.vue: desktop/views/components/media-image.vue:
sensitive: "Le contenu est NSFW" sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher" click-to-show: "Cliquer pour afficher"
@ -655,21 +659,21 @@ desktop/views/components/post-form.vue:
add-visible-user: "+Ajouter un utilisateur" add-visible-user: "+Ajouter un utilisateur"
attach-location-information: "Attacher des informations de localisation" attach-location-information: "Attacher des informations de localisation"
hide-contents: "Masquer les contenus" hide-contents: "Masquer les contenus"
reply-placeholder: "Répondre à cette note" reply-placeholder: "Répondre à cette note"
quote-placeholder: "Citer cette note" quote-placeholder: "Citer cette note"
submit: "Poster" submit: "Publier"
reply: "Répondre" reply: "Répondre"
renote: "Republier" renote: "Republier"
posted: "Posté!" posted: "Publié !"
replied: "Répondu!" replied: "Répondu !"
reposted: "Reposté!" reposted: "Reposté !"
note-failed: "La note à échoué" note-failed: "La note à échoué"
reply-failed: "La réponse à échoué" reply-failed: "La réponse à échoué"
renote-failed: "La renote à échoué" renote-failed: "Échec lors de la republication"
posting: "Publication..." posting: "Publication"
attach-media-from-local: "Joindre un media depuis votre PC" attach-media-from-local: "Joindre un média depuis votre appareil"
attach-media-from-drive: "Joindre un media depuis votre Drive" attach-media-from-drive: "Joindre un média depuis votre Drive"
attach-cancel: "Annuler la jointure de fichier" attach-cancel: "Annuler le fichier attaché"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "Créer un sondage" create-poll: "Créer un sondage"
text-remain: "{} charactères restants" text-remain: "{} charactères restants"
@ -684,15 +688,15 @@ desktop/views/components/post-form-window.vue:
note: "Nouvelle note" note: "Nouvelle note"
reply: "Répondre" reply: "Répondre"
attaches: "{} media joint(s)" attaches: "{} media joint(s)"
uploading-media: "Upload du media {}" uploading-media: "Transfert du média {}"
desktop/views/components/progress-dialog.vue: desktop/views/components/progress-dialog.vue:
waiting: "En attente" waiting: "En attente"
desktop/views/components/renote-form.vue: desktop/views/components/renote-form.vue:
quote: "Citer..." quote: "Citer..."
cancel: "Annuler" cancel: "Annuler"
renote: "Republier" renote: "Republier"
reposting: "Repost en cours..." reposting: "Republication en cours"
success: "Reposté!" success: "Republié !"
failure: "La renote a échoué" failure: "La renote a échoué"
desktop/views/components/renote-form-window.vue: desktop/views/components/renote-form-window.vue:
title: "Êtes vous sûr de vouloir renote cette note?" title: "Êtes vous sûr de vouloir renote cette note?"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Outils" tools: "Outils"
task-manager: "Gestionnaire de tâches" task-manager: "Gestionnaire de tâches"
third-parties: "Services tiers" third-parties: "Services tiers"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "Si vous configurez la vérication en deux étapes vous aurez non seulement besoin de votre mot de passe mais aussi un appareil déjà pré-enregistré(tel que votre smartphone) ce qui ameliora grandement la sécurité de votre compte." intro: "Si vous configurez la vérication en deux étapes vous aurez non seulement besoin de votre mot de passe mais aussi un appareil déjà pré-enregistré(tel que votre smartphone) ce qui ameliora grandement la sécurité de votre compte."
detail: "Voir les détails..." detail: "Voir les détails..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Mettre à jour le profil" save: "Mettre à jour le profil"
locked-account: "Protéger votre compte" locked-account: "Protéger votre compte"
is-locked: "Demande dabonnement en attente dapprobation" is-locked: "Demande dabonnement en attente dapprobation"
careful-bot: "Botからのフォローだけ承認制にする"
other: "Autre" other: "Autre"
is-bot: "Ce compte est un Bot" is-bot: "Ce compte est un Bot"
is-cat: "Ce compte est un Chat" is-cat: "Ce compte est un Chat"
@ -855,7 +864,7 @@ desktop/views/components/timeline.vue:
list-name: "Nom de la liste" list-name: "Nom de la liste"
desktop/views/components/ui.header.vue: desktop/views/components/ui.header.vue:
welcome-back: "Content de vous revoir !" welcome-back: "Content de vous revoir !"
adjective: "さん" adjective: "M."
desktop/views/components/ui.header.account.vue: desktop/views/components/ui.header.account.vue:
profile: "Votre profil" profile: "Votre profil"
drive: "Drive" drive: "Drive"
@ -875,7 +884,7 @@ desktop/views/components/ui.header.nav.vue:
desktop/views/components/ui.header.notifications.vue: desktop/views/components/ui.header.notifications.vue:
title: "Notifications" title: "Notifications"
desktop/views/components/ui.header.post.vue: desktop/views/components/ui.header.post.vue:
post: "Composer un nouveau post" post: "Rédiger une nouvelle publication"
desktop/views/components/ui.header.search.vue: desktop/views/components/ui.header.search.vue:
placeholder: "Chercher" placeholder: "Chercher"
desktop/views/components/received-follow-requests-window.vue: desktop/views/components/received-follow-requests-window.vue:
@ -908,9 +917,9 @@ desktop/views/pages/admin/admin.vue:
desktop/views/pages/admin/admin.dashboard.vue: desktop/views/pages/admin/admin.dashboard.vue:
dashboard: "Tableau de bord" dashboard: "Tableau de bord"
all-users: "Toutes les utilisateurrices" all-users: "Toutes les utilisateurrices"
original-users: "Utilisateurrices sur cette instance" original-users: "Utilisateur·rice·s sur cette instance"
all-notes: "Toutes les publications" all-notes: "Toutes les publications"
original-notes: "Publication sur cette instance" original-notes: "Publications sur cette instance"
invite: "Invitation" invite: "Invitation"
desktop/views/pages/admin/admin.suspend-user.vue: desktop/views/pages/admin/admin.suspend-user.vue:
suspend-user: "Suspendre un·e utilisateur·rice" suspend-user: "Suspendre un·e utilisateur·rice"
@ -938,9 +947,9 @@ desktop/views/pages/deck/deck.note.vue:
deleted: "cette publication a été supprimée" deleted: "cette publication a été supprimée"
desktop/views/pages/stats/stats.vue: desktop/views/pages/stats/stats.vue:
all-users: "Toutes les utilisateurrices" all-users: "Toutes les utilisateurrices"
original-users: "Utilisateurrices sur cette instance" original-users: "Utilisateur·rice·s sur cette instance"
all-notes: "Toutes les publications" all-notes: "Toutes les publications"
original-notes: "Publication sur cette instance" original-notes: "Publications sur cette instance"
desktop/views/pages/welcome.vue: desktop/views/pages/welcome.vue:
about: "à propos" about: "à propos"
gotit: "J'ai compris !" gotit: "J'ai compris !"
@ -966,7 +975,7 @@ desktop/views/pages/selectdrive.vue:
title: "Choisir fichier(s)" title: "Choisir fichier(s)"
ok: "OK" ok: "OK"
cancel: "Annuler" cancel: "Annuler"
upload: "Uploader un ou plusieurs fichier(s) depuis votre PC" upload: "Téléverser des fichiers à partir de votre ordinateur"
desktop/views/pages/search.vue: desktop/views/pages/search.vue:
not-available: "La fonction de recherche est désactivée dans les paramètres de linstance." not-available: "La fonction de recherche est désactivée dans les paramètres de linstance."
not-found: "Aucun message trouvé pour '{}'" not-found: "Aucun message trouvé pour '{}'"
@ -983,13 +992,13 @@ desktop/views/pages/user/user.followers-you-know.vue:
loading: "Chargement en cours" loading: "Chargement en cours"
no-users: "Pas d'utilisateurs" no-users: "Pas d'utilisateurs"
desktop/views/pages/user/user.friends.vue: desktop/views/pages/user/user.friends.vue:
title: "Personnes qui répondent le plus" title: "Mentions fréquentes"
loading: "Chargement en cours" loading: "Chargement en cours"
no-users: "Pas d'utilisateurs" no-users: "Pas d'utilisateurs"
desktop/views/pages/user/user.vue: desktop/views/pages/user/user.vue:
is-suspended: "Ce compte a été suspendu." is-suspended: "Ce compte a été suspendu."
is-remote: "Cet utilisateur n'est pas un utilisateur de Misskey. Certaines informations peuvent être erronées" is-remote: "Cet utilisateur n'est pas un utilisateur Misskey. Certaines informations peuvent ne pas refléter ce profil dans sa totalité."
view-remote: "Voir les informations détaillées" view-remote: "Consulter le profil complet"
desktop/views/pages/user/user.home.vue: desktop/views/pages/user/user.home.vue:
last-used-at: "Last used at" last-used-at: "Last used at"
desktop/views/pages/user/user.photos.vue: desktop/views/pages/user/user.photos.vue:
@ -997,7 +1006,7 @@ desktop/views/pages/user/user.photos.vue:
loading: "Chargement en cours" loading: "Chargement en cours"
no-photos: "Pas de photos" no-photos: "Pas de photos"
desktop/views/pages/user/user.profile.vue: desktop/views/pages/user/user.profile.vue:
follows-you: "Vous suis" follows-you: "Vous suit"
stalk: "Traquer" stalk: "Traquer"
stalking: "ストーキングしています" stalking: "ストーキングしています"
unstalk: "ストーク解除" unstalk: "ストーク解除"
@ -1026,8 +1035,8 @@ desktop/views/widgets/polls.vue:
refresh: "Afficher d'autres" refresh: "Afficher d'autres"
nothing: "Rien" nothing: "Rien"
desktop/views/widgets/post-form.vue: desktop/views/widgets/post-form.vue:
title: "Post" title: "Publication"
note: "Post" note: "Publication"
desktop/views/widgets/profile.vue: desktop/views/widgets/profile.vue:
update-banner: "Cliquer pour éditer votre bannière" update-banner: "Cliquer pour éditer votre bannière"
update-avatar: "Cliquer pour éditer votre avatar" update-avatar: "Cliquer pour éditer votre avatar"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "CW" nsfw: "CW"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "Le contenu est NSFW" sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher" click-to-show: "Cliquer pour afficher"
@ -1089,7 +1100,7 @@ mobile/views/components/friends-maker.vue:
refresh: "Voir plus" refresh: "Voir plus"
close: "Fermer" close: "Fermer"
mobile/views/components/note.vue: mobile/views/components/note.vue:
reposted-by: "Renoté par {}" reposted-by: "Republié par {}"
private: "cette publication est privée" private: "cette publication est privée"
deleted: "cette publication a été supprimée" deleted: "cette publication a été supprimée"
location: "Géolocalisation" location: "Géolocalisation"
@ -1116,7 +1127,7 @@ mobile/views/components/notifications.vue:
empty: "Pas de notifications" empty: "Pas de notifications"
mobile/views/components/post-form.vue: mobile/views/components/post-form.vue:
add-visible-user: "Ajouter un utilisateur" add-visible-user: "Ajouter un utilisateur"
submit: "Poster" submit: "Publier"
reply: "Répondre" reply: "Répondre"
renote: "Republier" renote: "Republier"
quote-placeholder: "Citer ce billet ... (Facultatif)" quote-placeholder: "Citer ce billet ... (Facultatif)"
@ -1168,7 +1179,7 @@ mobile/views/pages/drive.vue:
drive: "Drive" drive: "Drive"
more: "Afficher plus ..." more: "Afficher plus ..."
mobile/views/pages/signup.vue: mobile/views/pages/signup.vue:
lets-start: "Commençons ! 📦" lets-start: "Votre compte est prêt ! 📦"
mobile/views/pages/followers.vue: mobile/views/pages/followers.vue:
followers-of: "Abonné·e·s de {}" followers-of: "Abonné·e·s de {}"
mobile/views/pages/following.vue: mobile/views/pages/following.vue:
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "Bannière" banner: "Bannière"
is-cat: "Ce compte est un Bot" is-cat: "Ce compte est un Bot"
is-locked: "Demande dabonnement en attente dapprobation" is-locked: "Demande dabonnement en attente dapprobation"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "Avancé" advanced: "Avancé"
privacy: "Vie privée" privacy: "Vie privée"
save: "Mettre à jour le profil" save: "Mettre à jour le profil"
@ -1283,7 +1295,7 @@ mobile/views/pages/settings.vue:
sound: "Sons" sound: "Sons"
enable-sounds: "Activer les sons" enable-sounds: "Activer les sons"
mobile/views/pages/user.vue: mobile/views/pages/user.vue:
follows-you: "vous suit" follows-you: "Vous suit"
following: "Abonnements" following: "Abonnements"
followers: "Abonné·e·s" followers: "Abonné·e·s"
notes: "Notes" notes: "Notes"
@ -1291,8 +1303,8 @@ mobile/views/pages/user.vue:
timeline: "Fil d'actualité" timeline: "Fil d'actualité"
media: "Media" media: "Media"
is-suspended: "This account has been suspended." is-suspended: "This account has been suspended."
is-remote: "Cet utilisateur n'est pas un utilisateur de Misskey. Certaines informations peuvent être erronées " is-remote: "Ceci est le profil dun utilisateur·rice distant·e. Certaines informations peuvent ne pas refléter ce profil dans sa totalité."
view-remote: "Voir les informations détaillées" view-remote: "Consulter son profil complet"
mobile/views/pages/user/home.vue: mobile/views/pages/user/home.vue:
recent-notes: "Notes récentes" recent-notes: "Notes récentes"
images: "Images" images: "Images"
@ -1316,7 +1328,7 @@ mobile/views/pages/user/home.photos.vue:
no-photos: "Pas de photos" no-photos: "Pas de photos"
docs: docs:
edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation?" edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation?"
edit-this-page-on-github-link: "Modifiez cette page sur github!" edit-this-page-on-github-link: "Éditez cette page sur Github !"
api: api:
entities: entities:
properties: "Propriétés" properties: "Propriétés"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -307,6 +307,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -360,6 +363,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -879,6 +883,11 @@ desktop/views/components/settings.vue:
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -934,6 +943,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1229,6 +1239,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
@ -1416,6 +1428,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -265,37 +265,40 @@ common/views/components/media-banner.vue:
sensitive: "見せたらあかん" sensitive: "見せたらあかん"
click-to-show: "押してみ、見せたるわ" click-to-show: "押してみ、見せたるわ"
common/views/components/theme.vue: common/views/components/theme.vue:
light-theme: "非ダークモード時に使用するテーマ" light-theme: "ナイトゲームちゃう時のテーマどないする?"
dark-theme: "ダークモード時に使用するテーマ" dark-theme: "ナイトゲームの時のテーマどないする?"
light-themes: "明るいテーマ" light-themes: "デイゲーム"
dark-themes: "暗いテーマ" dark-themes: "ナイトゲーム"
install-a-theme: "テーマのインストール" install-a-theme: "テーマ入れるで"
theme-code: "テーマコード" theme-code: "テーマコード"
install: "インストール" install: "インストール"
installed: "「{}」をインストールしました" installed: "「{}」を入れたで!"
create-a-theme: "テーマの作成" create-a-theme: "テーマ作る"
save-created-theme: "テーマ保存" save-created-theme: "テーマ保存"
primary-color: "プライマリ カラー" primary-color: "この色一番重要や"
secondary-color: "セカンダリ カラー" secondary-color: "次はこの色出したって"
text-color: "文字" text-color: "文字はこの色や!"
base-theme: "ベーステーマ" base-theme: "この色が背景や!"
base-theme-light: "Light" base-theme-light: "Light"
base-theme-dark: "Dark" base-theme-dark: "Dark"
theme-name: "テーマ名" theme-name: "テーマ名"
preview-created-theme: "プレビュー" preview-created-theme: "試してみる"
invalid-theme: "テーマが正しくありません。" invalid-theme: "このテーマあかんわ、なんか間違うとる"
already-installed: "既にそのテーマはインストールされています。" already-installed: "のテーマもうあるで"
saved: "保存しました" saved: "保存したで!"
installed-themes: "インストールされたテーマ" manage-themes: "テーマの管理"
select-theme: "テーマを選択してください" builtin-themes: "いつものテーマ"
uninstall: "アンインストール" my-themes: "ワイのテーマ"
uninstalled: "「{}」をアンインストールしました" installed-themes: "入れたテーマ"
author: "作者" select-theme: "テーマ選んでや!"
uninstall: "ほかす"
uninstalled: "「{}」をほかしてもうたわ"
author: "作った人"
desc: "説明" desc: "説明"
export: "エクスポート" export: "エクスポート"
import: "インポート" import: "インポート"
import-by-code: "またはコードをペースト" import-by-code: "それかコードを貼っつける"
theme-name-required: "テーマ名は必須です。" theme-name-required: "テーマ名は絶対要るで"
common/views/components/cw-button.vue: common/views/components/cw-button.vue:
hide: "もうええわ" hide: "もうええわ"
show: "見たいやろ?" show: "見たいやろ?"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "もっと" detail: "もっと"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入りやめる"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留めやめる" unpin: "ピン留めやめる"
delete: "ほかす" delete: "ほかす"
@ -472,7 +476,7 @@ common/views/pages/follow.vue:
following: "フォローしとる" following: "フォローしとる"
follow: "フォロー" follow: "フォロー"
request-pending: "フォローの許し待っとる" request-pending: "フォローの許し待っとる"
follow-processing: "フォロー処理" follow-processing: "フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる" follow-request: "フォロー許してくれや!言うてみる"
desktop: desktop:
banner-crop-title: "どこバナーとして出す?" banner-crop-title: "どこバナーとして出す?"
@ -599,7 +603,7 @@ desktop/views/components/follow-button.vue:
following: "フォローしとる" following: "フォローしとる"
follow: "フォロー" follow: "フォロー"
request-pending: "フォローの許し待っとる" request-pending: "フォローの許し待っとる"
follow-processing: "フォロー処理" follow-processing: "フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる" follow-request: "フォロー許してくれや!言うてみる"
desktop/views/components/followers-window.vue: desktop/views/components/followers-window.vue:
followers: "{} のフォロワー" followers: "{} のフォロワー"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけとちゃうくて、予め登録しておいた物理的なデバイス(例えばあんさんのスマートフォンなど)も必要になり、よりセキュリティが向上すんで。" intro: "二段階認証を設定すると、サインイン時にパスワードだけとちゃうくて、予め登録しておいた物理的なデバイス(例えばあんさんのスマートフォンなど)も必要になり、よりセキュリティが向上すんで。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウント守る" locked-account: "アカウント守る"
is-locked: "他人のフォローは許可してからや!" is-locked: "他人のフォローは許可してからや!"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotやで" is-bot: "このアカウントはBotやで"
is-cat: "このアカウントはCatやで" is-cat: "このアカウントはCatやで"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ(md5)" hash: "ハッシュ(md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "ちょっと見せられへんわ" nsfw: "ちょっと見せられへんわ"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "見たらあかんで" sensitive: "見たらあかんで"
click-to-show: "押してみ、見せたるわ" click-to-show: "押してみ、見せたるわ"
@ -1080,7 +1091,7 @@ mobile/views/components/follow-button.vue:
following: "フォローしとる" following: "フォローしとる"
follow: "フォロー" follow: "フォロー"
request-pending: "フォローの許し待っとる" request-pending: "フォローの許し待っとる"
follow-processing: "フォロー処理" follow-processing: "フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる" follow-request: "フォロー許してくれや!言うてみる"
mobile/views/components/friends-maker.vue: mobile/views/components/friends-maker.vue:
title: "おもろそうやな" title: "おもろそうやな"
@ -1220,9 +1231,10 @@ mobile/views/pages/settings/settings.profile.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatや" is-cat: "このアカウントはCatや"
is-locked: "他人のフォローは許してからや!" is-locked: "他人のフォローは許してからや!"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシーオカンの年齢" privacy: "プライバシーってなんや?オカンの年齢か?"
save: "保存" save: "保存"
saved: "プロフィールを保存したで" saved: "プロフィールを保存したで"
uploading: "アップロードしとるで…" uploading: "アップロードしとるで…"
@ -1242,7 +1254,7 @@ mobile/views/pages/settings.vue:
specify-language: "言語選びや" specify-language: "言語選びや"
design: "見た感じ" design: "見た感じ"
dark-mode: "ナイトゲームや!" dark-mode: "ナイトゲームや!"
i-am-under-limited-internet: "電波がバァーっといけへんねん" i-am-under-limited-internet: "電波と阪神がザコいんや"
circle-icons: "アイコンもタコ焼きも丸いやんな?" circle-icons: "アイコンもタコ焼きも丸いやんな?"
contrasted-acct: "ユーザー名ようわからんし見やすしといて" contrasted-acct: "ユーザー名ようわからんし見やすしといて"
timeline: "タイムライン" timeline: "タイムライン"
@ -1254,8 +1266,8 @@ mobile/views/pages/settings.vue:
post-style-standard: "標準" post-style-standard: "標準"
post-style-smart: "べっぴんさん" post-style-smart: "べっぴんさん"
notification-position: "通知どこ見せる?" notification-position: "通知どこ見せる?"
notification-position-bottom: "ミナミ" notification-position-bottom: "ミナミの方"
notification-position-top: "キタ" notification-position-top: "キタの方"
theme: "テーマ" theme: "テーマ"
behavior: "動き" behavior: "動き"
fetch-on-scroll: "スクロールしたらもっと見せてや" fetch-on-scroll: "スクロールしたらもっと見せてや"

View File

@ -3,20 +3,20 @@ meta:
lang: "한국어" lang: "한국어"
divider: "" divider: ""
common: common:
misskey: "A ⭐ of fediverse" misskey: "연합우주의 ⭐"
about-title: "A ⭐ of fediverse." about-title: "연합우주의 ⭐."
about: "Misskey를 찾아 주셔서 감사합니다. Misskey은 지구에서 태어난 <b>분산 마이크로 블로그 SNS </b> 입니다. Fediverse (다양한 SNS로 구성되는 우주)에 존재하는 다른 SNS와 상호 연결되어 있습니다. 잠시 도시의 번잡함에서 벗어나 새로운 인터넷에 다이브 해 보지 않겠습니까." about: "Misskey를 찾아 주셔서 감사합니다. Misskey은 지구에서 태어난 <b>분산 마이크로 블로그 SNS </b> 입니다. Fediverse (다양한 SNS로 구성되는 우주)에 존재하는 다른 SNS와 상호 연결되어 있습니다. 잠시 도시의 번잡함에서 벗어나 새로운 인터넷에 다이브 해 보지 않겠습니까."
intro: intro:
title: "Misskeyって?" title: "Misskey란?"
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。" about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
features: "特徴" features: "특징"
rich-contents: "投稿" rich-contents: "게시"
rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。" rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。"
reaction: "リアクション" reaction: "반응"
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。" reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
ui: "インターフェース" ui: "인터페이스"
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。" ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
drive: "ドライブ" drive: "드라이브"
drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんかもしくは、アップロードしたファイルをフォルダ分けして整理したくなったことはありませんかMisskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。" drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんかもしくは、アップロードしたファイルをフォルダ分けして整理したくなったことはありませんかMisskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。"
outro: "他にもMisskeyにしかない機能はまだまだあるので、ぜひあなた自身の目で確かめてください。Misskeyは分散型SNSなので、このインスタンスが気に入らなければ他のインスタンスを試すこともできます。それでは、GLHF!" outro: "他にもMisskeyにしかない機能はまだまだあるので、ぜひあなた自身の目で確かめてください。Misskeyは分散型SNSなので、このインスタンスが気に入らなければ他のインスタンスを試すこともできます。それでは、GLHF!"
adblock: adblock:
@ -71,7 +71,7 @@ common:
friday: "금요일" friday: "금요일"
saturday: "토요일" saturday: "토요일"
reactions: reactions:
like: "いいね" like: "좋아요"
love: "좋아" love: "좋아"
laugh: "크크" laugh: "크크"
hmm: "음..." hmm: "음..."
@ -82,14 +82,14 @@ common:
rip: "RIP" rip: "RIP"
pudding: "Pudding" pudding: "Pudding"
note-visibility: note-visibility:
public: "公開" public: "공개"
home: "ホーム" home: ""
home-desc: "ホームタイムラインにのみ公開" home-desc: "홈 타임라인에만 공개"
followers: "フォロワー" followers: "팔로워"
followers-desc: "自分のフォロワーにのみ公開" followers-desc: "자신의 팔로워에게만 공개"
specified: "ダイレクト" specified: "다이렉트"
specified-desc: "指定したユーザーにのみ公開" specified-desc: "지정한 사용자에게만 공개"
private: "非公開" private: "비공개"
note-placeholders: note-placeholders:
a: "지금 어떻게하고있어?" a: "지금 어떻게하고있어?"
b: "뭔가 있었습니까?" b: "뭔가 있었습니까?"
@ -107,14 +107,14 @@ common:
i-like-sushi: "나는(푸딩보다 오히려)스시가 좋아" i-like-sushi: "나는(푸딩보다 오히려)스시가 좋아"
show-reversi-board-labels: "리버시 보드의 행과 열 레이블을 표시" show-reversi-board-labels: "리버시 보드의 행과 열 레이블을 표시"
use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける" use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
verified-user: "公式アカウント" verified-user: "공식 계정"
disable-animated-mfm: "게시물의 문자 애니메이션을 비활성화 할" disable-animated-mfm: "게시물의 문자 애니메이션을 비활성화 할"
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "항상 열람주의 미디어를 표시"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
show-full-acct: "ユーザー名のホストを省略しない" show-full-acct: "ユーザー名のホストを省略しない"
reduce-motion: "UIの動きを減らす" reduce-motion: "UIの動きを減らす"
this-setting-is-this-device-only: "このデバイスのみ" this-setting-is-this-device-only: "이 장치만"
do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。' do-not-use-in-production: '이것은 개발 빌드입니다. 프로덕션 환경에서 사용하지 마십시오.'
reversi: reversi:
drawn: "무승부" drawn: "무승부"
my-turn: "당신의 차례입니다" my-turn: "당신의 차례입니다"
@ -155,19 +155,19 @@ common:
home: "홈" home: "홈"
local: "로컬" local: "로컬"
hybrid: "소셜" hybrid: "소셜"
hashtag: "ハッシュタグ" hashtag: "해시태그"
global: "글로벌" global: "글로벌"
mentions: "あなた宛て" mentions: "あなた宛て"
direct: "ダイレクト投稿" direct: "ダイレクト投稿"
notifications: "통지" notifications: "통지"
list: "목록" list: "목록"
swap-left: "左に移動" swap-left: "왼쪽으로 이동"
swap-right: "右に移動" swap-right: "오른쪽으로 이동"
swap-up: "上に移動" swap-up: "위로 이동"
swap-down: "下に移動" swap-down: "아래로 이동"
remove: "カラムを削除" remove: "칼럼 제거"
add-column: "カラムを追加" add-column: "칼럼 추가"
rename: "名前を変更" rename: "이름 변경"
stack-left: "左に重ねる" stack-left: "左に重ねる"
pop-right: "右に出す" pop-right: "右に出す"
auth/views/form.vue: auth/views/form.vue:
@ -248,44 +248,47 @@ common/views/components/connect-failed.troubleshooter.vue:
checking-network: "ネットワーク接続を確認中" checking-network: "ネットワーク接続を確認中"
internet: "インターネット接続" internet: "インターネット接続"
checking-internet: "インターネット接続を確認中" checking-internet: "インターネット接続を確認中"
server: "サーバー接続" server: "서버 연결"
checking-server: "サーバー接続を確認中" checking-server: "サーバー接続を確認中"
finding: "問題を調べています" finding: "問題を調べています"
no-network: "ネットワークに接続されていません" no-network: "ネットワークに接続されていません"
no-network-desc: "お使いのPCのネットワーク接続が正常か確認してください。" no-network-desc: "お使いのPCのネットワーク接続が正常か確認してください。"
no-internet: "インターネットに接続されていません" no-internet: "インターネットに接続されていません"
no-internet-desc: "ネットワークには接続されていますが、インターネットには接続されていないようです。お使いのPCのインターネット接続が正常か確認してください。" no-internet-desc: "ネットワークには接続されていますが、インターネットには接続されていないようです。お使いのPCのインターネット接続が正常か確認してください。"
no-server: "Misskeyのサーバーに接続できません" no-server: "Misskey 서버에 연결할 수 없습니다."
no-server-desc: "お使いのPCのインターネット接続は正常ですが、Misskeyのサーバーには接続できませんでした。サーバーがダウンまたはメンテナンスしている可能性があるので、しばらくしてから再度御アクセスください。" no-server-desc: "お使いのPCのインターネット接続は正常ですが、Misskeyのサーバーには接続できませんでした。サーバーがダウンまたはメンテナンスしている可能性があるので、しばらくしてから再度御アクセスください。"
success: "Misskeyのサーバーに接続できました" success: "Misskeyのサーバーに接続できました"
success-desc: "正常に接続できるようです。ページを再度読み込みしてください。" success-desc: "正常に接続できるようです。ページを再度読み込みしてください。"
flush: "キャッシュの削除" flush: "캐시 삭제"
set-version: "バージョン指定" set-version: "버전 지정"
common/views/components/media-banner.vue: common/views/components/media-banner.vue:
sensitive: "閲覧注意" sensitive: "열람주의"
click-to-show: "クリックして表示" click-to-show: "클릭하여 표시"
common/views/components/theme.vue: common/views/components/theme.vue:
light-theme: "非ダークモード時に使用するテーマ" light-theme: "非ダークモード時に使用するテーマ"
dark-theme: "ダークモード時に使用するテーマ" dark-theme: "ダークモード時に使用するテーマ"
light-themes: "明るいテーマ" light-themes: "밝은 테마"
dark-themes: "暗いテーマ" dark-themes: "어두운 테마"
install-a-theme: "テーマのインストール" install-a-theme: "테마 설치"
theme-code: "テーマコード" theme-code: "테마 코드"
install: "インストール" install: "설치"
installed: "「{}」をインストールしました" installed: "「{}」をインストールしました"
create-a-theme: "テーマの作成" create-a-theme: "테마 만들기"
save-created-theme: "テーマを保存" save-created-theme: "테마 저장"
primary-color: "プライマリ カラー" primary-color: "기본 색"
secondary-color: "セカンダリ カラー" secondary-color: "보조 색"
text-color: "文字色" text-color: "글자 색상"
base-theme: "ベーステーマ" base-theme: "ベーステーマ"
base-theme-light: "Light" base-theme-light: "Light"
base-theme-dark: "Dark" base-theme-dark: "Dark"
theme-name: "テーマ名" theme-name: "테마명"
preview-created-theme: "プレビュー" preview-created-theme: "미리보기"
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -305,7 +308,7 @@ common/views/components/messaging.vue:
no-history: "履歴はありません" no-history: "履歴はありません"
common/views/components/messaging-room.vue: common/views/components/messaging-room.vue:
empty: "このユーザーと話したことはありません" empty: "このユーザーと話したことはありません"
more: "もっと読む" more: "더 보기"
no-history: "これより過去の履歴はありません" no-history: "これより過去の履歴はありません"
resize-form: "ドラッグしてフォームの広さを調整" resize-form: "ドラッグしてフォームの広さを調整"
new-message: "新しいメッセージがあります" new-message: "新しいメッセージがあります"
@ -320,18 +323,19 @@ common/views/components/messaging-room.message.vue:
is-read: "읽음" is-read: "읽음"
deleted: "このメッセージは削除されました" deleted: "このメッセージは削除されました"
common/views/components/nav.vue: common/views/components/nav.vue:
about: "Misskeyについて" about: "Misskey에 대하여"
stats: "統計" stats: "통계"
status: "ステータス" status: "ステータス"
wiki: "Wiki" wiki: "Wiki"
donors: "ドナー" donors: "기증자"
repository: "リポジトリ" repository: "저장소"
develop: "開発者" develop: "개발자"
feedback: "フィードバック" feedback: "피드백"
common/views/components/note-menu.vue: common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "링크 복사"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Deze notitie toevoegen aan favorieten" favorite: "Deze notitie toevoegen aan favorieten"
unfavorite: "お気に入り解除"
pin: "Vastmaken aan profielpagina" pin: "Vastmaken aan profielpagina"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Hulpmiddelen" tools: "Hulpmiddelen"
task-manager: "Taakbeheer" task-manager: "Taakbeheer"
third-parties: "Derde partij" third-parties: "Derde partij"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "Als je verificatie in twee stappen instelt, dan heb je niet alleen een wachtwoord nodig bij het inloggen, maar ook een geregistreerd fysiek apparaat (zoals je smartphone). Dit verhoogt de veiligheid. " intro: "Als je verificatie in twee stappen instelt, dan heb je niet alleen een wachtwoord nodig bij het inloggen, maar ook een geregistreerd fysiek apparaat (zoals je smartphone). Dit verhoogt de veiligheid. "
detail: "Details bekijken..." detail: "Details bekijken..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Profiel bijwerken" save: "Profiel bijwerken"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "Dit account is een Bot" is-bot: "Dit account is een Bot"
is-cat: "Dit account is een Kat" is-cat: "Dit account is een Kat"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "Omslagfoto" banner: "Omslagfoto"
is-cat: "Dit account is een Kat" is-cat: "Dit account is een Kat"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "Profiel bijwerken" save: "Profiel bijwerken"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "Detaljer" detail: "Detaljer"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Merket som favoritt" favorite: "Merket som favoritt"
unfavorite: "お気に入り解除"
pin: "Fest til profilen din" pin: "Fest til profilen din"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Slett" delete: "Slett"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Verktøy" tools: "Verktøy"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "Detaljer..." detail: "Detaljer..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Lagre profilen" save: "Lagre profilen"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "Annet" other: "Annet"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "NSFW" nsfw: "NSFW"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "NSFW" sensitive: "NSFW"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "Banner" banner: "Banner"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "Avansert" advanced: "Avansert"
privacy: "プライバシー" privacy: "プライバシー"
save: "Lagre profilen" save: "Lagre profilen"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "Dodaj do ulubionych" favorite: "Dodaj do ulubionych"
unfavorite: "お気に入り解除"
pin: "Przypnij do profilu" pin: "Przypnij do profilu"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "Usuń" delete: "Usuń"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "Narzędzia" tools: "Narzędzia"
task-manager: "Menedżer zadań" task-manager: "Menedżer zadań"
third-parties: "Autorzy trzeci" third-parties: "Autorzy trzeci"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "Jeżeli skonfigurujesz uwierzytelnianie dwuetapowe, aby zablokować się będziesz potrzebować (oprócz hasła) kodu ze skonfigurowanego urządzenia (np. smartfonu), co zwiększy bezpieczeństwo." intro: "Jeżeli skonfigurujesz uwierzytelnianie dwuetapowe, aby zablokować się będziesz potrzebować (oprócz hasła) kodu ze skonfigurowanego urządzenia (np. smartfonu), co zwiększy bezpieczeństwo."
detail: "Zobacz szczegóły…" detail: "Zobacz szczegóły…"
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "Aktualizuj profil" save: "Aktualizuj profil"
locked-account: "Zabezpiecz swoje konto" locked-account: "Zabezpiecz swoje konto"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "Inne" other: "Inne"
is-bot: "To konto jest prowadzone przez bota" is-bot: "To konto jest prowadzone przez bota"
is-cat: "To konto jest prowadzone przez kota" is-cat: "To konto jest prowadzone przez kota"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "To jest zawartość NSFW" sensitive: "To jest zawartość NSFW"
click-to-show: "Naciśnij aby wyświetlić" click-to-show: "Naciśnij aby wyświetlić"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "Baner" banner: "Baner"
is-cat: "To konto jest prowadzone przez kota" is-cat: "To konto jest prowadzone przez kota"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "Aktualizuj profil" save: "Aktualizuj profil"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "Capa" banner: "Capa"
is-cat: "Esta conta é gato" is-cat: "Esta conta é gato"
is-locked: "Pedido para seguir precisa ser aprovado" is-locked: "Pedido para seguir precisa ser aprovado"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "Avançado" advanced: "Avançado"
privacy: "Provacidade" privacy: "Provacidade"
save: "Atualizar perfil" save: "Atualizar perfil"

View File

@ -3,9 +3,9 @@ meta:
lang: "Русский язык" lang: "Русский язык"
divider: "" divider: ""
common: common:
misskey: "A ⭐ of fediverse" misskey: "Мы — ⭐ fediverse"
about-title: "A ⭐ of fediverse." about-title: "Мы — ⭐ fediverse"
about: "Misskeyを見つけていただき、ありがとうございます。Misskeyは、地球で生まれた<b>分散マイクロブログSNS</b>です。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。" about: "Спасибо, что нашли Misskey. Misskey — это <b>децентрализованная платформа для микроблоггинга</b> родом с планеты Земля. Поскольку она существует внутри Fediverse (вселенной различных социальных платформ), она связана с другими платформами. Отдохните от шума большого города — и познакомьтесь с новым интернетом."
intro: intro:
title: "Misskeyって" title: "Misskeyって"
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。" about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -286,6 +286,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。" invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。" already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました" saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ" installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください" select-theme: "テーマを選択してください"
uninstall: "アンインストール" uninstall: "アンインストール"
@ -332,6 +335,7 @@ common/views/components/note-menu.vue:
detail: "詳細" detail: "詳細"
copy-link: "リンクをコピー" copy-link: "リンクをコピー"
favorite: "お気に入り" favorite: "お気に入り"
unfavorite: "お気に入り解除"
pin: "ピン留め" pin: "ピン留め"
unpin: "ピン留め解除" unpin: "ピン留め解除"
delete: "削除" delete: "削除"
@ -781,6 +785,10 @@ desktop/views/components/settings.vue:
tools: "ツール" tools: "ツール"
task-manager: "タスクマネージャ" task-manager: "タスクマネージャ"
third-parties: "サードパーティ" third-parties: "サードパーティ"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
desktop/views/components/settings.2fa.vue: desktop/views/components/settings.2fa.vue:
intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。" intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
detail: "詳細..." detail: "詳細..."
@ -830,6 +838,7 @@ desktop/views/components/settings.profile.vue:
save: "保存" save: "保存"
locked-account: "アカウントの保護" locked-account: "アカウントの保護"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
other: "その他" other: "その他"
is-bot: "このアカウントはBotです" is-bot: "このアカウントはBotです"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
@ -1070,6 +1079,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意" nsfw: "閲覧注意"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"
@ -1221,6 +1232,7 @@ mobile/views/pages/settings/settings.profile.vue:
banner: "バナー" banner: "バナー"
is-cat: "このアカウントはCatです" is-cat: "このアカウントはCatです"
is-locked: "フォローを承認制にする" is-locked: "フォローを承認制にする"
careful-bot: "Botからのフォローだけ承認制にする"
advanced: "その他" advanced: "その他"
privacy: "プライバシー" privacy: "プライバシー"
save: "保存" save: "保存"

View File

@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "10.0.0", "version": "10.21.3",
"clientVersion": "1.0.10328", "clientVersion": "1.0.10618",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "main": "./built/index.js",
"private": true, "private": true,
@ -20,26 +20,27 @@
"format": "gulp format" "format": "gulp format"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "1.2.4", "@fortawesome/fontawesome-svg-core": "1.2.6",
"@fortawesome/free-brands-svg-icons": "5.3.1", "@fortawesome/free-brands-svg-icons": "5.4.1",
"@fortawesome/free-regular-svg-icons": "5.3.1", "@fortawesome/free-regular-svg-icons": "5.4.1",
"@fortawesome/free-solid-svg-icons": "5.3.1", "@fortawesome/free-solid-svg-icons": "5.4.1",
"@koa/cors": "2.2.2", "@koa/cors": "2.2.2",
"@prezzemolo/rap": "0.1.2", "@prezzemolo/rap": "0.1.2",
"@prezzemolo/zip": "0.0.3", "@prezzemolo/zip": "0.0.3",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/chai-http": "3.0.5",
"@types/dateformat": "1.0.1", "@types/dateformat": "1.0.1",
"@types/debug": "0.0.31", "@types/debug": "0.0.31",
"@types/deep-equal": "1.0.1", "@types/deep-equal": "1.0.1",
"@types/double-ended-queue": "2.1.0", "@types/double-ended-queue": "2.1.0",
"@types/elasticsearch": "5.0.26", "@types/elasticsearch": "5.0.27",
"@types/file-type": "5.2.1", "@types/file-type": "5.2.1",
"@types/gulp": "3.8.36", "@types/gulp": "3.8.36",
"@types/gulp-htmlmin": "1.3.32", "@types/gulp-htmlmin": "1.3.32",
"@types/gulp-mocha": "0.0.32", "@types/gulp-mocha": "0.0.32",
"@types/gulp-rename": "0.0.33", "@types/gulp-rename": "0.0.33",
"@types/gulp-replace": "0.0.31", "@types/gulp-replace": "0.0.31",
"@types/gulp-uglify": "3.0.5", "@types/gulp-uglify": "3.0.6",
"@types/gulp-util": "3.0.34", "@types/gulp-util": "3.0.34",
"@types/is-root": "1.0.0", "@types/is-root": "1.0.0",
"@types/is-url": "1.2.28", "@types/is-url": "1.2.28",
@ -48,7 +49,7 @@
"@types/koa-bodyparser": "5.0.1", "@types/koa-bodyparser": "5.0.1",
"@types/koa-compress": "2.0.8", "@types/koa-compress": "2.0.8",
"@types/koa-favicon": "2.0.19", "@types/koa-favicon": "2.0.19",
"@types/koa-logger": "3.1.0", "@types/koa-logger": "3.1.1",
"@types/koa-mount": "3.0.1", "@types/koa-mount": "3.0.1",
"@types/koa-multer": "1.0.0", "@types/koa-multer": "1.0.0",
"@types/koa-router": "7.0.32", "@types/koa-router": "7.0.32",
@ -58,19 +59,19 @@
"@types/minio": "7.0.0", "@types/minio": "7.0.0",
"@types/mkdirp": "0.5.2", "@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.3", "@types/mocha": "5.2.3",
"@types/mongodb": "3.1.10", "@types/mongodb": "3.1.12",
"@types/ms": "0.7.30", "@types/ms": "0.7.30",
"@types/node": "10.11.4", "@types/node": "10.12.0",
"@types/portscanner": "2.1.0", "@types/portscanner": "2.1.0",
"@types/pug": "2.0.4", "@types/pug": "2.0.4",
"@types/qrcode": "1.3.0", "@types/qrcode": "1.3.0",
"@types/ratelimiter": "2.1.28", "@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.6", "@types/redis": "2.8.7",
"@types/request": "2.47.1", "@types/request": "2.47.1",
"@types/request-promise-native": "1.0.15", "@types/request-promise-native": "1.0.15",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
"@types/seedrandom": "2.4.27", "@types/seedrandom": "2.4.27",
"@types/sharp": "0.17.10", "@types/sharp": "0.21.0",
"@types/showdown": "1.7.5", "@types/showdown": "1.7.5",
"@types/single-line-log": "1.1.0", "@types/single-line-log": "1.1.0",
"@types/speakeasy": "2.0.2", "@types/speakeasy": "2.0.2",
@ -78,7 +79,7 @@
"@types/tinycolor2": "1.4.1", "@types/tinycolor2": "1.4.1",
"@types/tmp": "0.0.33", "@types/tmp": "0.0.33",
"@types/uuid": "3.4.4", "@types/uuid": "3.4.4",
"@types/webpack": "4.4.14", "@types/webpack": "4.4.17",
"@types/webpack-stream": "3.2.10", "@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
@ -90,13 +91,15 @@
"bee-queue": "1.2.2", "bee-queue": "1.2.2",
"bootstrap-vue": "2.0.0-rc.11", "bootstrap-vue": "2.0.0-rc.11",
"cafy": "11.3.0", "cafy": "11.3.0",
"chai": "4.2.0",
"chai-http": "4.2.0",
"chalk": "2.4.1", "chalk": "2.4.1",
"chart.js": "2.7.2", "chart.js": "2.7.3",
"commander": "2.17.1", "commander": "2.19.0",
"crc-32": "1.2.0", "crc-32": "1.2.0",
"css-loader": "1.0.0", "css-loader": "1.0.0",
"dateformat": "3.0.3", "dateformat": "3.0.3",
"debug": "4.0.1", "debug": "4.1.0",
"deep-equal": "1.0.1", "deep-equal": "1.0.1",
"deepcopy": "0.6.3", "deepcopy": "0.6.3",
"diskusage": "0.2.5", "diskusage": "0.2.5",
@ -157,7 +160,7 @@
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"mocha": "5.2.0", "mocha": "5.2.0",
"moji": "0.5.1", "moji": "0.5.1",
"mongodb": "3.1.1", "mongodb": "3.1.8",
"monk": "6.0.6", "monk": "6.0.6",
"ms": "2.1.1", "ms": "2.1.1",
"nan": "2.11.1", "nan": "2.11.1",
@ -169,13 +172,14 @@
"parse5": "5.1.0", "parse5": "5.1.0",
"portscanner": "2.2.0", "portscanner": "2.2.0",
"progress-bar-webpack-plugin": "1.11.0", "progress-bar-webpack-plugin": "1.11.0",
"promise-limit": "2.7.0",
"promise-sequential": "1.1.1", "promise-sequential": "1.1.1",
"pug": "2.0.3", "pug": "2.0.3",
"punycode": "2.1.1", "punycode": "2.1.1",
"qrcode": "1.3.0", "qrcode": "1.3.0",
"ratelimiter": "3.2.0", "ratelimiter": "3.2.0",
"recaptcha-promise": "0.1.3", "recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.5", "reconnecting-websocket": "4.1.8",
"redis": "2.8.0", "redis": "2.8.0",
"request": "2.88.0", "request": "2.88.0",
"request-promise-native": "1.0.5", "request-promise-native": "1.0.5",
@ -191,7 +195,7 @@
"single-line-log": "1.1.2", "single-line-log": "1.1.2",
"speakeasy": "2.0.0", "speakeasy": "2.0.0",
"stringz": "1.0.0", "stringz": "1.0.0",
"style-loader": "0.23.0", "style-loader": "0.23.1",
"stylus": "0.54.5", "stylus": "0.54.5",
"stylus-loader": "3.0.2", "stylus-loader": "3.0.2",
"summaly": "2.2.0", "summaly": "2.2.0",
@ -204,21 +208,23 @@
"ts-node": "7.0.1", "ts-node": "7.0.1",
"tslint": "5.10.0", "tslint": "5.10.0",
"typescript": "2.9.2", "typescript": "2.9.2",
"typescript-eslint-parser": "19.0.2", "typescript-eslint-parser": "20.0.0",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
"url-loader": "1.1.1", "url-loader": "1.1.2",
"uuid": "3.3.2", "uuid": "3.3.2",
"v-animate-css": "0.0.2", "v-animate-css": "0.0.2",
"vue": "2.5.17", "vue": "2.5.17",
"vue-chartjs": "3.4.0", "vue-chartjs": "3.4.0",
"vue-color": "2.6.0", "vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "2.2.2", "vue-cropperjs": "2.2.2",
"vue-js-modal": "1.3.26", "vue-js-modal": "1.3.26",
"vue-json-tree-view": "2.1.4", "vue-json-tree-view": "2.1.4",
"vue-loader": "15.4.2", "vue-loader": "15.4.2",
"vue-router": "3.0.1", "vue-router": "3.0.1",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.0", "vue-svg-inline-loader": "1.2.1",
"vue-sweetalert2": "1.5.5",
"vue-template-compiler": "2.5.17", "vue-template-compiler": "2.5.17",
"vuedraggable": "2.16.0", "vuedraggable": "2.16.0",
"vuewordcloud": "18.7.11", "vuewordcloud": "18.7.11",

View File

@ -130,3 +130,29 @@ pre
[data-fa] [data-fa]
display inline-block display inline-block
.swal2-container
z-index 10000 !important
&.swal2-shown
background-color rgba(0, 0, 0, 0.5) !important
.swal2-popup
background var(--face) !important
.swal2-content
color var(--text) !important
.swal2-confirm
background-color var(--primary) !important
border-left-color var(--primary) !important
border-right-color var(--primary) !important
color var(--primaryForeground) !important
&:hover
background-image none !important
background-color var(--primaryDarken5) !important
&:active
background-image none !important
background-color var(--primaryDarken5) !important

View File

@ -142,7 +142,7 @@
localStorage.setItem('shouldFlush', 'false'); localStorage.setItem('shouldFlush', 'false');
// Random // Random
localStorage.setItem('salt', Math.random().toString()); localStorage.setItem('salt', Math.random().toString().substr(2, 8));
// Clear cache (service worker) // Clear cache (service worker)
try { try {

View File

@ -0,0 +1,178 @@
import parse from '../../../../mfm/parse';
import { sum } from '../../../../prelude/array';
import MkNoteMenu from '../views/components/note-menu.vue';
import MkReactionPicker from '../views/components/reaction-picker.vue';
import Ok from '../views/components/ok.vue';
function focus(el, fn) {
const target = fn(el);
if (target) {
if (target.hasAttribute('tabindex')) {
target.focus();
} else {
focus(target, fn);
}
}
}
type Opts = {
mobile?: boolean;
};
export default (opts: Opts = {}) => ({
data() {
return {
showContent: false
};
},
computed: {
keymap(): any {
return {
'r|left': () => this.reply(true),
'e|a|plus': () => this.react(true),
'q|right': () => this.renote(true),
'f|b': this.favorite,
'delete|ctrl+d': this.del,
'ctrl+q|ctrl+right': this.renoteDirectly,
'up|k|shift+tab': this.focusBefore,
'down|j|tab': this.focusAfter,
'esc': this.blur,
'm|o': () => this.menu(true),
's': this.toggleShowContent,
'1': () => this.reactDirectly('like'),
'2': () => this.reactDirectly('love'),
'3': () => this.reactDirectly('laugh'),
'4': () => this.reactDirectly('hmm'),
'5': () => this.reactDirectly('surprise'),
'6': () => this.reactDirectly('congrats'),
'7': () => this.reactDirectly('angry'),
'8': () => this.reactDirectly('confused'),
'9': () => this.reactDirectly('rip'),
'0': () => this.reactDirectly('pudding'),
};
},
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.fileIds.length == 0 &&
this.note.poll == null);
},
appearNote(): any {
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.appearNote.reactionCounts
? sum(Object.values(this.appearNote.reactionCounts))
: 0;
},
title(): string {
return new Date(this.appearNote.createdAt).toLocaleString();
},
urls(): string[] {
if (this.appearNote.text) {
const ast = parse(this.appearNote.text);
return ast
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
.map(t => t.url);
} else {
return null;
}
}
},
methods: {
reply(viaKeyboard = false) {
(this as any).apis.post({
reply: this.appearNote,
animation: !viaKeyboard,
cb: () => {
this.focus();
}
});
},
renote(viaKeyboard = false) {
(this as any).apis.post({
renote: this.appearNote,
animation: !viaKeyboard,
cb: () => {
this.focus();
}
});
},
renoteDirectly() {
(this as any).api('notes/create', {
renoteId: this.appearNote.id
});
},
react(viaKeyboard = false) {
this.blur();
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.appearNote,
showFocus: viaKeyboard,
animation: !viaKeyboard,
compact: opts.mobile,
big: opts.mobile
}).$once('closed', this.focus);
},
reactDirectly(reaction) {
(this as any).api('notes/reactions/create', {
noteId: this.appearNote.id,
reaction: reaction
});
},
favorite() {
(this as any).api('notes/favorites/create', {
noteId: this.appearNote.id
}).then(() => {
(this as any).os.new(Ok);
});
},
del() {
(this as any).api('notes/delete', {
noteId: this.appearNote.id
});
},
menu(viaKeyboard = false) {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.appearNote,
animation: !viaKeyboard,
compact: opts.mobile,
}).$once('closed', this.focus);
},
toggleShowContent() {
this.showContent = !this.showContent;
},
focus() {
this.$el.focus();
},
blur() {
this.$el.blur();
},
focusBefore() {
focus(this.$el, e => e.previousElementSibling);
},
focusAfter() {
focus(this.$el, e => e.nextElementSibling);
}
}
});

View File

@ -13,14 +13,14 @@ export default prop => ({
}, },
$_ns_isRenote(): boolean { $_ns_isRenote(): boolean {
return (this.$_ns_note_.renote && return (this.$_ns_note_.renote != null &&
this.$_ns_note_.text == null && this.$_ns_note_.text == null &&
this.$_ns_note_.fileIds.length == 0 && this.$_ns_note_.fileIds.length == 0 &&
this.$_ns_note_.poll == null); this.$_ns_note_.poll == null);
}, },
$_ns_target(): any { $_ns_target(): any {
return this._ns_isRenote ? this.$_ns_note_.renote : this.$_ns_note_; return this.$_ns_isRenote ? this.$_ns_note_.renote : this.$_ns_note_;
}, },
}, },
@ -86,8 +86,20 @@ export default prop => ({
switch (type) { switch (type) {
case 'reacted': { case 'reacted': {
const reaction = body.reaction; const reaction = body.reaction;
if (this.$_ns_target.reactionCounts == null) Vue.set(this.$_ns_target, 'reactionCounts', {});
this.$_ns_target.reactionCounts[reaction] = (this.$_ns_target.reactionCounts[reaction] || 0) + 1; if (this.$_ns_target.reactionCounts == null) {
Vue.set(this.$_ns_target, 'reactionCounts', {});
}
if (this.$_ns_target.reactionCounts[reaction] == null) {
Vue.set(this.$_ns_target.reactionCounts, reaction, 0);
}
this.$_ns_target.reactionCounts[reaction]++;
if (body.userId == this.$store.state.i.id) {
Vue.set(this.$_ns_target, 'myReaction', reaction);
}
break; break;
} }

View File

@ -9,8 +9,8 @@ import MiOS from '../../mios';
*/ */
export default class Stream extends EventEmitter { export default class Stream extends EventEmitter {
private stream: ReconnectingWebsocket; private stream: ReconnectingWebsocket;
private state: string; public state: string;
private buffer: any[]; private sharedConnectionPools: Pool[] = [];
private sharedConnections: SharedConnection[] = []; private sharedConnections: SharedConnection[] = [];
private nonSharedConnections: NonSharedConnection[] = []; private nonSharedConnections: NonSharedConnection[] = [];
@ -18,7 +18,6 @@ export default class Stream extends EventEmitter {
super(); super();
this.state = 'initializing'; this.state = 'initializing';
this.buffer = [];
const user = os.store.state.i; const user = os.store.state.i;
@ -26,114 +25,34 @@ export default class Stream extends EventEmitter {
this.stream.addEventListener('open', this.onOpen); this.stream.addEventListener('open', this.onOpen);
this.stream.addEventListener('close', this.onClose); this.stream.addEventListener('close', this.onClose);
this.stream.addEventListener('message', this.onMessage); this.stream.addEventListener('message', this.onMessage);
if (user) {
const main = this.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
os.store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
os.store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
os.store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('readAllMessagingMessages', () => {
os.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
os.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
});
main.on('unreadMention', () => {
os.store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
os.store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
os.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
os.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('clientSettingUpdated', x => {
os.store.commit('settings/set', {
key: x.key,
value: x.value
});
});
main.on('homeUpdated', x => {
os.store.commit('settings/setHome', x);
});
main.on('mobileHomeUpdated', x => {
os.store.commit('settings/setMobileHome', x);
});
main.on('widgetUpdated', x => {
os.store.commit('settings/setWidget', {
id: x.id,
data: x.data
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
alert('%i18n:common.my-token-regenerated%');
os.signout();
});
}
} }
public useSharedConnection = (channel: string): SharedConnection => { @autobind
const existConnection = this.sharedConnections.find(c => c.channel === channel); public useSharedConnection(channel: string): SharedConnection {
let pool = this.sharedConnectionPools.find(p => p.channel === channel);
if (existConnection) { if (pool == null) {
existConnection.use(); pool = new Pool(this, channel);
return existConnection; this.sharedConnectionPools.push(pool);
} else {
const connection = new SharedConnection(this, channel);
connection.use();
this.sharedConnections.push(connection);
return connection;
} }
const connection = new SharedConnection(this, channel, pool);
this.sharedConnections.push(connection);
return connection;
} }
@autobind @autobind
public removeSharedConnection(connection: SharedConnection) { public removeSharedConnection(connection: SharedConnection) {
this.sharedConnections = this.sharedConnections.filter(c => c.id !== connection.id); this.sharedConnections = this.sharedConnections.filter(c => c !== connection);
} }
public connectToChannel = (channel: string, params?: any): NonSharedConnection => { @autobind
public removeSharedConnectionPool(pool: Pool) {
this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool);
}
@autobind
public connectToChannel(channel: string, params?: any): NonSharedConnection {
const connection = new NonSharedConnection(this, channel, params); const connection = new NonSharedConnection(this, channel, params);
this.nonSharedConnections.push(connection); this.nonSharedConnections.push(connection);
return connection; return connection;
@ -141,7 +60,7 @@ export default class Stream extends EventEmitter {
@autobind @autobind
public disconnectToChannel(connection: NonSharedConnection) { public disconnectToChannel(connection: NonSharedConnection) {
this.nonSharedConnections = this.nonSharedConnections.filter(c => c.id !== connection.id); this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection);
} }
/** /**
@ -154,17 +73,10 @@ export default class Stream extends EventEmitter {
this.state = 'connected'; this.state = 'connected';
this.emit('_connected_'); this.emit('_connected_');
// バッファーを処理
const _buffer = [].concat(this.buffer); // Shallow copy
this.buffer = []; // Clear buffer
_buffer.forEach(data => {
this.send(data); // Resend each buffered messages
});
// チャンネル再接続 // チャンネル再接続
if (isReconnect) { if (isReconnect) {
this.sharedConnections.forEach(c => { this.sharedConnectionPools.forEach(p => {
c.connect(); p.connect();
}); });
this.nonSharedConnections.forEach(c => { this.nonSharedConnections.forEach(c => {
c.connect(); c.connect();
@ -177,8 +89,10 @@ export default class Stream extends EventEmitter {
*/ */
@autobind @autobind
private onClose() { private onClose() {
this.state = 'reconnecting'; if (this.state == 'connected') {
this.emit('_disconnected_'); this.state = 'reconnecting';
this.emit('_disconnected_');
}
} }
/** /**
@ -190,8 +104,18 @@ export default class Stream extends EventEmitter {
if (type == 'channel') { if (type == 'channel') {
const id = body.id; const id = body.id;
const connection = this.sharedConnections.find(c => c.id === id) || this.nonSharedConnections.find(c => c.id === id);
connection.emit(body.type, body.body); let connections: Connection[];
connections = this.sharedConnections.filter(c => c.id === id);
if (connections.length === 0) {
connections = [this.nonSharedConnections.find(c => c.id === id)];
}
connections.filter(c => c != null).forEach(c => {
c.emit(body.type, body.body);
});
} else { } else {
this.emit(type, body); this.emit(type, body);
} }
@ -207,12 +131,6 @@ export default class Stream extends EventEmitter {
body: payload body: payload
}; };
// まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する
if (this.state != 'connected') {
this.buffer.push(data);
return;
}
this.stream.send(JSON.stringify(data)); this.stream.send(JSON.stringify(data));
} }
@ -226,19 +144,139 @@ export default class Stream extends EventEmitter {
} }
} }
abstract class Connection extends EventEmitter { class Pool {
public channel: string; public channel: string;
public id: string; public id: string;
protected params: any;
protected stream: Stream; protected stream: Stream;
public users = 0;
private disposeTimerId: any;
private isConnected = false;
constructor(stream: Stream, channel: string, params?: any) { constructor(stream: Stream, channel: string) {
this.channel = channel;
this.stream = stream;
this.id = Math.random().toString().substr(2, 8);
this.stream.on('_disconnected_', this.onStreamDisconnected);
}
@autobind
private onStreamDisconnected() {
this.isConnected = false;
}
@autobind
public inc() {
if (this.users === 0 && !this.isConnected) {
this.connect();
}
this.users++;
// タイマー解除
if (this.disposeTimerId) {
clearTimeout(this.disposeTimerId);
this.disposeTimerId = null;
}
}
@autobind
public dec() {
this.users--;
// そのコネクションの利用者が誰もいなくなったら
if (this.users === 0) {
// また直ぐに再利用される可能性があるので、一定時間待ち、
// 新たな利用者が現れなければコネクションを切断する
this.disposeTimerId = setTimeout(() => {
this.disconnect();
}, 3000);
}
}
@autobind
public connect() {
if (this.isConnected) return;
this.isConnected = true;
this.stream.send('connect', {
channel: this.channel,
id: this.id
});
}
@autobind
private disconnect() {
this.stream.off('_disconnected_', this.onStreamDisconnected);
this.stream.send('disconnect', { id: this.id });
this.stream.removeSharedConnectionPool(this);
}
}
abstract class Connection extends EventEmitter {
public channel: string;
protected stream: Stream;
public abstract id: string;
constructor(stream: Stream, channel: string) {
super(); super();
this.stream = stream; this.stream = stream;
this.channel = channel; this.channel = channel;
}
@autobind
public send(id: string, typeOrPayload, payload?) {
const type = payload === undefined ? typeOrPayload.type : typeOrPayload;
const body = payload === undefined ? typeOrPayload.body : payload;
this.stream.send('ch', {
id: id,
type: type,
body: body
});
}
public abstract dispose(): void;
}
class SharedConnection extends Connection {
private pool: Pool;
public get id(): string {
return this.pool.id;
}
constructor(stream: Stream, channel: string, pool: Pool) {
super(stream, channel);
this.pool = pool;
this.pool.inc();
}
@autobind
public send(typeOrPayload, payload?) {
super.send(this.pool.id, typeOrPayload, payload);
}
@autobind
public dispose() {
this.pool.dec();
this.removeAllListeners();
this.stream.removeSharedConnection(this);
}
}
class NonSharedConnection extends Connection {
public id: string;
protected params: any;
constructor(stream: Stream, channel: string, params?: any) {
super(stream, channel);
this.params = params; this.params = params;
this.id = Math.random().toString(); this.id = Math.random().toString().substr(2, 8);
this.connect(); this.connect();
} }
@ -253,60 +291,7 @@ abstract class Connection extends EventEmitter {
@autobind @autobind
public send(typeOrPayload, payload?) { public send(typeOrPayload, payload?) {
const data = payload === undefined ? typeOrPayload : { super.send(this.id, typeOrPayload, payload);
type: typeOrPayload,
body: payload
};
this.stream.send('channel', {
id: this.id,
body: data
});
}
public abstract dispose: () => void;
}
class SharedConnection extends Connection {
private users = 0;
private disposeTimerId: any;
constructor(stream: Stream, channel: string) {
super(stream, channel);
}
@autobind
public use() {
this.users++;
// タイマー解除
if (this.disposeTimerId) {
clearTimeout(this.disposeTimerId);
this.disposeTimerId = null;
}
}
@autobind
public dispose() {
this.users--;
// そのコネクションの利用者が誰もいなくなったら
if (this.users === 0) {
// また直ぐに再利用される可能性があるので、一定時間待ち、
// 新たな利用者が現れなければコネクションを切断する
this.disposeTimerId = setTimeout(() => {
this.disposeTimerId = null;
this.removeAllListeners();
this.stream.send('disconnect', { id: this.id });
this.stream.removeSharedConnection(this);
}, 3000);
}
}
}
class NonSharedConnection extends Connection {
constructor(stream: Stream, channel: string, params?: any) {
super(stream, channel, params);
} }
@autobind @autobind

View File

@ -186,9 +186,8 @@ export default Vue.extend({
if (this.game.isStarted && !this.game.isEnded) { if (this.game.isStarted && !this.game.isEnded) {
this.pollingClock = setInterval(() => { this.pollingClock = setInterval(() => {
const crc32 = CRC32.str(this.logs.map(x => x.pos.toString()).join('')); const crc32 = CRC32.str(this.logs.map(x => x.pos.toString()).join(''));
this.connection.send({ this.connection.send('check', {
type: 'check', crc32: crc32
crc32
}); });
}, 3000); }, 3000);
} }
@ -224,9 +223,8 @@ export default Vue.extend({
sound.play(); sound.play();
} }
this.connection.send({ this.connection.send('set', {
type: 'set', pos: pos
pos
}); });
this.checkEnd(); this.checkEnd();

View File

@ -149,9 +149,9 @@ export default Vue.extend({
}, },
created() { created() {
this.connection.on('change-accepts', this.onChangeAccepts); this.connection.on('changeAccepts', this.onChangeAccepts);
this.connection.on('update-settings', this.onUpdateSettings); this.connection.on('updateSettings', this.onUpdateSettings);
this.connection.on('init-form', this.onInitForm); this.connection.on('initForm', this.onInitForm);
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
if (this.game.user1Id != this.$store.state.i.id && this.game.settings.form1) this.form = this.game.settings.form1; if (this.game.user1Id != this.$store.state.i.id && this.game.settings.form1) this.form = this.game.settings.form1;
@ -159,9 +159,9 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('change-accepts', this.onChangeAccepts); this.connection.off('changeAccepts', this.onChangeAccepts);
this.connection.off('update-settings', this.onUpdateSettings); this.connection.off('updateSettings', this.onUpdateSettings);
this.connection.off('init-form', this.onInitForm); this.connection.off('initForm', this.onInitForm);
this.connection.off('message', this.onMessage); this.connection.off('message', this.onMessage);
}, },
@ -171,15 +171,11 @@ export default Vue.extend({
}, },
accept() { accept() {
this.connection.send({ this.connection.send('accept', {});
type: 'accept'
});
}, },
cancel() { cancel() {
this.connection.send({ this.connection.send('cancelAccept', {});
type: 'cancel-accept'
});
}, },
onChangeAccepts(accepts) { onChangeAccepts(accepts) {
@ -189,8 +185,7 @@ export default Vue.extend({
}, },
updateSettings() { updateSettings() {
this.connection.send({ this.connection.send('updateSettings', {
type: 'update-settings',
settings: this.game.settings settings: this.game.settings
}); });
}, },
@ -216,8 +211,7 @@ export default Vue.extend({
}, },
onChangeForm(item) { onChangeForm(item) {
this.connection.send({ this.connection.send('updateForm', {
type: 'update-form',
id: item.id, id: item.id,
value: item.value value: item.value
}); });
@ -238,9 +232,9 @@ export default Vue.extend({
const y = Math.floor(pos / this.game.settings.map[0].length); const y = Math.floor(pos / this.game.settings.map[0].length);
const newPixel = const newPixel =
pixel == ' ' ? '-' : pixel == ' ' ? '-' :
pixel == '-' ? 'b' : pixel == '-' ? 'b' :
pixel == 'b' ? 'w' : pixel == 'b' ? 'w' :
' '; ' ';
const line = this.game.settings.map[y].split(''); const line = this.game.settings.map[y].split('');
line[x] = newPixel; line[x] = newPixel;
this.$set(this.game.settings.map, y, line.join('')); this.$set(this.game.settings.map, y, line.join(''));

View File

@ -71,8 +71,7 @@ export default Vue.extend({
this.pingClock = setInterval(() => { this.pingClock = setInterval(() => {
if (this.matching) { if (this.matching) {
this.connection.send({ this.connection.send('ping', {
type: 'ping',
id: this.matching.id id: this.matching.id
}); });
} }

View File

@ -1,5 +1,6 @@
import Vue from 'vue'; import Vue from 'vue';
import noteSkeleton from './note-skeleton.vue';
import theme from './theme.vue'; import theme from './theme.vue';
import instance from './instance.vue'; import instance from './instance.vue';
import cwButton from './cw-button.vue'; import cwButton from './cw-button.vue';
@ -44,6 +45,7 @@ import uiSelect from './ui/select.vue';
import formButton from './ui/form/button.vue'; import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue'; import formRadio from './ui/form/radio.vue';
Vue.component('mk-note-skeleton', noteSkeleton);
Vue.component('mk-theme', theme); Vue.component('mk-theme', theme);
Vue.component('mk-instance', instance); Vue.component('mk-instance', instance);
Vue.component('mk-cw-button', cwButton); Vue.component('mk-cw-button', cwButton);

View File

@ -71,7 +71,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
this.connection =((this as any).os.stream.connectToChannel('messaging', { otherparty: this.user.id }); this.connection = (this as any).os.stream.connectToChannel('messaging', { otherparty: this.user.id });
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
this.connection.on('read', this.onRead); this.connection.on('read', this.onRead);
@ -174,8 +174,7 @@ export default Vue.extend({
this.messages.push(message); this.messages.push(message);
if (message.userId != this.$store.state.i.id && !document.hidden) { if (message.userId != this.$store.state.i.id && !document.hidden) {
this.connection.send({ this.connection.send('read', {
type: 'read',
id: message.id id: message.id
}); });
} }
@ -247,8 +246,7 @@ export default Vue.extend({
if (document.hidden) return; if (document.hidden) return;
this.messages.forEach(message => { this.messages.forEach(message => {
if (message.userId !== this.$store.state.i.id && !message.isRead) { if (message.userId !== this.$store.state.i.id && !message.isRead) {
this.connection.send({ this.connection.send('read', {
type: 'read',
id: message.id id: message.id
}); });
} }
@ -356,7 +354,7 @@ export default Vue.extend({
max-width 600px max-width 600px
margin 0 auto margin 0 auto
padding 0 padding 0
//background rgba(var(--face), 0.95) background var(--messagingRoomBg)
background-clip content-box background-clip content-box
> .new-message > .new-message

View File

@ -116,16 +116,16 @@ export default Vue.component('misskey-flavored-markdown', {
case 'mention': { case 'mention': {
return (createElement as any)('a', { return (createElement as any)('a', {
attrs: { attrs: {
href: `${url}/@${getAcct(token)}`, href: `${url}/${token.canonical}`,
target: '_blank', target: '_blank',
dataIsMe: (this as any).i && getAcct((this as any).i) == getAcct(token), dataIsMe: (this as any).i && getAcct((this as any).i) == getAcct(token),
style: 'color:var(--mfmMention);' style: 'color:var(--mfmMention);'
}, },
directives: [{ directives: [{
name: 'user-preview', name: 'user-preview',
value: token.content value: token.canonical
}] }]
}, token.content); }, token.canonical);
} }
case 'hashtag': { case 'hashtag': {

View File

@ -8,6 +8,7 @@
import Vue from 'vue'; import Vue from 'vue';
import { url } from '../../../config'; import { url } from '../../../config';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard'; import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import Ok from './ok.vue';
export default Vue.extend({ export default Vue.extend({
props: ['note', 'source', 'compact'], props: ['note', 'source', 'compact'],
@ -21,12 +22,34 @@ export default Vue.extend({
icon: '%fa:link%', icon: '%fa:link%',
text: '%i18n:@copy-link%', text: '%i18n:@copy-link%',
action: this.copyLink action: this.copyLink
}, null, {
icon: '%fa:star%',
text: '%i18n:@favorite%',
action: this.favorite
}]; }];
if (this.note.uri) {
items.push({
icon: '%fa:external-link-square-alt%',
text: '%i18n:@remote%',
action: () => {
window.open(this.note.uri, '_blank');
}
});
}
items.push(null);
if (this.note.isFavorited) {
items.push({
icon: '%fa:star%',
text: '%i18n:@unfavorite%',
action: this.unfavorite
});
} else {
items.push({
icon: '%fa:star%',
text: '%i18n:@favorite%',
action: this.favorite
});
}
if (this.note.userId == this.$store.state.i.id) { if (this.note.userId == this.$store.state.i.id) {
if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) { if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) {
items.push({ items.push({
@ -44,6 +67,7 @@ export default Vue.extend({
} }
if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) { if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
items.push(null);
items.push({ items.push({
icon: '%fa:trash-alt R%', icon: '%fa:trash-alt R%',
text: '%i18n:@delete%', text: '%i18n:@delete%',
@ -51,16 +75,6 @@ export default Vue.extend({
}); });
} }
if (this.note.uri) {
items.push({
icon: '%fa:external-link-square-alt%',
text: '%i18n:@remote%',
action: () => {
window.open(this.note.uri, '_blank');
}
});
}
return items; return items;
} }
}, },
@ -78,6 +92,7 @@ export default Vue.extend({
(this as any).api('i/pin', { (this as any).api('i/pin', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
(this as any).os.new(Ok);
this.destroyDom(); this.destroyDom();
}); });
}, },
@ -103,6 +118,16 @@ export default Vue.extend({
(this as any).api('notes/favorites/create', { (this as any).api('notes/favorites/create', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
(this as any).os.new(Ok);
this.destroyDom();
});
},
unfavorite() {
(this as any).api('notes/favorites/delete', {
noteId: this.note.id
}).then(() => {
(this as any).os.new(Ok);
this.destroyDom(); this.destroyDom();
}); });
}, },

View File

@ -0,0 +1,52 @@
<template>
<div>
<vue-content-loading v-if="width" :width="width" :height="100" :primary="primary" :secondary="secondary">
<circle cx="30" cy="30" r="30" />
<rect x="75" y="13" rx="4" ry="4" :width="150 + r1" height="15" />
<rect x="75" y="39" rx="4" ry="4" :width="260 + r2" height="10" />
<rect x="75" y="59" rx="4" ry="4" :width="230 + r3" height="10" />
</vue-content-loading>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import VueContentLoading from 'vue-content-loading';
import * as tinycolor from 'tinycolor2';
export default Vue.extend({
components: {
VueContentLoading,
},
data() {
return {
width: 0,
r1: (Math.random() * 100) - 50,
r2: (Math.random() * 100) - 50,
r3: (Math.random() * 100) - 50
};
},
computed: {
text(): tinycolor.Instance {
const text = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text'));
return text;
},
primary(): string {
return '#' + this.text.clone().toHex();
},
secondary(): string {
return '#' + this.text.clone().darken(20).toHex();
}
},
mounted() {
let width = this.$el.clientWidth;
if (width < 400) width = 400;
this.width = width;
}
});
</script>

View File

@ -0,0 +1,175 @@
<template>
<div class="yvbkymdqeusiqucuuloahhiqflzinufs">
<div class="bg" ref="bg"></div>
<div class="body" ref="body">
<div class="icon">
<div class="circle left"></div>
<span class="check tip"></span>
<span class="check long"></span>
<div class="ring"></div>
<div class="fix"></div>
<div class="circle right"></div>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
export default Vue.extend({
mounted() {
this.$nextTick(() => {
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 300,
easing: 'linear'
});
anime({
targets: this.$refs.body,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
});
setTimeout(() => {
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
anime({
targets: this.$refs.body,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [0.5, 0, 1, 0.5],
complete: () => this.destroyDom()
});
}, 1250);
}
});
</script>
<style lang="stylus" scoped>
.yvbkymdqeusiqucuuloahhiqflzinufs
pointer-events none
> .bg
display block
position fixed
z-index 10000
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
> .body
position fixed
z-index 10000
top 0
right 0
left 0
bottom 0
margin auto
width 150px
height 150px
background var(--face)
border-radius 8px
opacity 0
> .icon
display flex
justify-content center
position absolute
top 0
right 0
left 0
bottom 0
width 5em
height 5em
margin auto
border .25em solid transparent
border-radius 50%
line-height 5em
cursor default
box-sizing content-box
user-select none
zoom normal
border-color #a5dc86
> .circle
position absolute
width 3.75em
height 7.5em
transform rotate(45deg)
border-radius 50%
background var(--face)
&.left
top -.4375em
left -2.0635em
transform rotate(-45deg)
transform-origin 3.75em 3.75em
border-radius 7.5em 0 0 7.5em
&.right
top -.6875em
left 1.875em
transform rotate(-45deg)
transform-origin 0 3.75em
border-radius 0 7.5em 7.5em 0
animation swal2-rotate-success-circular-line 4.25s ease-in
> .check
display block
position absolute
height .3125em
border-radius .125em
background-color #a5dc86
z-index 2
&.tip
top 2.875em
left .875em
width 1.5625em
transform rotate(45deg)
animation swal2-animate-success-line-tip .75s
&.long
top 2.375em
right .5em
width 2.9375em
transform rotate(-45deg)
animation swal2-animate-success-line-long .75s
> .fix
position absolute
top .5em
left 1.625em
width .4375em
height 5.625em
transform rotate(-45deg)
z-index 1
background var(--face)
> .ring
position absolute
top -.25em
left -.25em
width 100%
height 100%
border .25em solid rgba(165,220,134,.3)
border-radius 50%
z-index 2
box-sizing content-box
</style>

View File

@ -1,16 +1,16 @@
<template> <template>
<div class="mk-reactions-viewer"> <div class="mk-reactions-viewer">
<template v-if="reactions"> <template v-if="reactions">
<span :class="{notReacted}" @click="react('like')" v-if="reactions.like"><mk-reaction-icon reaction="like"/><span>{{ reactions.like }}</span></span> <span :class="{ reacted: note.myReaction == 'like' }" @click="react('like')" v-if="reactions.like"><mk-reaction-icon reaction="like"/><span>{{ reactions.like }}</span></span>
<span :class="{notReacted}" @click="react('love')" v-if="reactions.love"><mk-reaction-icon reaction="love"/><span>{{ reactions.love }}</span></span> <span :class="{ reacted: note.myReaction == 'love' }" @click="react('love')" v-if="reactions.love"><mk-reaction-icon reaction="love"/><span>{{ reactions.love }}</span></span>
<span :class="{notReacted}" @click="react('laugh')" v-if="reactions.laugh"><mk-reaction-icon reaction="laugh"/><span>{{ reactions.laugh }}</span></span> <span :class="{ reacted: note.myReaction == 'laugh' }" @click="react('laugh')" v-if="reactions.laugh"><mk-reaction-icon reaction="laugh"/><span>{{ reactions.laugh }}</span></span>
<span :class="{notReacted}" @click="react('hmm')" v-if="reactions.hmm"><mk-reaction-icon reaction="hmm"/><span>{{ reactions.hmm }}</span></span> <span :class="{ reacted: note.myReaction == 'hmm' }" @click="react('hmm')" v-if="reactions.hmm"><mk-reaction-icon reaction="hmm"/><span>{{ reactions.hmm }}</span></span>
<span :class="{notReacted}" @click="react('surprise')" v-if="reactions.surprise"><mk-reaction-icon reaction="surprise"/><span>{{ reactions.surprise }}</span></span> <span :class="{ reacted: note.myReaction == 'surprise' }" @click="react('surprise')" v-if="reactions.surprise"><mk-reaction-icon reaction="surprise"/><span>{{ reactions.surprise }}</span></span>
<span :class="{notReacted}" @click="react('congrats')" v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span> <span :class="{ reacted: note.myReaction == 'congrats' }" @click="react('congrats')" v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span>
<span :class="{notReacted}" @click="react('angry')" v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span> <span :class="{ reacted: note.myReaction == 'angry' }" @click="react('angry')" v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span>
<span :class="{notReacted}" @click="react('confused')" v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span> <span :class="{ reacted: note.myReaction == 'confused' }" @click="react('confused')" v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span>
<span :class="{notReacted}" @click="react('rip')" v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span> <span :class="{ reacted: note.myReaction == 'rip' }" @click="react('rip')" v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span>
<span :class="{notReacted}" @click="react('pudding')" v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span> <span :class="{ reacted: note.myReaction == 'pudding' }" @click="react('pudding')" v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span>
</template> </template>
</div> </div>
</template> </template>
@ -22,9 +22,6 @@ export default Vue.extend({
computed: { computed: {
reactions(): number { reactions(): number {
return this.note.reactionCounts; return this.note.reactionCounts;
},
notReacted(): boolean {
return this.note.myReaction == null;
} }
}, },
methods: { methods: {
@ -40,25 +37,42 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-reactions-viewer .mk-reactions-viewer
border-top dashed 1px var(--reactionViewerBorder) margin 6px 0
border-bottom dashed 1px var(--reactionViewerBorder)
margin 4px 0
&:empty &:empty
display none display none
> span > span
margin-right 8px display inline-block
height 32px
margin-right 6px
padding 0 6px
border-radius 4px
&.notReacted *
user-select none
pointer-events none
&.reacted
background var(--primary)
> span
color var(--primaryForeground)
&:not(.reacted)
cursor pointer cursor pointer
background var(--reactionViewerButtonBg)
&:hover
background var(--reactionViewerButtonHoverBg)
> .mk-reaction-icon > .mk-reaction-icon
font-size 1.4em font-size 1.4em
> span > span
margin-left 4px font-size 1.1em
font-size 1.2em line-height 32px
vertical-align middle
color var(--text) color var(--text)
</style> </style>

View File

@ -67,22 +67,30 @@
</details> </details>
<details> <details>
<summary>%fa:folder-open% %i18n:@installed-themes%</summary> <summary>%fa:folder-open% %i18n:@manage-themes%</summary>
<ui-select v-model="selectedInstalledThemeId" placeholder="%i18n:@select-theme%"> <ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%">
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <optgroup label="%i18n:@builtin-themes%">
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
<optgroup label="%i18n:@my-themes%">
<option v-for="x in installedThemes.filter(t => t.author == this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
<optgroup label="%i18n:@installed-themes%">
<option v-for="x in installedThemes.filter(t => t.author != this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</ui-select> </ui-select>
<template v-if="selectedInstalledTheme"> <template v-if="selectedTheme">
<ui-input readonly :value="selectedInstalledTheme.author"> <ui-input readonly :value="selectedTheme.author">
<span>%i18n:@author%</span> <span>%i18n:@author%</span>
</ui-input> </ui-input>
<ui-textarea v-if="selectedInstalledTheme.desc" readonly :value="selectedInstalledTheme.desc"> <ui-textarea v-if="selectedTheme.desc" readonly :value="selectedTheme.desc">
<span>%i18n:@desc%</span> <span>%i18n:@desc%</span>
</ui-textarea> </ui-textarea>
<ui-textarea readonly :value="selectedInstalledThemeCode"> <ui-textarea readonly :value="selectedThemeCode">
<span>%i18n:@theme-code%</span> <span>%i18n:@theme-code%</span>
</ui-textarea> </ui-textarea>
<ui-button @click="export_()" link :download="`${selectedInstalledTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button> <ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button>
<ui-button @click="uninstall()">%fa:trash-alt R% %i18n:@uninstall%</ui-button> <ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)">%fa:trash-alt R% %i18n:@uninstall%</ui-button>
</template> </template>
</details> </details>
</div> </div>
@ -117,8 +125,9 @@ export default Vue.extend({
data() { data() {
return { return {
builtinThemes: builtinThemes,
installThemeCode: null, installThemeCode: null,
selectedInstalledThemeId: null, selectedThemeId: null,
myThemeBase: 'light', myThemeBase: 'light',
myThemeName: '', myThemeName: '',
myThemeDesc: '', myThemeDesc: '',
@ -155,14 +164,14 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); } set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); }
}, },
selectedInstalledTheme() { selectedTheme() {
if (this.selectedInstalledThemeId == null) return null; if (this.selectedThemeId == null) return null;
return this.installedThemes.find(x => x.id == this.selectedInstalledThemeId); return this.themes.find(x => x.id == this.selectedThemeId);
}, },
selectedInstalledThemeCode() { selectedThemeCode() {
if (this.selectedInstalledTheme == null) return null; if (this.selectedTheme == null) return null;
return JSON5.stringify(this.selectedInstalledTheme, null, '\t'); return JSON5.stringify(this.selectedTheme, null, '\t');
}, },
myTheme(): any { myTheme(): any {
@ -210,7 +219,10 @@ export default Vue.extend({
try { try {
theme = JSON5.parse(code); theme = JSON5.parse(code);
} catch (e) { } catch (e) {
alert('%i18n:@invalid-theme%'); this.$swal({
type: 'error',
text: '%i18n:@invalid-theme%'
});
return; return;
} }
@ -220,12 +232,18 @@ export default Vue.extend({
} }
if (theme.id == null) { if (theme.id == null) {
alert('%i18n:@invalid-theme%'); this.$swal({
type: 'error',
text: '%i18n:@invalid-theme%'
});
return; return;
} }
if (this.$store.state.device.themes.some(t => t.id == theme.id)) { if (this.$store.state.device.themes.some(t => t.id == theme.id)) {
alert('%i18n:@already-installed%'); this.$swal({
type: 'info',
text: '%i18n:@already-installed%'
});
return; return;
} }
@ -234,16 +252,23 @@ export default Vue.extend({
key: 'themes', value: themes key: 'themes', value: themes
}); });
alert('%i18n:@installed%'.replace('{}', theme.name)); this.$swal({
type: 'success',
text: '%i18n:@installed%'.replace('{}', theme.name)
});
}, },
uninstall() { uninstall() {
const theme = this.selectedInstalledTheme; const theme = this.selectedTheme;
const themes = this.$store.state.device.themes.filter(t => t.id != theme.id); const themes = this.$store.state.device.themes.filter(t => t.id != theme.id);
this.$store.commit('device/set', { this.$store.commit('device/set', {
key: 'themes', value: themes key: 'themes', value: themes
}); });
alert('%i18n:@uninstalled%'.replace('{}', theme.name));
this.$swal({
type: 'info',
text: '%i18n:@uninstalled%'.replace('{}', theme.name)
});
}, },
import_() { import_() {
@ -251,7 +276,7 @@ export default Vue.extend({
} }
export_() { export_() {
const blob = new Blob([this.selectedInstalledThemeCode], { const blob = new Blob([this.selectedThemeCode], {
type: 'application/json5' type: 'application/json5'
}); });
this.$refs.export.$el.href = window.URL.createObjectURL(blob); this.$refs.export.$el.href = window.URL.createObjectURL(blob);
@ -275,16 +300,26 @@ export default Vue.extend({
gen() { gen() {
const theme = this.myTheme; const theme = this.myTheme;
if (theme.name == null || theme.name.trim() == '') { if (theme.name == null || theme.name.trim() == '') {
alert('%i18n:@theme-name-required%'); this.$swal({
type: 'warning',
text: '%i18n:@theme-name-required%'
});
return; return;
} }
theme.id = uuid(); theme.id = uuid();
const themes = this.$store.state.device.themes.concat(theme); const themes = this.$store.state.device.themes.concat(theme);
this.$store.commit('device/set', { this.$store.commit('device/set', {
key: 'themes', value: themes key: 'themes', value: themes
}); });
alert('%i18n:@saved%');
this.$swal({
type: 'success',
text: '%i18n:@saved%'
});
} }
} }
}); });

View File

@ -1,6 +1,6 @@
import * as getCaretCoordinates from 'textarea-caret'; import * as getCaretCoordinates from 'textarea-caret';
import MkAutocomplete from '../components/autocomplete.vue'; import MkAutocomplete from '../components/autocomplete.vue';
import renderAcct from '../../../../../misc/acct/render'; import { toASCII } from 'punycode';
export default { export default {
bind(el, binding, vn) { bind(el, binding, vn) {
@ -188,7 +188,7 @@ class Autocomplete {
const trimmedBefore = before.substring(0, before.lastIndexOf('@')); const trimmedBefore = before.substring(0, before.lastIndexOf('@'));
const after = source.substr(caret); const after = source.substr(caret);
const acct = renderAcct(value); const acct = value.host === null ? value.username : `${value.username}@${toASCII(value.host)}`;
// 挿入 // 挿入
this.text = `${trimmedBefore}@${acct} ${after}`; this.text = `${trimmedBefore}@${acct} ${after}`;

View File

@ -113,9 +113,8 @@ export default define({
this.connection.on('stats', this.onStats); this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog); this.connection.on('statsLog', this.onStatsLog);
this.connection.send({ this.connection.send('requestLog',{
type: 'requestLog', id: Math.random().toString().substr(2, 8)
id: Math.random().toString()
}); });
}, },
beforeDestroy() { beforeDestroy() {

View File

@ -44,7 +44,6 @@ export default define({
}, },
fetch() { fetch() {
fetch(`https://api.rss2json.com/v1/api.json?rss_url=${this.props.url}`, { fetch(`https://api.rss2json.com/v1/api.json?rss_url=${this.props.url}`, {
cache: 'no-cache'
}).then(res => { }).then(res => {
res.json().then(feed => { res.json().then(feed => {
this.items = feed.items; this.items = feed.items;

View File

@ -91,9 +91,8 @@ export default Vue.extend({
mounted() { mounted() {
this.connection.on('stats', this.onStats); this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog); this.connection.on('statsLog', this.onStatsLog);
this.connection.send({ this.connection.send('requestLog', {
type: 'requestLog', id: Math.random().toString().substr(2, 8)
id: Math.random().toString()
}); });
}, },
beforeDestroy() { beforeDestroy() {

View File

@ -6,13 +6,17 @@ export default (os: OS) => opts => {
const o = opts || {}; const o = opts || {};
if (o.renote) { if (o.renote) {
const vm = os.new(RenoteFormWindow, { const vm = os.new(RenoteFormWindow, {
note: o.renote note: o.renote,
animation: o.animation == null ? true : o.animation
}); });
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el); document.body.appendChild(vm.$el);
} else { } else {
const vm = os.new(PostFormWindow, { const vm = os.new(PostFormWindow, {
reply: o.reply reply: o.reply,
animation: o.animation == null ? true : o.animation
}); });
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el); document.body.appendChild(vm.$el);
} }
}; };

View File

@ -1,37 +0,0 @@
<template>
<div class="mk-ellipsis-icon">
<div></div><div></div><div></div>
</div>
</template>
<style lang="stylus" scoped>
.mk-ellipsis-icon
width 70px
margin 0 auto
text-align center
> div
display inline-block
width 18px
height 18px
background-color rgba(#000, 0.3)
border-radius 100%
animation bounce 1.4s infinite ease-in-out both
&:nth-child(1)
animation-delay 0s
&:nth-child(2)
margin 0 6px
animation-delay 0.16s
&:nth-child(3)
animation-delay 0.32s
@keyframes bounce
0%, 80%, 100%
transform scale(0)
40%
transform scale(1)
</style>

View File

@ -8,7 +8,6 @@
<router-link class="name" :to="user | userPage" v-user-preview="user.id">{{ user | userName }}</router-link> <router-link class="name" :to="user | userPage" v-user-preview="user.id">{{ user | userName }}</router-link>
<p class="username">@{{ user | acct }}</p> <p class="username">@{{ user | acct }}</p>
</div> </div>
<mk-follow-button :user="user"/>
</div> </div>
</div> </div>
<p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p> <p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>

View File

@ -38,7 +38,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="main"> <div class="main" :class="{ side: widgets.left.length == 0 || widgets.right.length == 0 }">
<template v-if="customize"> <template v-if="customize">
<x-draggable v-for="place in ['left', 'right']" <x-draggable v-for="place in ['left', 'right']"
:list="widgets[place]" :list="widgets[place]"
@ -359,12 +359,10 @@ export default Vue.extend({
box-shadow var(--shadow) box-shadow var(--shadow)
border-radius var(--round) border-radius var(--round)
@media (max-width 700px) &.side
padding 0 > .main
width calc(100% - 280px)
> .tl max-width 680px
border none
border-radius 0
> *:not(.main) > *:not(.main)
width 280px width 280px
@ -381,14 +379,24 @@ export default Vue.extend({
padding-right 16px padding-right 16px
order 3 order 3
@media (max-width 1100px) &.side
> *:not(.main) @media (max-width 1000px)
display none > *:not(.main)
display none
> .main > .main
float none width 100%
width 100% max-width 700px
max-width 700px margin 0 auto
margin 0 auto
&:not(.side)
@media (max-width 1200px)
> *:not(.main)
display none
> .main
width 100%
max-width 700px
margin 0 auto
</style> </style>

View File

@ -9,7 +9,6 @@ import subNoteContent from './sub-note-content.vue';
import window from './window.vue'; import window from './window.vue';
import noteFormWindow from './post-form-window.vue'; import noteFormWindow from './post-form-window.vue';
import renoteFormWindow from './renote-form-window.vue'; import renoteFormWindow from './renote-form-window.vue';
import ellipsisIcon from './ellipsis-icon.vue';
import mediaImage from './media-image.vue'; import mediaImage from './media-image.vue';
import mediaImageDialog from './media-image-dialog.vue'; import mediaImageDialog from './media-image-dialog.vue';
import mediaVideo from './media-video.vue'; import mediaVideo from './media-video.vue';
@ -39,7 +38,6 @@ Vue.component('mk-sub-note-content', subNoteContent);
Vue.component('mk-window', window); Vue.component('mk-window', window);
Vue.component('mk-post-form-window', noteFormWindow); Vue.component('mk-post-form-window', noteFormWindow);
Vue.component('mk-renote-form-window', renoteFormWindow); Vue.component('mk-renote-form-window', renoteFormWindow);
Vue.component('mk-ellipsis-icon', ellipsisIcon);
Vue.component('mk-media-image', mediaImage); Vue.component('mk-media-image', mediaImage);
Vue.component('mk-media-image-dialog', mediaImageDialog); Vue.component('mk-media-image-dialog', mediaImageDialog);
Vue.component('mk-media-video', mediaVideo); Vue.component('mk-media-video', mediaVideo);

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="note" tabindex="-1" v-hotkey="keymap" :title="title"> <div class="note" v-show="appearNote.deletedAt == null" :tabindex="appearNote.deletedAt == null ? '-1' : null" v-hotkey="keymap" :title="title">
<div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)"> <div class="reply-to" v-if="appearNote.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
<x-sub :note="p.reply"/> <x-sub :note="appearNote.reply"/>
</div> </div>
<div class="renote" v-if="isRenote"> <div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/> <mk-avatar class="avatar" :user="note.user"/>
@ -12,228 +12,76 @@
<mk-time :time="note.createdAt"/> <mk-time :time="note.createdAt"/>
</div> </div>
<article> <article>
<mk-avatar class="avatar" :user="p.user"/> <mk-avatar class="avatar" :user="appearNote.user"/>
<div class="main"> <div class="main">
<mk-note-header class="header" :note="p"/> <mk-note-header class="header" :note="appearNote"/>
<div class="body"> <div class="body">
<p v-if="p.cw != null" class="cw"> <p v-if="appearNote.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span> <span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
<mk-cw-button v-model="showContent"/> <mk-cw-button v-model="showContent"/>
</p> </p>
<div class="content" v-show="p.cw == null || showContent"> <div class="content" v-show="appearNote.cw == null || showContent">
<div class="text"> <div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span> <span v-if="appearNote.isHidden" style="opacity: 0.5">%i18n:@private%</span>
<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span> <a class="reply" v-if="appearNote.reply">%fa:reply%</a>
<a class="reply" v-if="p.reply">%fa:reply%</a> <misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/> <a class="rp" v-if="appearNote.renote">RP:</a>
<a class="rp" v-if="p.renote">RP:</a>
</div> </div>
<div class="files" v-if="p.files.length > 0"> <div class="files" v-if="appearNote.files.length > 0">
<mk-media-list :media-list="p.files"/> <mk-media-list :media-list="appearNote.files"/>
</div> </div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/> <mk-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
<a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a> <a class="location" v-if="appearNote.geo" :href="`https://maps.google.com/maps?q=${appearNote.geo.coordinates[1]},${appearNote.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div> <div class="renote" v-if="appearNote.renote"><mk-note-preview :note="appearNote.renote"/></div>
<div class="renote" v-if="p.renote"><mk-note-preview :note="p.renote"/></div>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
</div> </div>
</div> </div>
<footer v-if="p.deletedAt == null"> <footer>
<mk-reactions-viewer :note="p" ref="reactionsViewer"/> <mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
<button class="replyButton" @click="reply()" title="%i18n:@reply%"> <button class="replyButton" @click="reply()" title="%i18n:@reply%">
<template v-if="p.reply">%fa:reply-all%</template> <template v-if="appearNote.reply">%fa:reply-all%</template>
<template v-else>%fa:reply%</template> <template v-else>%fa:reply%</template>
<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p> <p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button> </button>
<button class="renoteButton" @click="renote()" title="%i18n:@renote%"> <button class="renoteButton" @click="renote()" title="%i18n:@renote%">
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p> %fa:retweet%<p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button> </button>
<button class="reactionButton" :class="{ reacted: p.myReaction != null }" @click="react()" ref="reactButton" title="%i18n:@add-reaction%"> <button class="reactionButton" :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton" title="%i18n:@add-reaction%">
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p> %fa:plus%<p class="count" v-if="appearNote.reactions_count > 0">{{ appearNote.reactions_count }}</p>
</button> </button>
<button @click="menu()" ref="menuButton"> <button @click="menu()" ref="menuButton">
%fa:ellipsis-h% %fa:ellipsis-h%
</button> </button>
<!-- <button title="%i18n:@detail">
<template v-if="!isDetailOpened">%fa:caret-down%</template>
<template v-if="isDetailOpened">%fa:caret-up%</template>
</button> -->
</footer> </footer>
</div> </div>
</article> </article>
<div class="detail" v-if="isDetailOpened">
<mk-note-status-graph width="462" height="130" :note="p"/>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import parse from '../../../../../mfm/parse';
import MkPostFormWindow from './post-form-window.vue'; import MkPostFormWindow from './post-form-window.vue';
import MkRenoteFormWindow from './renote-form-window.vue'; import MkRenoteFormWindow from './renote-form-window.vue';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './notes.note.sub.vue'; import XSub from './notes.note.sub.vue';
import { sum } from '../../../../../prelude/array'; import noteMixin from '../../../common/scripts/note-mixin';
import noteSubscriber from '../../../common/scripts/note-subscriber'; import noteSubscriber from '../../../common/scripts/note-subscriber';
function focus(el, fn) {
const target = fn(el);
if (target) {
if (target.hasAttribute('tabindex')) {
target.focus();
} else {
focus(target, fn);
}
}
}
export default Vue.extend({ export default Vue.extend({
components: { components: {
XSub XSub
}, },
mixins: [noteSubscriber('note')], mixins: [
noteMixin(),
noteSubscriber('note')
],
props: { props: {
note: { note: {
type: Object, type: Object,
required: true required: true
} }
},
data() {
return {
showContent: false,
isDetailOpened: false
};
},
computed: {
keymap(): any {
return {
'r|left': () => this.reply(true),
'e|a|plus': () => this.react(true),
'q|right': () => this.renote(true),
'ctrl+q|ctrl+right': this.renoteDirectly,
'up|k|shift+tab': this.focusBefore,
'down|j|tab': this.focusAfter,
'esc': this.blur,
'm|o': () => this.menu(true),
's': this.toggleShowContent,
'1': () => this.reactDirectly('like'),
'2': () => this.reactDirectly('love'),
'3': () => this.reactDirectly('laugh'),
'4': () => this.reactDirectly('hmm'),
'5': () => this.reactDirectly('surprise'),
'6': () => this.reactDirectly('congrats'),
'7': () => this.reactDirectly('angry'),
'8': () => this.reactDirectly('confused'),
'9': () => this.reactDirectly('rip'),
'0': () => this.reactDirectly('pudding'),
};
},
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.fileIds.length == 0 &&
this.note.poll == null);
},
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.p.reactionCounts
? sum(Object.values(this.p.reactionCounts))
: 0;
},
title(): string {
return new Date(this.p.createdAt).toLocaleString();
},
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
return ast
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
.map(t => t.url);
} else {
return null;
}
}
},
methods: {
reply(viaKeyboard = false) {
(this as any).os.new(MkPostFormWindow, {
reply: this.p,
animation: !viaKeyboard
}).$once('closed', this.focus);
},
renote(viaKeyboard = false) {
(this as any).os.new(MkRenoteFormWindow, {
note: this.p,
animation: !viaKeyboard
}).$once('closed', this.focus);
},
renoteDirectly() {
(this as any).api('notes/create', {
renoteId: this.p.id
});
},
react(viaKeyboard = false) {
this.blur();
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
showFocus: viaKeyboard,
animation: !viaKeyboard
}).$once('closed', this.focus);
},
reactDirectly(reaction) {
(this as any).api('notes/reactions/create', {
noteId: this.p.id,
reaction: reaction
});
},
menu(viaKeyboard = false) {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.p,
animation: !viaKeyboard
}).$once('closed', this.focus);
},
toggleShowContent() {
this.showContent = !this.showContent;
},
focus() {
this.$el.focus();
},
blur() {
this.$el.blur();
},
focusBefore() {
focus(this.$el, e => e.previousElementSibling);
},
focusAfter() {
focus(this.$el, e => e.nextElementSibling);
}
} }
}); });
</script> </script>
@ -445,10 +293,6 @@ export default Vue.extend({
&.reacted, &.reacted:hover &.reacted, &.reacted:hover
color var(--noteActionsReactionHover) color var(--noteActionsReactionHover)
> .detail
padding-top 4px
background rgba(#000, 0.0125)
</style> </style>
<style lang="stylus" module> <style lang="stylus" module>

View File

@ -9,6 +9,12 @@
<button @click="resolveInitPromise">%i18n:@retry%</button> <button @click="resolveInitPromise">%i18n:@retry%</button>
</div> </div>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
<mk-note-skeleton :key="i"/>
</template>
</div>
<!-- トランジションを有効にするとなぜかメモリリークする --> <!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes"> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes">
<template v-for="(note, i) in _notes"> <template v-for="(note, i) in _notes">
@ -226,6 +232,10 @@ export default Vue.extend({
> * > *
transition transform .3s ease, opacity .3s ease transition transform .3s ease, opacity .3s ease
> .placeholder
padding 32px
opacity 0.3
> .notes > .notes
> .date > .date
display block display block

View File

@ -1,5 +1,11 @@
<template> <template>
<div class="mk-notifications"> <div class="mk-notifications">
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
<mk-note-skeleton :key="i"/>
</template>
</div>
<div class="notifications" v-if="notifications.length != 0"> <div class="notifications" v-if="notifications.length != 0">
<!-- トランジションを有効にするとなぜかメモリリークする --> <!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div"> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div">
@ -102,7 +108,6 @@
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }} <template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
</button> </button>
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p> <p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
<p class="loading" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
</div> </div>
</template> </template>
@ -181,8 +186,7 @@ export default Vue.extend({
onNotification(notification) { onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({ (this as any).os.stream.send('readNotification', {
type: 'readNotification',
id: notification.id id: notification.id
}); });
@ -203,6 +207,10 @@ export default Vue.extend({
> * > *
transition transform .3s ease, opacity .3s ease transition transform .3s ease, opacity .3s ease
> .placeholder
padding 16px
opacity 0.3
> .notifications > .notifications
> div > div
> .notification > .notification
@ -320,13 +328,4 @@ export default Vue.extend({
text-align center text-align center
color #aaa color #aaa
> .loading
margin 0
padding 16px
text-align center
color #aaa
> [data-fa]
margin-right 4px
</style> </style>

View File

@ -65,6 +65,7 @@ import { host } from '../../../config';
import { erase, unique } from '../../../../../prelude/array'; import { erase, unique } from '../../../../../prelude/array';
import { length } from 'stringz'; import { length } from 'stringz';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import { toASCII } from 'punycode';
export default Vue.extend({ export default Vue.extend({
components: { components: {
@ -158,14 +159,14 @@ export default Vue.extend({
} }
if (this.reply && this.reply.user.host != null) { if (this.reply && this.reply.user.host != null) {
this.text = `@${this.reply.user.username}@${this.reply.user.host} `; this.text = `@${this.reply.user.username}@${toASCII(this.reply.user.host)} `;
} }
if (this.reply && this.reply.text != null) { if (this.reply && this.reply.text != null) {
const ast = parse(this.reply.text); const ast = parse(this.reply.text);
ast.filter(t => t.type == 'mention').forEach(x => { ast.filter(t => t.type == 'mention').forEach(x => {
const mention = x.host ? `@${x.username}@${x.host}` : `@${x.username}`; const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : `@${x.username}`;
// 自分は除外 // 自分は除外
if (this.$store.state.i.username == x.username && x.host == null) return; if (this.$store.state.i.username == x.username && x.host == null) return;

View File

@ -21,12 +21,13 @@
<ui-button primary @click="save">%i18n:@save%</ui-button> <ui-button primary @click="save">%i18n:@save%</ui-button>
<section> <section>
<h2>%i18n:@locked-account%</h2> <h2>%i18n:@locked-account%</h2>
<ui-switch v-model="$store.state.i.isLocked" @change="onChangeIsLocked">%i18n:@is-locked%</ui-switch> <ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch>
<ui-switch v-model="carefulBot" @change="save(false)">%i18n:@careful-bot%</ui-switch>
</section> </section>
<section> <section>
<h2>%i18n:@other%</h2> <h2>%i18n:@other%</h2>
<ui-switch v-model="$store.state.i.isBot" @change="onChangeIsBot">%i18n:@is-bot%</ui-switch> <ui-switch v-model="isBot" @change="save(false)">%i18n:@is-bot%</ui-switch>
<ui-switch v-model="$store.state.i.isCat" @change="onChangeIsCat">%i18n:@is-cat%</ui-switch> <ui-switch v-model="isCat" @change="save(false)">%i18n:@is-cat%</ui-switch>
<ui-switch v-model="alwaysMarkNsfw">%i18n:common.always-mark-nsfw%</ui-switch> <ui-switch v-model="alwaysMarkNsfw">%i18n:common.always-mark-nsfw%</ui-switch>
</section> </section>
</div> </div>
@ -42,6 +43,10 @@ export default Vue.extend({
location: null, location: null,
description: null, description: null,
birthday: null, birthday: null,
isBot: false,
isCat: false,
isLocked: false,
carefulBot: false,
}; };
}, },
computed: { computed: {
@ -55,34 +60,29 @@ export default Vue.extend({
this.location = this.$store.state.i.profile.location; this.location = this.$store.state.i.profile.location;
this.description = this.$store.state.i.description; this.description = this.$store.state.i.description;
this.birthday = this.$store.state.i.profile.birthday; this.birthday = this.$store.state.i.profile.birthday;
this.isCat = this.$store.state.i.isCat;
this.isBot = this.$store.state.i.isBot;
this.isLocked = this.$store.state.i.isLocked;
this.carefulBot = this.$store.state.i.carefulBot;
}, },
methods: { methods: {
updateAvatar() { updateAvatar() {
(this as any).apis.updateAvatar(); (this as any).apis.updateAvatar();
}, },
save() { save(notify) {
(this as any).api('i/update', { (this as any).api('i/update', {
name: this.name || null, name: this.name || null,
location: this.location || null, location: this.location || null,
description: this.description || null, description: this.description || null,
birthday: this.birthday || null birthday: this.birthday || null,
isCat: this.isCat,
isBot: this.isBot,
isLocked: this.isLocked,
carefulBot: this.carefulBot
}).then(() => { }).then(() => {
(this as any).apis.notify('%i18n:@profile-updated%'); if (notify) {
}); (this as any).apis.notify('%i18n:@profile-updated%');
}, }
onChangeIsLocked() {
(this as any).api('i/update', {
isLocked: this.$store.state.i.isLocked
});
},
onChangeIsBot() {
(this as any).api('i/update', {
isBot: this.$store.state.i.isBot
});
},
onChangeIsCat() {
(this as any).api('i/update', {
isCat: this.$store.state.i.isCat
}); });
} }
} }

View File

@ -88,6 +88,13 @@
<ui-switch v-model="disableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch> <ui-switch v-model="disableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
<ui-switch v-model="games_reversi_showBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch> <ui-switch v-model="games_reversi_showBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
<ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch> <ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
<section>
<header>%i18n:@navbar-position%</header>
<ui-radio v-model="navbar" value="top">%i18n:@navbar-position-top%</ui-radio>
<ui-radio v-model="navbar" value="left">%i18n:@navbar-position-left%</ui-radio>
<ui-radio v-model="navbar" value="right">%i18n:@navbar-position-right%</ui-radio>
</section>
</section> </section>
<section class="web" v-show="page == 'web'"> <section class="web" v-show="page == 'web'">
@ -293,6 +300,11 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'darkmode', value }); } set(value) { this.$store.commit('device/set', { key: 'darkmode', value }); }
}, },
navbar: {
get() { return this.$store.state.device.navbar; },
set(value) { this.$store.commit('device/set', { key: 'navbar', value }); }
},
enableSounds: { enableSounds: {
get() { return this.$store.state.device.enableSounds; }, get() { return this.$store.state.device.enableSounds; },
set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); } set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); }

View File

@ -1,9 +1,6 @@
<template> <template>
<div class="mk-timeline-core"> <div class="mk-timeline-core">
<mk-friends-maker v-if="src == 'home' && alone"/> <mk-friends-maker v-if="src == 'home' && alone"/>
<div class="fetching" v-if="fetching">
<mk-ellipsis-icon/>
</div>
<mk-notes ref="timeline" :more="existMore ? more : null"> <mk-notes ref="timeline" :more="existMore ? more : null">
<p :class="$style.empty" slot="empty"> <p :class="$style.empty" slot="empty">
@ -170,15 +167,10 @@ export default Vue.extend({
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-timeline-core .mk-timeline-core
> .mk-friends-maker > .mk-friends-maker
border-bottom solid 1px #eee border-bottom solid 1px #eee
> .fetching
padding 64px 0
</style> </style>
<style lang="stylus" module> <style lang="stylus" module>

View File

@ -19,7 +19,7 @@
<li @click="list"> <li @click="list">
<p>%fa:list%<span>%i18n:@lists%</span>%fa:angle-right%</p> <p>%fa:list%<span>%i18n:@lists%</span>%fa:angle-right%</p>
</li> </li>
<li @click="followRequests" v-if="$store.state.i.isLocked"> <li @click="followRequests" v-if="($store.state.i.isLocked || $store.state.i.carefulBot)">
<p>%fa:envelope R%<span>%i18n:@follow-requests%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></span>%fa:angle-right%</p> <p>%fa:envelope R%<span>%i18n:@follow-requests%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></span>%fa:angle-right%</p>
</li> </li>
</ul> </ul>
@ -157,6 +157,9 @@ export default Vue.extend({
font-family Meiryo, sans-serif font-family Meiryo, sans-serif
text-decoration none text-decoration none
@media (max-width 1100px)
display none
[data-fa] [data-fa]
margin-left 8px margin-left 8px
@ -171,6 +174,9 @@ export default Vue.extend({
border-radius 4px border-radius 4px
transition filter 100ms ease transition filter 100ms ease
@media (max-width 1100px)
margin-left 8px
> .menu > .menu
$bgcolor = var(--face) $bgcolor = var(--face)
display block display block

View File

@ -17,8 +17,6 @@ export default Vue.extend({
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.note .note
display inline-block display inline-block
padding 8px padding 8px

View File

@ -29,6 +29,9 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.search .search
@media (max-width 800px)
display none !important
> [data-fa] > [data-fa]
display block display block
position absolute position absolute
@ -58,6 +61,9 @@ export default Vue.extend({
transition color 0.5s ease, border 0.5s ease transition color 0.5s ease, border 0.5s ease
color var(--desktopHeaderSearchFg) color var(--desktopHeaderSearchFg)
@media (max-width 1000px)
width 10em
&::placeholder &::placeholder
color var(--desktopHeaderFg) color var(--desktopHeaderFg)

View File

@ -0,0 +1,368 @@
<template>
<div class="header" :class="navbar">
<div class="body">
<div class="post">
<button @click="post" title="%i18n:@post%">%fa:pencil-alt%</button>
</div>
<div class="nav" v-if="$store.getters.isSignedIn">
<div class="home" :class="{ active: $route.name == 'index' }" @click="goToTop">
<router-link to="/">%fa:home%</router-link>
</div>
<div class="deck" :class="{ active: $route.name == 'deck' }" @click="goToTop">
<router-link to="/deck">%fa:columns%</router-link>
</div>
<div class="messaging">
<a @click="messaging">%fa:comments%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template></a>
</div>
<div class="game">
<a @click="game">%fa:gamepad%<template v-if="hasGameInvitations">%fa:circle%</template></a>
</div>
</div>
<div class="nav bottom" v-if="$store.getters.isSignedIn">
<div>
<a @click="drive">%fa:cloud%</a>
</div>
<div ref="notificationsButton" :class="{ active: showNotifications }">
<a @click="notifications">%fa:R bell%</a>
</div>
<div>
<a @click="settings">%fa:cog%</a>
</div>
</div>
<div class="account">
<router-link :to="`/@${ $store.state.i.username }`">
<mk-avatar class="avatar" :user="$store.state.i"/>
</router-link>
<div class="nav menu">
<div class="signout">
<a @click="signout">%fa:power-off%</a>
</div>
<div>
<router-link to="/i/favorites">%fa:star%</router-link>
</div>
<div v-if="($store.state.i.isLocked || $store.state.i.carefulBot)">
<a @click="followRequests">%fa:envelope R%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></a>
</div>
</div>
</div>
<div class="nav dark">
<div>
<a @click="dark"><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template></a>
</div>
</div>
</div>
<transition :name="`slide-${navbar}`">
<div class="notifications" v-if="showNotifications" ref="notifications" :class="navbar">
<mk-notifications/>
</div>
</transition>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import MkUserListsWindow from './user-lists-window.vue';
import MkFollowRequestsWindow from './received-follow-requests-window.vue';
import MkSettingsWindow from './settings-window.vue';
import MkDriveWindow from './drive-window.vue';
import MkMessagingWindow from './messaging-window.vue';
import MkGameWindow from './game-window.vue';
import contains from '../../../common/scripts/contains';
export default Vue.extend({
data() {
return {
hasGameInvitations: false,
connection: null,
showNotifications: false
};
},
computed: {
hasUnreadMessagingMessage(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadMessagingMessage;
},
navbar(): string {
return this.$store.state.device.navbar;
},
},
mounted() {
if (this.$store.getters.isSignedIn) {
this.connection = (this as any).os.stream.useSharedConnection('main');
this.connection.on('reversiInvited', this.onReversiInvited);
this.connection.on('reversi_no_invites', this.onReversiNoInvites);
}
},
beforeDestroy() {
if (this.$store.getters.isSignedIn) {
this.connection.dispose();
}
},
methods: {
onReversiInvited() {
this.hasGameInvitations = true;
},
onReversiNoInvites() {
this.hasGameInvitations = false;
},
messaging() {
(this as any).os.new(MkMessagingWindow);
},
game() {
(this as any).os.new(MkGameWindow);
},
post() {
(this as any).apis.post();
},
drive() {
(this as any).os.new(MkDriveWindow);
},
list() {
const w = (this as any).os.new(MkUserListsWindow);
w.$once('choosen', list => {
this.$router.push(`i/lists/${ list.id }`);
});
},
followRequests() {
(this as any).os.new(MkFollowRequestsWindow);
},
settings() {
(this as any).os.new(MkSettingsWindow);
},
signout() {
(this as any).os.signout();
},
notifications() {
this.showNotifications ? this.closeNotifications() : this.openNotifications();
},
openNotifications() {
this.showNotifications = true;
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.addEventListener('mousedown', this.onMousedown);
});
},
closeNotifications() {
this.showNotifications = false;
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.removeEventListener('mousedown', this.onMousedown);
});
},
onMousedown(e) {
e.preventDefault();
if (
!contains(this.$refs.notifications, e.target) &&
this.$refs.notifications != e.target &&
!contains(this.$refs.notificationsButton, e.target) &&
this.$refs.notificationsButton != e.target
) {
this.closeNotifications();
}
return false;
},
dark() {
this.$store.commit('device/set', {
key: 'darkmode',
value: !this.$store.state.device.darkmode
});
},
goToTop() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}
}
});
</script>
<style lang="stylus" scoped>
.header
$width = 68px
position fixed
top 0
z-index 1000
width $width
height 100%
&.left
left 0
box-shadow var(--shadowRight)
&.right
right 0
box-shadow var(--shadowLeft)
> .body
position fixed
top 0
z-index 1
width $width
height 100%
background var(--desktopHeaderBg)
> .post
width $width
height $width
padding 12px
> button
display inline-block
margin 0
padding 0
height 100%
width 100%
font-size 1.2em
font-weight normal
text-decoration none
color var(--primaryForeground)
background var(--primary) !important
outline none
border none
border-radius 100%
transition background 0.1s ease
cursor pointer
*
pointer-events none
&:hover
background var(--primaryLighten10) !important
&:active
background var(--primaryDarken10) !important
transition background 0s ease
> .nav.bottom
position absolute
bottom 128px
left 0
> .account
position absolute
bottom 64px
left 0
width $width
height $width
padding 14px
> .menu
display none
position absolute
bottom 64px
left 0
background var(--desktopHeaderBg)
&:hover
> .menu
display block
> *:not(.menu)
display block
width 100%
height 100%
> .avatar
pointer-events none
width 100%
height 100%
> .dark
position absolute
bottom 0
left 0
width $width
height $width
> .notifications
position fixed
top 0
width 350px
height 100%
overflow auto
background var(--face)
&.left
left $width
box-shadow var(--shadowRight)
&.right
right $width
box-shadow var(--shadowLeft)
.nav
> *
> *
display block
width $width
line-height 52px
text-align center
font-size 18px
color var(--desktopHeaderFg)
&:hover
background rgba(0, 0, 0, 0.05)
color var(--desktopHeaderHoverFg)
text-decoration none
&:active
background rgba(0, 0, 0, 0.1)
&.left
.nav
> *
&.active
box-shadow -4px 0 var(--primary) inset
&.right
.nav
> *
&.active
box-shadow 4px 0 var(--primary) inset
.slide-left-enter-active,
.slide-left-leave-active {
transition: all 0.2s ease;
}
.slide-left-enter, .slide-left-leave-to {
transform: translateX(-16px);
opacity: 0;
}
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 0.2s ease;
}
.slide-right-enter, .slide-right-leave-to {
transform: translateX(16px);
opacity: 0;
}
</style>

View File

@ -1,8 +1,9 @@
<template> <template>
<div class="mk-ui" v-hotkey.global="keymap"> <div class="mk-ui" v-hotkey.global="keymap">
<div class="bg" v-if="$store.getters.isSignedIn && $store.state.i.wallpaperUrl" :style="style"></div> <div class="bg" v-if="$store.getters.isSignedIn && $store.state.i.wallpaperUrl" :style="style"></div>
<x-header class="header" v-show="!zenMode" ref="header"/> <x-header class="header" v-if="navbar == 'top'" v-show="!zenMode" ref="header"/>
<div class="content"> <x-sidebar class="sidebar" v-if="navbar != 'top'" ref="sidebar"/>
<div class="content" :class="[{ sidebar: navbar != 'top' }, navbar]">
<slot></slot> <slot></slot>
</div> </div>
<mk-stream-indicator v-if="$store.getters.isSignedIn"/> <mk-stream-indicator v-if="$store.getters.isSignedIn"/>
@ -12,10 +13,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import XHeader from './ui.header.vue'; import XHeader from './ui.header.vue';
import XSidebar from './ui.sidebar.vue';
export default Vue.extend({ export default Vue.extend({
components: { components: {
XHeader XHeader,
XSidebar
}, },
data() { data() {
@ -25,6 +28,10 @@ export default Vue.extend({
}, },
computed: { computed: {
navbar(): string {
return this.$store.state.device.navbar;
},
style(): any { style(): any {
if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {}; if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {};
return { return {
@ -45,6 +52,12 @@ export default Vue.extend({
watch: { watch: {
'$store.state.uiHeaderHeight'() { '$store.state.uiHeaderHeight'() {
this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px'; this.$el.style.paddingTop = this.$store.state.uiHeaderHeight + 'px';
},
navbar() {
if (this.navbar != 'top') {
this.$store.commit('setUiHeaderHeight', 0);
}
} }
}, },
@ -83,8 +96,10 @@ export default Vue.extend({
background-attachment fixed background-attachment fixed
opacity 0.3 opacity 0.3
> .header > .content.sidebar.left
@media (max-width 1000px) padding-left 68px
display none
> .content.sidebar.right
padding-right 68px
</style> </style>

View File

@ -26,12 +26,14 @@ export default Vue.extend({
this.init(); this.init();
}, },
beforeDestroy() { beforeDestroy() {
this.connection.close(); this.connection.dispose();
}, },
methods: { methods: {
init() { init() {
if (this.connection) this.connection.close(); if (this.connection) this.connection.dispose();
this.connection = new UserListStream((this as any).os, this.$store.state.i, this.list.id); this.connection = (this as any).os.stream.connectToChannel('userList', {
listId: this.list.id
});
this.connection.on('note', this.onNote); this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded); this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved); this.connection.on('userRemoved', this.onUserRemoved);

View File

@ -77,9 +77,8 @@ export default Vue.extend({
mounted() { mounted() {
this.connection.on('stats', this.onStats); this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog); this.connection.on('statsLog', this.onStatsLog);
this.connection.send({ this.connection.send('requestLog', {
type: 'requestLog', id: Math.random().toString().substr(2, 8),
id: Math.random().toString(),
length: 200 length: 200
}); });
}, },

View File

@ -276,13 +276,24 @@ export default Vue.extend({
min-width 330px min-width 330px
height 100% height 100%
background var(--face) background var(--face)
border-radius 6px border-radius var(--round)
//box-shadow 0 2px 16px rgba(#000, 0.1) box-shadow var(--shadow)
overflow hidden overflow hidden
&.draghover &.draghover
box-shadow 0 0 0 2px var(--primaryAlpha08) box-shadow 0 0 0 2px var(--primaryAlpha08)
&:after
content ""
display block
position absolute
z-index 1000
top 0
left 0
width 100%
height 100%
background var(--primaryAlpha02)
&.dragging &.dragging
box-shadow 0 0 0 2px var(--primaryAlpha04) box-shadow 0 0 0 2px var(--primaryAlpha04)
@ -310,7 +321,7 @@ export default Vue.extend({
> header > header
display flex display flex
z-index 1 z-index 2
line-height $header-height line-height $header-height
padding 0 16px padding 0 16px
font-size 14px font-size 14px
@ -338,6 +349,7 @@ export default Vue.extend({
> .toggleActive > .toggleActive
> .menu > .menu
padding 0
width $header-height width $header-height
line-height $header-height line-height $header-height
font-size 16px font-size 16px

View File

@ -1,5 +1,5 @@
<template> <template>
<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/> <x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -46,8 +46,10 @@ export default Vue.extend({
}, },
mounted() { mounted() {
if (this.connection) this.connection.close(); if (this.connection) this.connection.dispose();
this.connection = new UserListStream((this as any).os, this.$store.state.i, this.list.id); this.connection = (this as any).os.stream.connectToChannel('userList', {
listId: this.list.id
});
this.connection.on('note', this.onNote); this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded); this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved); this.connection.on('userRemoved', this.onUserRemoved);
@ -56,7 +58,7 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.close(); this.connection.dispose();
}, },
methods: { methods: {

View File

@ -1,7 +1,15 @@
<template> <template>
<div v-if="!mediaView" class="zyjjkidcqjnlegkqebitfviomuqmseqk" :class="{ renote: isRenote }"> <div
<div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)"> v-if="!mediaView"
<x-sub :note="p.reply"/> v-show="appearNote.deletedAt == null"
:tabindex="appearNote.deletedAt == null ? '-1' : null"
class="zyjjkidcqjnlegkqebitfviomuqmseqk"
:class="{ renote: isRenote }"
v-hotkey="keymap"
:title="title"
>
<div class="reply-to" v-if="appearNote.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
<x-sub :note="appearNote.reply"/>
</div> </div>
<div class="renote" v-if="isRenote"> <div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/> <mk-avatar class="avatar" :user="note.user"/>
@ -12,43 +20,42 @@
<mk-time :time="note.createdAt"/> <mk-time :time="note.createdAt"/>
</div> </div>
<article> <article>
<mk-avatar class="avatar" :user="p.user"/> <mk-avatar class="avatar" :user="appearNote.user"/>
<div class="main"> <div class="main">
<mk-note-header class="header" :note="p" :mini="true"/> <mk-note-header class="header" :note="appearNote" :mini="true"/>
<div class="body"> <div class="body">
<p v-if="p.cw != null" class="cw"> <p v-if="appearNote.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span> <span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
<mk-cw-button v-model="showContent"/> <mk-cw-button v-model="showContent"/>
</p> </p>
<div class="content" v-show="p.cw == null || showContent"> <div class="content" v-show="appearNote.cw == null || showContent">
<div class="text"> <div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span> <span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span> <a class="reply" v-if="appearNote.reply">%fa:reply%</a>
<a class="reply" v-if="p.reply">%fa:reply%</a> <misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i"/>
<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/> <a class="rp" v-if="appearNote.renote != null">RP:</a>
<a class="rp" v-if="p.renote != null">RP:</a>
</div> </div>
<div class="files" v-if="p.files.length > 0"> <div class="files" v-if="appearNote.files.length > 0">
<mk-media-list :media-list="p.files"/> <mk-media-list :media-list="appearNote.files"/>
</div> </div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/> <mk-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
<a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a> <a class="location" v-if="appearNote.geo" :href="`https://maps.google.com/maps?q=${appearNote.geo.coordinates[1]},${appearNote.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="renote" v-if="p.renote"> <div class="renote" v-if="appearNote.renote">
<mk-note-preview :note="p.renote" :mini="true"/> <mk-note-preview :note="appearNote.renote" :mini="true"/>
</div> </div>
<mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="false" :mini="true"/> <mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="false" :mini="true"/>
</div> </div>
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span> <span class="app" v-if="appearNote.app">via <b>{{ appearNote.app.name }}</b></span>
</div> </div>
<footer> <footer>
<mk-reactions-viewer :note="p" ref="reactionsViewer"/> <mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
<button @click="reply"> <button @click="reply()">
<template v-if="p.reply">%fa:reply-all%</template> <template v-if="appearNote.reply">%fa:reply-all%</template>
<template v-else>%fa:reply%</template> <template v-else>%fa:reply%</template>
</button> </button>
<button @click="renote" title="Renote">%fa:retweet%</button> <button @click="renote()" title="Renote">%fa:retweet%</button>
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">%fa:plus%</button> <button :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton">%fa:plus%</button>
<button class="menu" @click="menu" ref="menuButton">%fa:ellipsis-h%</button> <button class="menu" @click="menu()" ref="menuButton">%fa:ellipsis-h%</button>
</footer> </footer>
</div> </div>
</article> </article>
@ -65,11 +72,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import parse from '../../../../../../mfm/parse'; import MkPostFormWindow from '../../components/post-form-window.vue';
import MkRenoteFormWindow from '../../components/renote-form-window.vue';
import MkNoteMenu from '../../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../../common/views/components/reaction-picker.vue';
import XSub from './deck.note.sub.vue'; import XSub from './deck.note.sub.vue';
import noteMixin from '../../../../common/scripts/note-mixin';
import noteSubscriber from '../../../../common/scripts/note-subscriber'; import noteSubscriber from '../../../../common/scripts/note-subscriber';
export default Vue.extend({ export default Vue.extend({
@ -77,7 +83,10 @@ export default Vue.extend({
XSub XSub
}, },
mixins: [noteSubscriber('note')], mixins: [
noteMixin(),
noteSubscriber('note')
],
props: { props: {
note: { note: {
@ -89,66 +98,6 @@ export default Vue.extend({
required: false, required: false,
default: false default: false
} }
},
data() {
return {
showContent: false
};
},
computed: {
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.fileIds.length == 0 &&
this.note.poll == null);
},
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
return ast
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
.map(t => t.url);
} else {
return null;
}
}
},
methods: {
reply() {
(this as any).apis.post({
reply: this.p
});
},
renote() {
(this as any).apis.post({
renote: this.p
});
},
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
compact: true
});
},
menu() {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.p,
compact: true
});
}
} }
}); });
</script> </script>
@ -168,6 +117,20 @@ export default Vue.extend({
font-size 13px font-size 13px
border-bottom solid 1px var(--faceDivider) border-bottom solid 1px var(--faceDivider)
&:focus
z-index 1
&:after
content ""
pointer-events none
position absolute
top 2px
right 2px
bottom 2px
left 2px
border 2px solid var(--primaryAlpha03)
border-radius 4px
&:last-of-type &:last-of-type
border-bottom none border-bottom none

View File

@ -2,6 +2,12 @@
<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu"> <div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot> <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
<mk-note-skeleton :key="i"/>
</template>
</div>
<div v-if="!fetching && requestInitPromise != null"> <div v-if="!fetching && requestInitPromise != null">
<p>%i18n:@error%</p> <p>%i18n:@error%</p>
<button @click="resolveInitPromise">%i18n:@retry%</button> <button @click="resolveInitPromise">%i18n:@retry%</button>
@ -205,6 +211,10 @@ export default Vue.extend({
> * > *
transition transform .3s ease, opacity .3s ease transition transform .3s ease, opacity .3s ease
> .placeholder
padding 16px
opacity 0.3
> .notes > .notes
> .date > .date
display block display block

View File

@ -1,5 +1,11 @@
<template> <template>
<div class="oxynyeqmfvracxnglgulyqfgqxnxmehl"> <div class="oxynyeqmfvracxnglgulyqfgqxnxmehl">
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
<mk-note-skeleton :key="i"/>
</template>
</div>
<!-- トランジションを有効にするとなぜかメモリリークする --> <!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications"> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications">
<template v-for="(notification, i) in _notifications"> <template v-for="(notification, i) in _notifications">
@ -14,7 +20,6 @@
<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }} <template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
</button> </button>
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p> <p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
<p class="loading" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
</div> </div>
</template> </template>
@ -113,8 +118,7 @@ export default Vue.extend({
onNotification(notification) { onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({ (this as any).os.stream.send('readNotification', {
type: 'readNotification',
id: notification.id id: notification.id
}); });
@ -162,6 +166,10 @@ export default Vue.extend({
> * > *
transition transform .3s ease, opacity .3s ease transition transform .3s ease, opacity .3s ease
> .placeholder
padding 16px
opacity 0.3
> .notifications > .notifications
> .notification:not(:last-child) > .notification:not(:last-child)
@ -208,13 +216,4 @@ export default Vue.extend({
text-align center text-align center
color #aaa color #aaa
> .loading
margin 0
padding 16px
text-align center
color #aaa
> [data-fa]
margin-right 4px
</style> </style>

View File

@ -15,7 +15,7 @@
<ui-switch v-model="column.isMediaView" @change="onChangeSettings">%i18n:@is-media-view%</ui-switch> <ui-switch v-model="column.isMediaView" @change="onChangeSettings">%i18n:@is-media-view%</ui-switch>
</div> </div>
<x-list-tl v-if="column.type == 'list'" :list="column.list" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/> <x-list-tl v-if="column.type == 'list'" :list="column.list" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
<x-hashtag-tl v-if="column.type == 'hashtag'" :tag-tl="$store.state.settings.tagTimelines.find(x => x.id == column.tagTlId)" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/> <x-hashtag-tl v-else-if="column.type == 'hashtag'" :tag-tl="$store.state.settings.tagTimelines.find(x => x.id == column.tagTlId)" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
<x-tl v-else :src="column.type" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/> <x-tl v-else :src="column.type" :media-only="column.isMediaOnly" :media-view="column.isMediaView"/>
</x-column> </x-column>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/> <x-notes ref="timeline" :more="existMore ? more : null" :media-view="mediaView"/>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@ -3,9 +3,6 @@
<header :class="$style.header"> <header :class="$style.header">
<h1>{{ q }}</h1> <h1>{{ q }}</h1>
</header> </header>
<div :class="$style.loading" v-if="fetching">
<mk-ellipsis-icon/>
</div>
<p :class="$style.notAvailable" v-if="!fetching && notAvailable">%i18n:@not-available%</p> <p :class="$style.notAvailable" v-if="!fetching && notAvailable">%i18n:@not-available%</p>
<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:not-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:not-found%'.split('{}')[1] }}</p> <p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:not-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:not-found%'.split('{}')[1] }}</p>
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/> <mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
@ -119,9 +116,6 @@ export default Vue.extend({
border-radius 6px border-radius 6px
overflow hidden overflow hidden
.loading
padding 64px 0
.empty .empty
display block display block
margin 0 auto margin 0 auto

View File

@ -3,9 +3,6 @@
<header :class="$style.header"> <header :class="$style.header">
<h1>#{{ $route.params.tag }}</h1> <h1>#{{ $route.params.tag }}</h1>
</header> </header>
<div :class="$style.loading" v-if="fetching">
<mk-ellipsis-icon/>
</div>
<p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:no-posts-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:no-posts-found%'.split('{}')[1] }}</p> <p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:no-posts-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:no-posts-found%'.split('{}')[1] }}</p>
<mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/> <mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/>
</mk-ui> </mk-ui>
@ -108,9 +105,6 @@ export default Vue.extend({
border-radius 6px border-radius 6px
overflow hidden overflow hidden
.loading
padding 64px 0
.empty .empty
display block display block
margin 0 auto margin 0 auto

View File

@ -8,8 +8,6 @@
<div> <div>
<span class="username"><mk-acct :user="user" :detail="true" /></span> <span class="username"><mk-acct :user="user" :detail="true" /></span>
<span v-if="user.isBot" title="%i18n:@is-bot%">%fa:robot%</span> <span v-if="user.isBot" title="%i18n:@is-bot%">%fa:robot%</span>
<span class="location" v-if="user.host === null && user.profile.location">%fa:map-marker% {{ user.profile.location }}</span>
<span class="birthday" v-if="user.host === null && user.profile.birthday">%fa:birthday-cake% {{ user.profile.birthday.replace('-', '').replace('-', '') + '' }} ({{ age }})</span>
</div> </div>
</div> </div>
</div> </div>
@ -18,6 +16,10 @@
<div class="description"> <div class="description">
<misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/> <misskey-flavored-markdown v-if="user.description" :text="user.description" :i="$store.state.i"/>
</div> </div>
<div class="info">
<span class="location" v-if="user.host === null && user.profile.location">%fa:map-marker% {{ user.profile.location }}</span>
<span class="birthday" v-if="user.host === null && user.profile.birthday">%fa:birthday-cake% {{ user.profile.birthday.replace('-', '').replace('-', '') + '' }} ({{ age }})</span>
</div>
<div class="status"> <div class="status">
<span class="notes-count"><b>{{ user.notesCount | number }}</b>%i18n:@posts%</span> <span class="notes-count"><b>{{ user.notesCount | number }}</b>%i18n:@posts%</span>
<span class="following clickable" @click="showFollowing"><b>{{ user.followingCount | number }}</b>%i18n:@following%</span> <span class="following clickable" @click="showFollowing"><b>{{ user.followingCount | number }}</b>%i18n:@following%</span>
@ -182,6 +184,14 @@ export default Vue.extend({
padding 16px 16px 16px 154px padding 16px 16px 16px 154px
color var(--text) color var(--text)
> .info
margin-top 16px
padding-top 16px
border-top solid 1px var(--faceDivider)
> *
margin-right 16px
> .status > .status
margin-top 16px margin-top 16px
padding-top 16px padding-top 16px

View File

@ -5,9 +5,6 @@
<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%fa:comments% %i18n:@with-replies%</span> <span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%fa:comments% %i18n:@with-replies%</span>
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%fa:images% %i18n:@with-media%</span> <span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%fa:images% %i18n:@with-media%</span>
</header> </header>
<div class="loading" v-if="fetching">
<mk-ellipsis-icon/>
</div>
<mk-notes ref="timeline" :more="existMore ? more : null"> <mk-notes ref="timeline" :more="existMore ? more : null">
<p class="empty" slot="empty">%fa:R comments%%i18n:@empty%</p> <p class="empty" slot="empty">%fa:R comments%%i18n:@empty%</p>
</mk-notes> </mk-notes>
@ -152,9 +149,6 @@ export default Vue.extend({
&:hover &:hover
color var(--desktopTimelineSrcHover) color var(--desktopTimelineSrcHover)
> .loading
padding 64px 0
> .empty > .empty
display block display block
margin 0 auto margin 0 auto

View File

@ -13,7 +13,6 @@
<router-link class="name" :to="_user | userPage" v-user-preview="_user.id">{{ _user | userName }}</router-link> <router-link class="name" :to="_user | userPage" v-user-preview="_user.id">{{ _user | userName }}</router-link>
<p class="username">@{{ _user | acct }}</p> <p class="username">@{{ _user | acct }}</p>
</div> </div>
<mk-follow-button :user="_user"/>
</div> </div>
</template> </template>
<p class="empty" v-else>%i18n:@no-one%</p> <p class="empty" v-else>%i18n:@no-one%</p>

View File

@ -8,6 +8,7 @@ import VueRouter from 'vue-router';
import * as TreeView from 'vue-json-tree-view'; import * as TreeView from 'vue-json-tree-view';
import VAnimateCss from 'v-animate-css'; import VAnimateCss from 'v-animate-css';
import VModal from 'vue-js-modal'; import VModal from 'vue-js-modal';
import VueSweetalert2 from 'vue-sweetalert2';
import VueHotkey from './common/hotkey'; import VueHotkey from './common/hotkey';
import App from './app.vue'; import App from './app.vue';
@ -26,6 +27,7 @@ Vue.use(TreeView);
Vue.use(VAnimateCss); Vue.use(VAnimateCss);
Vue.use(VModal); Vue.use(VModal);
Vue.use(VueHotkey); Vue.use(VueHotkey);
Vue.use(VueSweetalert2);
// Register global directives // Register global directives
require('./common/views/directives'); require('./common/views/directives');
@ -122,11 +124,17 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
//#region shadow //#region shadow
const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)'; const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
const shadowRight = '4px 0 4px rgba(0, 0, 0, 0.1)';
const shadowLeft = '-4px 0 4px rgba(0, 0, 0, 0.1)';
if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadow', shadow); if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadow', shadow);
if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadowRight', shadowRight);
if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadowLeft', shadowLeft);
os.store.watch(s => { os.store.watch(s => {
return s.settings.useShadow; return s.settings.useShadow;
}, v => { }, v => {
document.documentElement.style.setProperty('--shadow', v ? shadow : 'none'); document.documentElement.style.setProperty('--shadow', v ? shadow : 'none');
document.documentElement.style.setProperty('--shadowRight', v ? shadowRight : 'none');
document.documentElement.style.setProperty('--shadowLeft', v ? shadowLeft : 'none');
}); });
//#endregion //#endregion

View File

@ -212,7 +212,7 @@ export default class MiOS extends EventEmitter {
const fetched = () => { const fetched = () => {
this.emit('signedin'); this.emit('signedin');
this.stream = new Stream(this); this.initStream();
// Finish init // Finish init
callback(); callback();
@ -247,12 +247,103 @@ export default class MiOS extends EventEmitter {
// Finish init // Finish init
callback(); callback();
this.stream = new Stream(this); this.initStream();
} }
}); });
} }
} }
@autobind
private initStream() {
this.stream = new Stream(this);
if (this.store.getters.isSignedIn) {
const main = this.stream.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
this.store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
this.store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
this.store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('readAllMessagingMessages', () => {
this.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
this.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
});
main.on('unreadMention', () => {
this.store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
this.store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
this.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
this.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('clientSettingUpdated', x => {
this.store.commit('settings/set', {
key: x.key,
value: x.value
});
});
main.on('homeUpdated', x => {
this.store.commit('settings/setHome', x);
});
main.on('mobileHomeUpdated', x => {
this.store.commit('settings/setMobileHome', x);
});
main.on('widgetUpdated', x => {
this.store.commit('settings/setWidget', {
id: x.id,
data: x.data
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
alert('%i18n:common.my-token-regenerated%');
this.signout();
});
}
}
/** /**
* Register service worker * Register service worker
*/ */
@ -352,10 +443,10 @@ export default class MiOS extends EventEmitter {
}; };
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
const viaStream = this.stream && this.store.state.device.apiViaStream && !forceFetch; const viaStream = this.stream && this.stream.state == 'connected' && this.store.state.device.apiViaStream && !forceFetch;
if (viaStream) { if (viaStream) {
const id = Math.random().toString(); const id = Math.random().toString().substr(2, 8);
this.stream.once(`api:${id}`, res => { this.stream.once(`api:${id}`, res => {
if (res == null || Object.keys(res).length == 0) { if (res == null || Object.keys(res).length == 0) {

View File

@ -18,6 +18,7 @@ export default (os) => (opts) => {
}).$mount(); }).$mount();
vm.$once('cancel', recover); vm.$once('cancel', recover);
vm.$once('posted', recover); vm.$once('posted', recover);
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el); document.body.appendChild(vm.$el);
(vm as any).focus(); (vm as any).focus();
}; };

View File

@ -91,8 +91,6 @@ export default Vue.extend({
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-dialog .mk-dialog
> .bg > .bg
display block display block

View File

@ -41,6 +41,8 @@
<ui-button link :href="`${file.url}?download`" :download="file.name">%fa:download% %i18n:@download%</ui-button> <ui-button link :href="`${file.url}?download`" :download="file.name">%fa:download% %i18n:@download%</ui-button>
<ui-button @click="rename">%fa:pencil-alt% %i18n:@rename%</ui-button> <ui-button @click="rename">%fa:pencil-alt% %i18n:@rename%</ui-button>
<ui-button @click="move">%fa:R folder-open% %i18n:@move%</ui-button> <ui-button @click="move">%fa:R folder-open% %i18n:@move%</ui-button>
<ui-button @click="toggleSensitive" v-if="file.isSensitive">%fa:R eye% %i18n:@unmark-as-sensitive%</ui-button>
<ui-button @click="toggleSensitive" v-else>%fa:R eye-slash% %i18n:@mark-as-sensitive%</ui-button>
<ui-button @click="del">%fa:trash-alt R% %i18n:@delete%</ui-button> <ui-button @click="del">%fa:trash-alt R% %i18n:@delete%</ui-button>
</div> </div>
</div> </div>
@ -71,25 +73,30 @@ import { gcd } from '../../../../../prelude/math';
export default Vue.extend({ export default Vue.extend({
props: ['file'], props: ['file'],
data() { data() {
return { return {
gcd, gcd,
exif: null exif: null
}; };
}, },
computed: { computed: {
browser(): any { browser(): any {
return this.$parent; return this.$parent;
}, },
kind(): string { kind(): string {
return this.file.type.split('/')[0]; return this.file.type.split('/')[0];
}, },
style(): any { style(): any {
return this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? { return this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? {
'background-color': `rgb(${ this.file.properties.avgColor.join(',') })` 'background-color': `rgb(${ this.file.properties.avgColor.join(',') })`
} : {}; } : {};
} }
}, },
methods: { methods: {
rename() { rename() {
const name = window.prompt('%i18n:@rename%', this.file.name); const name = window.prompt('%i18n:@rename%', this.file.name);
@ -101,6 +108,7 @@ export default Vue.extend({
this.browser.cf(this.file, true); this.browser.cf(this.file, true);
}); });
}, },
move() { move() {
(this as any).apis.chooseDriveFolder().then(folder => { (this as any).apis.chooseDriveFolder().then(folder => {
(this as any).api('drive/files/update', { (this as any).api('drive/files/update', {
@ -111,6 +119,7 @@ export default Vue.extend({
}); });
}); });
}, },
del() { del() {
(this as any).api('drive/files/delete', { (this as any).api('drive/files/delete', {
fileId: this.file.id fileId: this.file.id
@ -118,9 +127,20 @@ export default Vue.extend({
this.browser.cd(this.file.folderId, true); this.browser.cd(this.file.folderId, true);
}); });
}, },
toggleSensitive() {
(this as any).api('drive/files/update', {
fileId: this.file.id,
isSensitive: !this.file.isSensitive
});
this.file.isSensitive = !this.file.isSensitive;
},
showCreatedAt() { showCreatedAt() {
alert(new Date(this.file.createdAt).toLocaleString()); alert(new Date(this.file.createdAt).toLocaleString());
}, },
onImageLoaded() { onImageLoaded() {
const self = this; const self = this;
EXIF.getData(this.$refs.img, function(this: any) { EXIF.getData(this.$refs.img, function(this: any) {

View File

@ -1,7 +1,13 @@
<template> <template>
<div class="note" :class="{ renote: isRenote, smart: $store.state.device.postStyle == 'smart' }"> <div
<div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)"> class="note"
<x-sub :note="p.reply"/> v-show="appearNote.deletedAt == null"
:tabindex="appearNote.deletedAt == null ? '-1' : null"
:class="{ renote: isRenote, smart: $store.state.device.postStyle == 'smart' }"
v-hotkey="keymap"
>
<div class="reply-to" v-if="appearNote.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
<x-sub :note="appearNote.reply"/>
</div> </div>
<div class="renote" v-if="isRenote"> <div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/> <mk-avatar class="avatar" :user="note.user"/>
@ -12,47 +18,45 @@
<mk-time :time="note.createdAt"/> <mk-time :time="note.createdAt"/>
</div> </div>
<article> <article>
<mk-avatar class="avatar" :user="p.user" v-if="$store.state.device.postStyle != 'smart'"/> <mk-avatar class="avatar" :user="appearNote.user" v-if="$store.state.device.postStyle != 'smart'"/>
<div class="main"> <div class="main">
<mk-note-header class="header" :note="p" :mini="true"/> <mk-note-header class="header" :note="appearNote" :mini="true"/>
<div class="body"> <div class="body">
<p v-if="p.cw != null" class="cw"> <p v-if="appearNote.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span> <span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
<mk-cw-button v-model="showContent"/> <mk-cw-button v-model="showContent"/>
</p> </p>
<div class="content" v-show="p.cw == null || showContent"> <div class="content" v-show="appearNote.cw == null || showContent">
<div class="text"> <div class="text">
<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span> <span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span> <a class="reply" v-if="appearNote.reply">%fa:reply%</a>
<a class="reply" v-if="p.reply">%fa:reply%</a> <misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/> <a class="rp" v-if="appearNote.renote != null">RP:</a>
<a class="rp" v-if="p.renote != null">RP:</a>
</div> </div>
<div class="files" v-if="p.files.length > 0"> <div class="files" v-if="appearNote.files.length > 0">
<mk-media-list :media-list="p.files"/> <mk-media-list :media-list="appearNote.files"/>
</div> </div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/> <mk-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a> <a class="location" v-if="appearNote.geo" :href="`https://maps.google.com/maps?q=${appearNote.geo.coordinates[1]},${appearNote.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="map" v-if="p.geo" ref="map"></div> <div class="renote" v-if="appearNote.renote"><mk-note-preview :note="appearNote.renote"/></div>
<div class="renote" v-if="p.renote"><mk-note-preview :note="p.renote"/></div>
</div> </div>
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span> <span class="app" v-if="appearNote.app">via <b>{{ appearNote.app.name }}</b></span>
</div> </div>
<footer v-if="p.deletedAt == null"> <footer>
<mk-reactions-viewer :note="p" ref="reactionsViewer"/> <mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
<button @click="reply"> <button @click="reply()">
<template v-if="p.reply">%fa:reply-all%</template> <template v-if="appearNote.reply">%fa:reply-all%</template>
<template v-else>%fa:reply%</template> <template v-else>%fa:reply%</template>
<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p> <p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button> </button>
<button @click="renote" title="Renote"> <button @click="renote()" title="Renote">
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p> %fa:retweet%<p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button> </button>
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton"> <button :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton">
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p> %fa:plus%<p class="count" v-if="appearNote.reactions_count > 0">{{ appearNote.reactions_count }}</p>
</button> </button>
<button class="menu" @click="menu" ref="menuButton"> <button class="menu" @click="menu()" ref="menuButton">
%fa:ellipsis-h% %fa:ellipsis-h%
</button> </button>
</footer> </footer>
@ -63,12 +67,9 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import parse from '../../../../../mfm/parse';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './note.sub.vue'; import XSub from './note.sub.vue';
import { sum } from '../../../../../prelude/array'; import noteMixin from '../../../common/scripts/note-mixin';
import noteSubscriber from '../../../common/scripts/note-subscriber'; import noteSubscriber from '../../../common/scripts/note-subscriber';
export default Vue.extend({ export default Vue.extend({
@ -76,74 +77,17 @@ export default Vue.extend({
XSub XSub
}, },
mixins: [noteSubscriber('note')], mixins: [
noteMixin({
mobile: true
}),
noteSubscriber('note')
],
props: ['note'], props: {
note: {
data() { type: Object,
return { required: true
showContent: false
};
},
computed: {
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.fileIds.length == 0 &&
this.note.poll == null);
},
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.p.reactionCounts
? sum(Object.values(this.p.reactionCounts))
: 0;
},
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
return ast
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
.map(t => t.url);
} else {
return null;
}
}
},
methods: {
reply() {
(this as any).apis.post({
reply: this.p
});
},
renote() {
(this as any).apis.post({
renote: this.p
});
},
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
compact: true,
big: true
});
},
menu() {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.p,
compact: true
});
} }
} }
}); });
@ -154,6 +98,20 @@ export default Vue.extend({
font-size 12px font-size 12px
border-bottom solid 1px var(--faceDivider) border-bottom solid 1px var(--faceDivider)
&:focus
z-index 1
&:after
content ""
pointer-events none
position absolute
top 2px
right 2px
bottom 2px
left 2px
border 2px solid var(--primaryAlpha03)
border-radius 4px
&:last-of-type &:last-of-type
border-bottom none border-bottom none

View File

@ -4,8 +4,10 @@
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot> <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
<div class="init" v-if="fetching"> <div class="placeholder" v-if="fetching">
%fa:spinner .pulse%%i18n:common.loading% <template v-for="i in 10">
<mk-note-skeleton :key="i"/>
</template>
</div> </div>
<div v-if="!fetching && requestInitPromise != null"> <div v-if="!fetching && requestInitPromise != null">
@ -251,13 +253,12 @@ export default Vue.extend({
[data-fa] [data-fa]
margin-right 8px margin-right 8px
> .init > .placeholder
padding 64px 0 padding 16px
text-align center opacity 0.3
color #999
> [data-fa] @media (min-width 500px)
margin-right 4px padding 32px
> .empty > .empty
margin 0 auto margin 0 auto

View File

@ -1,5 +1,11 @@
<template> <template>
<div class="mk-notifications"> <div class="mk-notifications">
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
<mk-note-skeleton :key="i"/>
</template>
</div>
<!-- トランジションを有効にするとなぜかメモリリークする --> <!-- トランジションを有効にするとなぜかメモリリークする -->
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications"> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications">
<template v-for="(notification, i) in _notifications"> <template v-for="(notification, i) in _notifications">
@ -17,7 +23,6 @@
</button> </button>
<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p> <p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
</div> </div>
</template> </template>
@ -77,6 +82,8 @@ export default Vue.extend({
methods: { methods: {
fetchMoreNotifications() { fetchMoreNotifications() {
if (this.fetchingMoreNotifications) return;
this.fetchingMoreNotifications = true; this.fetchingMoreNotifications = true;
const max = 30; const max = 30;
@ -98,8 +105,7 @@ export default Vue.extend({
onNotification(notification) { onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({ (this as any).os.stream.send('readNotification', {
type: 'readNotification',
id: notification.id id: notification.id
}); });
@ -178,13 +184,11 @@ export default Vue.extend({
text-align center text-align center
color #aaa color #aaa
> .fetching > .placeholder
margin 0
padding 16px padding 16px
text-align center opacity 0.3
color #aaa
> [data-fa] @media (min-width 500px)
margin-right 4px padding 32px
</style> </style>

View File

@ -62,6 +62,7 @@ import { host } from '../../../config';
import { erase, unique } from '../../../../../prelude/array'; import { erase, unique } from '../../../../../prelude/array';
import { length } from 'stringz'; import { length } from 'stringz';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import { toASCII } from 'punycode';
export default Vue.extend({ export default Vue.extend({
components: { components: {
@ -153,14 +154,14 @@ export default Vue.extend({
} }
if (this.reply && this.reply.user.host != null) { if (this.reply && this.reply.user.host != null) {
this.text = `@${this.reply.user.username}@${this.reply.user.host} `; this.text = `@${this.reply.user.username}@${toASCII(this.reply.user.host)} `;
} }
if (this.reply && this.reply.text != null) { if (this.reply && this.reply.text != null) {
const ast = parse(this.reply.text); const ast = parse(this.reply.text);
ast.filter(t => t.type == 'mention').forEach(x => { ast.filter(t => t.type == 'mention').forEach(x => {
const mention = x.host ? `@${x.username}@${x.host}` : `@${x.username}`; const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : `@${x.username}`;
// 自分は除外 // 自分は除外
if (this.$store.state.i.username == x.username && x.host == null) return; if (this.$store.state.i.username == x.username && x.host == null) return;

View File

@ -18,7 +18,7 @@
<li><router-link to="/" :data-active="$route.name == 'index'">%fa:home%%i18n:@timeline%%fa:angle-right%</router-link></li> <li><router-link to="/" :data-active="$route.name == 'index'">%fa:home%%i18n:@timeline%%fa:angle-right%</router-link></li>
<li><router-link to="/i/notifications" :data-active="$route.name == 'notifications'">%fa:R bell%%i18n:@notifications%<template v-if="hasUnreadNotification">%fa:circle%</template>%fa:angle-right%</router-link></li> <li><router-link to="/i/notifications" :data-active="$route.name == 'notifications'">%fa:R bell%%i18n:@notifications%<template v-if="hasUnreadNotification">%fa:circle%</template>%fa:angle-right%</router-link></li>
<li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'">%fa:R comments%%i18n:@messaging%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>%fa:angle-right%</router-link></li> <li><router-link to="/i/messaging" :data-active="$route.name == 'messaging'">%fa:R comments%%i18n:@messaging%<template v-if="hasUnreadMessagingMessage">%fa:circle%</template>%fa:angle-right%</router-link></li>
<li v-if="$store.getters.isSignedIn && $store.state.i.isLocked"><router-link to="/i/received-follow-requests" :data-active="$route.name == 'received-follow-requests'">%fa:R envelope%%i18n:@follow-requests%<template v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount">%fa:circle%</template>%fa:angle-right%</router-link></li> <li v-if="$store.getters.isSignedIn && ($store.state.i.isLocked || $store.state.i.carefulBot)"><router-link to="/i/received-follow-requests" :data-active="$route.name == 'received-follow-requests'">%fa:R envelope%%i18n:@follow-requests%<template v-if="$store.getters.isSignedIn && $store.state.i.pendingReceivedFollowRequestsCount">%fa:circle%</template>%fa:angle-right%</router-link></li>
<li><router-link to="/reversi" :data-active="$route.name == 'reversi'">%fa:gamepad%%i18n:@game%<template v-if="hasGameInvitation">%fa:circle%</template>%fa:angle-right%</router-link></li> <li><router-link to="/reversi" :data-active="$route.name == 'reversi'">%fa:gamepad%%i18n:@game%<template v-if="hasGameInvitation">%fa:circle%</template>%fa:angle-right%</router-link></li>
</ul> </ul>
<ul> <ul>

View File

@ -58,8 +58,7 @@ export default Vue.extend({
methods: { methods: {
onNotification(notification) { onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({ (this as any).os.stream.send('readNotification', {
type: 'readNotification',
id: notification.id id: notification.id
}); });

View File

@ -36,13 +36,15 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
this.connection.close(); this.connection.dispose();
}, },
methods: { methods: {
init() { init() {
if (this.connection) this.connection.close(); if (this.connection) this.connection.dispose();
this.connection = new UserListStream((this as any).os, this.$store.state.i, this.list.id); this.connection = (this as any).os.stream.connectToChannel('userList', {
listId: this.list.id
});
this.connection.on('note', this.onNote); this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded); this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved); this.connection.on('userRemoved', this.onUserRemoved);

View File

@ -8,7 +8,14 @@
<x-profile/> <x-profile/>
<ui-card> <ui-card>
<div slot="title">%fa:palette% %i18n:@design%</div> <div slot="title">%fa:palette% %i18n:@theme%</div>
<section>
<mk-theme/>
</section>
</ui-card>
<ui-card>
<div slot="title">%fa:poll-h% %i18n:@design%</div>
<section> <section>
<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch> <ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
@ -23,13 +30,6 @@
<ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch> <ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
</section> </section>
<section>
<header>%i18n:@theme%</header>
<div>
<mk-theme/>
</div>
</section>
<section> <section>
<header>%i18n:@timeline%</header> <header>%i18n:@timeline%</header>
<div> <div>
@ -54,7 +54,7 @@
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title">%fa:cog% %i18n:@behavior%</div> <div slot="title">%fa:sliders-h% %i18n:@behavior%</div>
<section> <section>
<ui-switch v-model="fetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch> <ui-switch v-model="fetchOnScroll">%i18n:@fetch-on-scroll%</ui-switch>

View File

@ -58,6 +58,7 @@
<div> <div>
<ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch> <ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch>
<ui-switch v-model="carefulBot" @change="save(false)">%i18n:@careful-bot%</ui-switch>
</div> </div>
</section> </section>
</ui-card> </ui-card>
@ -80,6 +81,7 @@ export default Vue.extend({
bannerId: null, bannerId: null,
isCat: false, isCat: false,
isLocked: false, isLocked: false,
carefulBot: false,
saving: false, saving: false,
avatarUploading: false, avatarUploading: false,
bannerUploading: false bannerUploading: false
@ -103,6 +105,7 @@ export default Vue.extend({
this.bannerId = this.$store.state.i.bannerId; this.bannerId = this.$store.state.i.bannerId;
this.isCat = this.$store.state.i.isCat; this.isCat = this.$store.state.i.isCat;
this.isLocked = this.$store.state.i.isLocked; this.isLocked = this.$store.state.i.isLocked;
this.carefulBot = this.$store.state.i.carefulBot;
}, },
methods: { methods: {
@ -161,7 +164,8 @@ export default Vue.extend({
avatarId: this.avatarId, avatarId: this.avatarId,
bannerId: this.bannerId, bannerId: this.bannerId,
isCat: this.isCat, isCat: this.isCat,
isLocked: this.isLocked isLocked: this.isLocked,
carefulBot: this.carefulBot
}).then(i => { }).then(i => {
this.saving = false; this.saving = false;
this.$store.state.i.avatarId = i.avatarId; this.$store.state.i.avatarId = i.avatarId;
@ -170,7 +174,10 @@ export default Vue.extend({
this.$store.state.i.bannerUrl = i.bannerUrl; this.$store.state.i.bannerUrl = i.bannerUrl;
if (notify) { if (notify) {
alert('%i18n:@saved%'); this.$swal({
type: 'success',
text: '%i18n:@saved%'
});
} }
}); });
} }

View File

@ -56,6 +56,7 @@ const defaultDeviceSettings = {
loadRawImages: false, loadRawImages: false,
alwaysShowNsfw: false, alwaysShowNsfw: false,
postStyle: 'standard', postStyle: 'standard',
navbar: 'top',
mobileNotificationPosition: 'bottom' mobileNotificationPosition: 'bottom'
}; };

View File

@ -26,7 +26,7 @@
face: '$secondary', face: '$secondary',
faceText: '#fff', faceText: '#fff',
faceHeader: ':lighten<5<$secondary', faceHeader: ':lighten<5<$secondary',
faceHeaderText: '#e3e5e8', faceHeaderText: '$text',
faceDivider: 'rgba(0, 0, 0, 0.3)', faceDivider: 'rgba(0, 0, 0, 0.3)',
faceTextButton: '$text', faceTextButton: '$text',
faceTextButtonHover: ':lighten<10<$text', faceTextButtonHover: ':lighten<10<$text',
@ -84,7 +84,8 @@
reactionPickerButtonHoverBg: 'rgba(255, 255, 255, 0.18)', reactionPickerButtonHoverBg: 'rgba(255, 255, 255, 0.18)',
reactionViewerBorder: 'rgba(255, 255, 255, 0.1)', reactionViewerButtonBg: 'rgba(255, 255, 255, 0.1)',
reactionViewerButtonHoverBg: 'rgba(255, 255, 255, 0.2)',
pollEditorInputBg: 'rgba(0, 0, 0, 0.25)', pollEditorInputBg: 'rgba(0, 0, 0, 0.25)',

View File

@ -84,7 +84,8 @@
reactionPickerButtonHoverBg: '#eee', reactionPickerButtonHoverBg: '#eee',
reactionViewerBorder: 'rgba(0, 0, 0, 0.1)', reactionViewerButtonBg: 'rgba(0, 0, 0, 0.05)',
reactionViewerButtonHoverBg: 'rgba(0, 0, 0, 0.1)',
pollEditorInputBg: '#fff', pollEditorInputBg: '#fff',

View File

@ -62,6 +62,8 @@ export type Source = {
*/ */
ghost?: string; ghost?: string;
proxy?: string;
summalyProxy?: string; summalyProxy?: string;
accesslog?: string; accesslog?: string;
@ -93,9 +95,13 @@ export type Source = {
private_key: string; private_key: string;
}; };
google_maps_api_key: string;
clusterLimit?: number; clusterLimit?: number;
user_recommendation?: {
external: boolean;
engine: string;
timeout: number;
};
}; };
/** /**

View File

@ -1,10 +1,10 @@
import * as redis from 'redis'; import * as redis from 'redis';
import config from '../config'; import config from '../config';
export default redis.createClient( export default config.redis ? redis.createClient(
config.redis.port, config.redis.port,
config.redis.host, config.redis.host,
{ {
auth_pass: config.redis.pass auth_pass: config.redis.pass
} }
); ) : null;

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