Compare commits

..

181 Commits

Author SHA1 Message Date
syuilo 182ca5d434 10.96.0 2019-03-16 21:30:31 +09:00
syuilo facde9a75d Increase chart limit 2019-03-16 21:02:17 +09:00
syuilo 41385640b9 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-16 18:17:15 +09:00
syuilo 7bad9db32e Update interval 2019-03-16 18:16:28 +09:00
MeiMei af66f0a497 Fix #3504 (#4513) 2019-03-16 18:00:23 +09:00
MeiMei 95e1b80f41 updatePersonを試行した時点でもlastFetchedAtを更新する (#4510) 2019-03-16 09:55:19 +09:00
MeiMei 556e2eba95 Resolve #1727 (#4512) 2019-03-16 09:54:40 +09:00
syuilo efe530cb17 Update CONTRIBUTING.md 2019-03-16 02:53:35 +09:00
syuilo 34e7c99283 Increase display instances limit 2019-03-16 01:36:10 +09:00
syuilo 4157ea8bc3 Add icons 🎨 2019-03-16 01:34:21 +09:00
syuilo 550517bbf3 Resolve #4506 2019-03-16 01:20:13 +09:00
syuilo eb910cd8a1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-16 01:08:34 +09:00
syuilo 75131c4e8a Fix bug 2019-03-16 01:08:26 +09:00
MeiMei ee29ab95be Fix users/search (#4505) 2019-03-15 22:22:59 +09:00
syuilo e97951fc51 10.95.0 2019-03-15 13:54:08 +09:00
syuilo dfabdef60f Resolve #4501 2019-03-15 13:51:23 +09:00
syuilo 5a87763193 New Crowdin translations (#4481)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

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

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (English)
2019-03-15 13:48:46 +09:00
syuilo 6bb90f56fa ジョブを一覧できるように 2019-03-15 13:48:17 +09:00
syuilo c883ae1350 🎨 2019-03-15 13:14:50 +09:00
syuilo 09e25e6a02 Better queue chart 2019-03-15 13:09:19 +09:00
syuilo bf5d43054b Fix bug 2019-03-15 13:09:05 +09:00
MeiMei 63b3c65691 ファビコンが保存されないのを修正 (#4500)
* Fix ファビコン保存されない

* Fix meta
2019-03-15 12:40:10 +09:00
syuilo f193da7f67 🎨 2019-03-15 02:17:10 +09:00
syuilo 40f38c2c0a Improve queue page 2019-03-15 01:56:40 +09:00
syuilo db439ef804 🎨 2019-03-15 01:43:11 +09:00
MeiMei 56eb896a03 Accept Article object (#4499) 2019-03-15 00:23:24 +09:00
Acid Chicken (硫酸鶏) 68d43e43b6 Fix hashtag style 2019-03-15 00:03:24 +09:00
Acid Chicken (硫酸鶏) c60517e49a Follow #3676 2019-03-14 22:18:31 +09:00
Acid Chicken (硫酸鶏) 3f59d261f2 Follow #3676 2019-03-14 22:18:10 +09:00
Acid Chicken (硫酸鶏) 4068d220e5 Follow #3676 2019-03-14 22:17:26 +09:00
syuilo 18968e7208 Fix bug 2019-03-14 21:51:33 +09:00
Acid Chicken (硫酸鶏) 38656103c0 Add angle bracket covered url syntax to mfm (#4483)
* Add angle bracket covered url syntax to mfm

* Fix path

* Fix match

* Fix index
2019-03-14 21:23:15 +09:00
syuilo 0f65b1bcc5 10.94.0 2019-03-14 16:40:06 +09:00
syuilo a628821834 Improve readability 2019-03-14 16:35:07 +09:00
syuilo 6ceff60c1e Faviconを可変にするなど 2019-03-14 16:30:51 +09:00
syuilo d762a6ce58 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-14 15:19:09 +09:00
syuilo 75a8037a46 Fix #4489 2019-03-14 15:19:02 +09:00
MeiMei 1179920790 unFollowAll on suspend (#4490)
* unFollowAll on suspend

* use services

* silent
2019-03-14 15:16:07 +09:00
Acid Chicken (硫酸鶏) b323a160e3 Follow #3676 2019-03-14 01:20:25 +09:00
syuilo b157e9535e Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-13 23:27:21 +09:00
syuilo 7668475bd6 🎨 2019-03-13 23:27:11 +09:00
syuilo 8bda2a1fb7 Refactor 2019-03-13 23:04:17 +09:00
Acid Chicken (硫酸鶏) b092086b5b Add languages (#4452) 2019-03-13 14:22:55 +09:00
MeiMei 69a0d9034f Fix #4486 (#4487) 2019-03-13 11:21:16 +09:00
syuilo f5be8fd313 10.93.1 2019-03-13 09:34:14 +09:00
syuilo 7f835d7f76 🎨 2019-03-13 09:32:10 +09:00
syuilo ddbb7c5993 🎨 2019-03-13 09:26:38 +09:00
syuilo 00a3fe39e8 Update dependencies 🚀 2019-03-13 09:19:48 +09:00
syuilo 7537fb88d4 Refactor 2019-03-13 00:14:44 +09:00
syuilo a81bc71a1e Resolve #4454 2019-03-13 00:13:56 +09:00
MeiMei 0a0aa0e2db Fix #4484 (#4485)
* Fix #4484

* import order
2019-03-12 23:38:11 +09:00
syuilo c56b94ae96 Add type annotation to avoid type error 2019-03-12 23:31:18 +09:00
syuilo e90712706d Add icons 🎨 2019-03-12 23:30:44 +09:00
syuilo eb0623331f 🎨 2019-03-12 23:14:18 +09:00
MeiMei d15bd59109 Fix queue charts (#4482) 2019-03-12 21:53:36 +09:00
syuilo 60e0b19372 10.93.0 2019-03-12 17:24:42 +09:00
syuilo 922eb937ff 🎨 2019-03-12 17:20:40 +09:00
syuilo 87573284f1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-12 17:11:33 +09:00
syuilo a91c585f55 Add queue chart 2019-03-12 17:11:06 +09:00
syuilo 953ea21d5e New Crowdin translations (#4479)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)
2019-03-12 16:43:13 +09:00
syuilo ecb00968bc Fix bug 2019-03-12 16:42:56 +09:00
syuilo 50ad8adb2d Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-12 13:26:33 +09:00
syuilo 16878caf09 Update doc 2019-03-12 13:26:26 +09:00
syuilo 5bc30c5493 Add log 2019-03-12 13:12:49 +09:00
MeiMei 85d89cf4c4 embedプレイヤーを閉じれるように (#4402) 2019-03-12 13:12:34 +09:00
syuilo db693f598b シェアページを統合 2019-03-12 13:02:16 +09:00
syuilo 0494c770a1 Better share template 2019-03-12 12:59:26 +09:00
syuilo c473b62aed Follow latest Web Share Target specification 2019-03-12 12:55:43 +09:00
syuilo f19ac5320e Remove unnecessary checking 2019-03-12 12:46:01 +09:00
syuilo 612e3aafbc activeなジョブ数のカウント方法を分けた
https://github.com/syuilo/misskey/issues/4470#issuecomment-471827030
2019-03-12 12:31:01 +09:00
syuilo 0e97fec451 Fix typo 2019-03-12 10:46:25 +09:00
syuilo e8c8626ee4 New Crowdin translations (#4469)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Czech)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Korean)
2019-03-12 10:41:05 +09:00
syuilo d89e0f07f8 Update issue templates 2019-03-12 10:38:30 +09:00
syuilo e7f81a42ce Resolve #4470 2019-03-12 10:35:17 +09:00
Acid Chicken (硫酸鶏) ac614148b8 Update README.md [AUTOGEN] (#4477) 2019-03-12 09:50:42 +09:00
MeiMei 5eb02b4901 Resolve #4458 (#4476) 2019-03-12 09:50:20 +09:00
Acid Chicken (硫酸鶏) 65631525f6 Update README.md [AUTOGEN] (#4474) 2019-03-12 09:49:58 +09:00
syuilo 969435cfe9 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-12 00:34:30 +09:00
syuilo c932f7a25b Resolve #1736 2019-03-12 00:34:19 +09:00
MeiMei 42d164dc57 log failed job (#4472) 2019-03-11 22:45:07 +09:00
syuilo a7e60f80bd Refactor: Extract downloadTextFile function 2019-03-11 20:23:29 +09:00
syuilo 3dd5f313b7 Add icons 🎨 2019-03-11 20:12:44 +09:00
syuilo 883962c393 Add icon 🎨 2019-03-11 20:07:27 +09:00
syuilo 8a30ff1c76 🌎 A federated blogging platform 🚀 2019-03-11 20:03:02 +09:00
syuilo e47c354916 Refactor 2019-03-11 19:57:50 +09:00
syuilo 496f42805d リストをインポートしたときにプロキシアカウントがフォローするように修正 2019-03-11 19:51:58 +09:00
syuilo c3d34bda37 Resolve #4259 2019-03-11 19:43:58 +09:00
syuilo bb6ede2b8f 10.92.4 2019-03-11 13:59:27 +09:00
syuilo 822400a1ba New Crowdin translations (#4461)
* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Czech)
2019-03-11 13:57:21 +09:00
syuilo e3e08843f1 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-11 09:59:14 +09:00
syuilo ce0d4f77fa リストのエクスポートをできるように
#4259
2019-03-11 09:59:07 +09:00
Acid Chicken (硫酸鶏) 94fdb4e974 Update README.md [AUTOGEN] (#4467) 2019-03-11 08:13:02 +09:00
MeiMei 4d425fc8a4 Use proxy instead of weserv for url-preview images (#4466) 2019-03-11 01:03:09 +09:00
MeiMei c6cdfa2f5a Ignore 4xx references in AP (#4463)
* Ignore 4xx references

* remove unnecessary comment
2019-03-10 22:27:25 +09:00
syuilo 0fff2e4f16 Remove debug code 2019-03-10 19:20:25 +09:00
syuilo 80a2172715 Resolve #4462 2019-03-10 19:16:33 +09:00
syuilo 5a0a297634 Improve redis config 2019-03-09 23:44:54 +09:00
syuilo 948a133b7b Fix log 2019-03-09 18:15:44 +09:00
syuilo 2ee826c958 10.92.3 2019-03-09 10:21:45 +09:00
syuilo 539409faf8 Better logs 2019-03-09 10:18:59 +09:00
syuilo 606e46e4d7 Make info 2019-03-09 10:10:24 +09:00
syuilo a179cfd69a Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-09 10:09:12 +09:00
syuilo d8379253d4 Rename 2019-03-09 10:09:04 +09:00
MeiMei c3344fbd68 To retry AP deliver queue (#4457) 2019-03-09 08:57:55 +09:00
syuilo 4cebd6e84a Increase job attempts limit a little 2019-03-08 21:43:17 +09:00
syuilo 90fbf9dbb0 Add missing semicolon 2019-03-08 21:38:23 +09:00
syuilo d365b9f634 Fix bug 2019-03-08 21:30:12 +09:00
syuilo a2f06acaa4 10.92.2 2019-03-08 21:00:35 +09:00
syuilo 8c90cbcbfb Disable lifo 2019-03-08 20:58:01 +09:00
syuilo a4a47772dc New Crowdin translations (#4447)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

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

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

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

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Polish)
2019-03-08 20:55:36 +09:00
syuilo 5dde1f4602 Update README.md 2019-03-08 20:52:09 +09:00
syuilo 9dc0909eeb Add index 2019-03-08 20:36:42 +09:00
syuilo 0ed2592e41 Resolve #4453 2019-03-08 20:07:29 +09:00
syuilo 76cff98220 Fix bug 2019-03-08 19:45:01 +09:00
syuilo 60604b6f51 🎨 2019-03-08 15:34:34 +09:00
syuilo f410b7aecb 🎨 2019-03-08 15:27:06 +09:00
syuilo 1a61f2cee9 自動更新 2019-03-08 13:10:38 +09:00
syuilo 78a8293520 ジョブの数を一覧できるように 2019-03-08 13:03:38 +09:00
syuilo 03cfb4fc8d 🎨 2019-03-08 13:03:17 +09:00
syuilo 144345a359 Enable lifo job 2019-03-08 05:39:59 +09:00
syuilo fd2c01515e Increase job concurrency 2019-03-08 05:23:13 +09:00
syuilo 219570e08b Update job handlers 2019-03-08 05:22:14 +09:00
syuilo 69df556ff5 Resolve #4448 2019-03-08 04:55:55 +09:00
syuilo 5f4a52574f Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-07 23:43:56 +09:00
syuilo 5a1f6c5839 10.92.1 2019-03-07 23:43:42 +09:00
syuilo 91d0342fe8 New Crowdin translations (#4445)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)
2019-03-07 23:41:13 +09:00
syuilo 8cc236daf8 Clean jobs 2019-03-07 23:36:08 +09:00
syuilo d283ec69f7 Refactor 2019-03-07 23:27:38 +09:00
syuilo d1aea7596c Fix import 2019-03-07 23:14:01 +09:00
syuilo c934987b14 Resolve #4444 2019-03-07 23:07:21 +09:00
syuilo 00c9f4a2e5 Update issue templates 2019-03-07 21:41:39 +09:00
syuilo 6605c1d07f New Crowdin translations (#4431)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)
2019-03-07 21:19:53 +09:00
MeiMei 7325d66c52 Implement Update Question (#4435)
* Update remote votes count

* save updatedAt

* deliver Update

* use renderNote

* use id

* fix typeof
2019-03-07 21:19:32 +09:00
Acid Chicken (硫酸鶏) a485061e22 Update README.md [AUTOGEN] (#4441) 2019-03-07 20:30:41 +09:00
Acid Chicken (硫酸鶏) 1f63f50343 Create robots.txt (#4439)
* Create robots.txt

* Update index.ts
2019-03-07 20:11:04 +09:00
syuilo cd3170dabd Update PULL_REQUEST_TEMPLATE.md 2019-03-07 20:06:36 +09:00
syuilo 841cedc5f8 Update issue templates 2019-03-07 19:56:02 +09:00
dependabot[bot] 7f4882734d Update @types/speakeasy requirement from 2.0.3 to 2.0.4 (#4369)
Updates the requirements on [@types/speakeasy](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/speakeasy) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/speakeasy)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:41:08 +09:00
dependabot[bot] e7d647d412 Update eslint requirement from 5.12.0 to 5.15.0 (#4394)
Updates the requirements on [eslint](https://github.com/eslint/eslint) to permit the latest version.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v5.12.0...v5.15.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:40:55 +09:00
dependabot[bot] 913d14a58a Update @types/node requirement from 10.12.24 to 11.10.4 (#4396)
Updates the requirements on [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:40:41 +09:00
dependabot[bot] 909272ec3d Update @types/koa-router requirement from 7.0.39 to 7.0.40 (#4418)
Updates the requirements on [@types/koa-router](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/koa-router) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/koa-router)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:40:22 +09:00
dependabot[bot] 7af40ffbbe Update webpack-cli requirement from 3.2.1 to 3.2.3 (#4422)
Updates the requirements on [webpack-cli](https://github.com/webpack/webpack-cli) to permit the latest version.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v.3.2.1...V.3.2.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:40:09 +09:00
dependabot[bot] 9df79a3ec9 Update file-type requirement from 10.7.1 to 10.9.0 (#4423)
Updates the requirements on [file-type](https://github.com/sindresorhus/file-type) to permit the latest version.
- [Release notes](https://github.com/sindresorhus/file-type/releases)
- [Commits](https://github.com/sindresorhus/file-type/compare/v10.7.1...v10.9.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:39:52 +09:00
dependabot[bot] 4f2eee06aa Update bootstrap-vue requirement from 2.0.0-rc.11 to 2.0.0-rc.13 (#4353)
Updates the requirements on [bootstrap-vue](https://github.com/bootstrap-vue/bootstrap-vue) to permit the latest version.
- [Release notes](https://github.com/bootstrap-vue/bootstrap-vue/releases)
- [Changelog](https://github.com/bootstrap-vue/bootstrap-vue/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/bootstrap-vue/bootstrap-vue/compare/v2.0.0-rc.11...v2.0.0-rc.13)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-07 19:39:39 +09:00
syuilo 1b9cf76008 Update issue templates 2019-03-07 18:16:49 +09:00
syuilo d035a43ed6 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-07 16:50:41 +09:00
syuilo 95ee9a6e09 Update issue templates 2019-03-07 16:50:05 +09:00
Acid Chicken (硫酸鶏) 02a63cdcb3 Update index.js 2019-03-07 16:43:16 +09:00
syuilo f02125dd47 Resolve #4437 2019-03-07 13:03:46 +09:00
syuilo c11e813146 Fix 2019-03-07 09:35:23 +09:00
syuilo a365849048 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-07 09:31:07 +09:00
syuilo a493c9f769 🎨 2019-03-07 09:30:44 +09:00
Acid Chicken (硫酸鶏) a13f522b2a Update README.md [AUTOGEN] (#4436) 2019-03-07 09:18:57 +09:00
syuilo 1ed70b2e2c 10.92.0 2019-03-06 23:37:53 +09:00
syuilo 86d5a599b7 New Crowdin translations (#4414)
* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Czech)
2019-03-06 23:30:39 +09:00
Acid Chicken (硫酸鶏) c226fc8d63 Make building locales better (#4427) 2019-03-06 23:28:50 +09:00
syuilo bbf4e1c413 Add fallback for broken db 2019-03-06 23:23:50 +09:00
syuilo a24a20a83d Clean up 2019-03-06 23:19:15 +09:00
Acid Chicken (硫酸鶏) 725600da8f Enhance poll (#4409)
* Start working

* WIP: Enhance poll

* Fix bug

* Use `name` in voting note
refs: https://github.com/syuilo/misskey/issues/4407#issuecomment-469057296

* Fix style

* Refactor
Co-authored-by: MeiMei <30769358+mei23@users.noreply.github.com>

* WIP: Update poll editor

* Fix bug

* Fix bug
refs: https://github.com/syuilo/misskey/pull/4409#discussion_r

* Fix typo

* Better design

* Beautify poll editor

* Fix UI

* Fix bug
refs: https://github.com/syuilo/misskey/pull/4409#discussion_r262217524

* Add debug logging

* Fix bug

* Log deliver

* fix vote

* Update ap/show
refs: https://github.com/syuilo/misskey/pull/4409#issuecomment-469652386

* Update poll view

* Maybe done

* Add tests

* Fix path

* Fix test

* Fix test

* Fix test

* Fix expired check on AP

* Update note.ts

* Squashed commit of the following:

commit d9a4beabf851893b8992a0f4568265eb9d4f0b8e
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 05:16:14 2019 +0900

    tune

commit 83ff421a6e978243f80ba9ec820189bc897e6e3b
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 05:01:14 2019 +0900

    fallback

commit 0b566af973b115ade9e75ea4b8094ee2b329dabc
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 04:40:12 2019 +0900

    Note

commit cc0296dd6127580ac584c40398db3f762a311f8b
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 04:33:58 2019 +0900

    createで送る

* Squashed commit of the following:

commit ae696b1ed12568b27c27367ac5a77035c97c9a1f
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 06:11:17 2019 +0900

    fix

commit b735e354e7a9e64534c4f17d04ecbc65fb735c21
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 06:08:33 2019 +0900

    messge

commit d9a4beabf851893b8992a0f4568265eb9d4f0b8e
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 05:16:14 2019 +0900

    tune

commit 83ff421a6e978243f80ba9ec820189bc897e6e3b
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 05:01:14 2019 +0900

    fallback

commit 0b566af973b115ade9e75ea4b8094ee2b329dabc
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 04:40:12 2019 +0900

    Note

commit cc0296dd6127580ac584c40398db3f762a311f8b
Author: mei23 <m@m544.net>
Date:   Wed Mar 6 04:33:58 2019 +0900

    createで送る

* Fix typo

* Update vote.ts

* Update vote.ts

* Update poll-editor.vue

* Update tslint.json

* Fix layout

* Add note

* Fix bug

* Rename text key

* 投票するときに投稿として扱わないように (#4425)

* wip

* 形式をMastodonと合わせた

* Bye something

* Use - instead of ~

* Redundancy

* Yes!

* Refactor

* Use moment instead of Date

* Fix indent

* Refactor

if (votes.length)
は必要なさそう

* Clean up

* Bye Date

* Clean

* Fix timer is not displayed

* Fix リモートから無期限pollにvoteできない

* Fix vote actor
2019-03-06 22:55:47 +09:00
syuilo f74a32ed9b [Client] Fix bug 2019-03-06 20:17:59 +09:00
Acid Chicken (硫酸鶏) e08e72dd10 Use meta url instead of constant url 2019-03-06 12:31:58 +09:00
Acid Chicken (硫酸鶏) ce02e1e528 Bye on-build-webpack (#4404)
* Update webpack.config.ts

* Update package.json
2019-03-06 09:26:22 +09:00
syuilo 0b27d8a717 Fix #4421 2019-03-06 09:24:16 +09:00
syuilo 2782e7d26f [MFM] Improve hashtag parsing 2019-03-05 22:18:29 +09:00
syuilo 2c83a05e80 [Client] Better transition 2019-03-05 20:45:19 +09:00
syuilo 467f68502a Refactor 2019-03-05 20:32:39 +09:00
syuilo d95b0dee6b Fix #4416 2019-03-05 08:27:50 +09:00
syuilo a1f3323fa5 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-05 08:24:26 +09:00
syuilo 494796a7f0 Fix #4415 2019-03-05 08:24:19 +09:00
MeiMei 94f2c20d35 Fix #4413 (#4417) 2019-03-05 07:56:23 +09:00
Acid Chicken (硫酸鶏) c1deb9438d Update README.md [AUTOGEN] (#4411) 2019-03-04 19:27:53 +09:00
syuilo ea86527c66 Add indexes 2019-03-04 19:26:05 +09:00
syuilo d1a18fe266 Resolve #3220 2019-03-04 14:32:36 +09:00
syuilo 737064da82 Better log 2019-03-04 14:02:42 +09:00
syuilo 606cc85ff5 10.91.2 2019-03-04 09:25:28 +09:00
syuilo dcfc8f1b30 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-03-04 09:23:49 +09:00
syuilo ebe4b84f14 Fix bug 2019-03-04 09:23:45 +09:00
syuilo 699d4897db New Crowdin translations (#4403)
* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)
2019-03-04 09:17:46 +09:00
syuilo fcdfd8d323 Use vue-json-pretty 2019-03-04 09:16:24 +09:00
MeiMei db8625c31a Revert "Improve log view" (#4406)
This reverts commit e8ef8f0004.
2019-03-04 08:55:35 +09:00
syuilo b65f265c55 Update logs 2019-03-03 10:57:17 +09:00
166 changed files with 5206 additions and 12184 deletions
+7 -7
View File
@@ -1,30 +1,30 @@
---
name: Bug Report
name: 🐛 Bug Report
about: Create a report to help us improve
title: ''
labels: bug
labels: ⚠️bug?
assignees: ''
---
# Summary
## Summary
<!-- Tell us what the bug is -->
# Expected Behavior
## Expected Behavior
<!--- Tell us what should happen -->
# Actual Behavior
## Actual Behavior
<!--- Tell us what happens instead of the expected behavior -->
# Steps to Reproduce
## Steps to Reproduce
1.
2.
3.
# Environment
## Environment
<!-- Tell us where on the platform it happens -->
@@ -1,31 +0,0 @@
---
name: Client-side Bug Report
about: Create a report to help us improve
title: ''
labels: bug, client-side
assignees: ''
---
# Summary
<!-- Tell us what the bug is -->
# Expected Behavior
<!--- Tell us what should happen -->
# Actual Behavior
<!--- Tell us what happens instead of the expected behavior -->
# Steps to Reproduce
1.
2.
3.
# Environment
<!-- Tell us where on the platform it happens -->
<!-- e.g. desktop or mobile version, your browser, your OS -->
@@ -1,12 +1,12 @@
---
name: Feature Request
name: Feature Request
about: Suggest an idea for this project
title: ''
labels: feature
labels: ✨Feature
assignees: ''
---
# Summary
## Summary
<!-- Tell us what the suggestion is -->
@@ -1,31 +0,0 @@
---
name: Server-side Bug Report
about: Create a report to help us improve
title: ''
labels: bug, server-side
assignees: ''
---
# Summary
<!-- Tell us what the bug is -->
# Expected Behavior
<!--- Tell us what should happen -->
# Actual Behavior
<!--- Tell us what happens instead of the expected behavior -->
# Steps to Reproduce
1.
2.
3.
# Environment
<!-- Tell us where on the platform it happens -->
<!-- e.g. your Node.js version, your OS -->
@@ -1,12 +0,0 @@
---
name: Client-side Feature Request
about: Suggest an idea for this project
title: ''
labels: client-side, feature
assignees: ''
---
# Summary
<!-- Tell us what the suggestion is -->
@@ -1,12 +0,0 @@
---
name: Server-side Feature Request
about: Suggest an idea for this project
title: ''
labels: feature, server-side
assignees: ''
---
# Summary
<!-- Tell us what the suggestion is -->
+1 -1
View File
@@ -1,4 +1,4 @@
# Summary
## Summary
<!--
-
+89
View File
@@ -1,6 +1,95 @@
ChangeLog
=========
If you encounter any problems with updating, please try the following:
1. `npm run clean` or `npm run cleanall`
2. Retry update (Don't forget `npm i`)
10.96.0
----------
* 連合ユーザーの投稿に対してActivityPubオブジェクトを要求されたら元のインスタンスにリダイレクトするように
* updatePersonを試行した時点でもlastFetchedAtを更新するように
* 管理画面でリモートインスタンスの登録日時を表示
* ユーザーサジェストが機能しなくなっていた問題を修正
* 最近使ったハッシュタグ表示が機能していない問題を修正
* バグ修正
* デザインの調整
10.95.0
----------
* ジョブを一覧できるように
* MFMでURLを明示する構文の追加
* Articleタイプのアクティビティを受け入れるように
* 凍結されたユーザーをサジェストしないように
* ファビコンが保存されないのを修正
* キューのジョブクリアの動作を修正
* デザインの調整
10.94.0
----------
* Faviconを設定できるように
* アカウントを凍結したときすべてのフォローを解除するように
* シェアページが機能していない問題を修正
* インスタンスブロックをしていてもRenote等すると取得されてしまう問題を修正
* デザインの調整
10.93.1
----------
* データのエクスポートとインポートの動作を修正
* デザインの調整
10.93.0
----------
* フォローリストをインポートできるように
* embedプレイヤーを閉じれるように
* リストをインポートしたときにプロキシアカウントがフォローするように修正
* Web Share Targetの動作を修正
* おすすめアンケートのチョイスを修正
* デザインの調整
10.92.4
----------
* リストのエクスポートをできるように
* ジョブキューウィジェットを追加
* URLプレビューのサムネイルが表示されないことがある問題を修正
10.92.3
----------
* 管理画面の各種ジョブ数がおかしい問題を修正
* ジョブキューの動作を調整
10.92.2
----------
* 管理画面で各種ジョブ数を一覧できるように
* ジョブキューの動作を修正
* notes/children が遅い問題を修正
10.92.1
----------
* アンケートの結果をリモートと同期するように
* ジョブキューを有効に
* 投稿の返信一覧に引用Renoteも含めるように
* robots.txt追加
* デザインの調整
10.92.0
----------
* Mastodonのアンケートに対応
* 複数回答できるアンケートを作成できるように
* アンケートに期限を設定できるように
* 絵文字ピッカーを改良
* ハッシュタグの判定を改善
* デッキのタグTLで別のタグをクリックしてもTLが変わらない問題を修正
* ユーザーサジェストで表示名が変わらない問題を修正
* UIのバグ修正
* デザインの調整
* など
10.91.2
----------
* 10.91.1 で追加した依存関係にXSS脆弱性があったので他のパッケージに差し替え
* 初期アクセスでテーマが正しく設定されない問題を修正
10.91.1
----------
* ログビューを強化
+3
View File
@@ -46,6 +46,9 @@ Convert な(na) to にゃ(nya)
Revert Nyaize
## Code style
### Use semicolon
To avoid ASI Hazard
### Don't use `export default`
Bad:
``` ts
+8 -4
View File
@@ -1,4 +1,4 @@
<img src="https://github.com/syuilo/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/>
<a href="https://ai.misskey.xyz/"><img src="https://github.com/syuilo/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/></a>
[![Misskey](/assets/title.png)](https://misskey.xyz/)
================================================================
@@ -103,7 +103,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/1?token-time=2145916800&token-hash=WeuDzzz24cRXJogyIkU-mxARqkdyms-rcZKbO-GpGjw%3D" alt="weep" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/3?token-time=2145916800&token-hash=c8HeVqLtmdgH-gSBJg8i10gmOcwllM87MDHeznl3el0%3D" alt="Melilot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4?token-time=2145916800&token-hash=vZdDTTF-ahiKBjjgppS2ev4rkD8H7TTKkXXoxsucs6Y%3D" alt="Melilot" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/3?token-time=2145916800&token-hash=LtV2lRi3L2jOWMLwccr9qWYfPrFlzIo2jYZHKzHEb6k%3D" alt="Xeltica" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=1FlxS9MEgmNGH_RHUVHbO5hIXB5I1z0lvA33CTvYvjA%3D" alt="gutfuckllc" width="100"></td>
@@ -124,6 +124,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1?token-time=2145916800&token-hash=95p8VdGX45E8BitZR_eOcDlqCjumjzNLBPQJrJdeCpI%3D" alt="takimura" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4?token-time=2145916800&token-hash=SbdZeN5SmsuT9stD6v0jN1z0hftg0FmRiCTxysU0Ihw%3D" alt="Damillora" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/935a10339daa4ede8e555903a0707060/1?token-time=2145916800&token-hash=3CrpqH-XtKs_NoIlSsTyVs8wCzP1WFCsG2xwps1IJq0%3D" alt="Atsuko Tominaga" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
@@ -133,10 +134,12 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/2?token-time=2145916800&token-hash=zcwFxb2zopzWwksKVU1YpfAEjsl4yKT02aQ6yiAFRiQ%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3?token-time=2145916800&token-hash=-iJszBqgYBhsM5qMdA1knf9wvprhEfESzKfR2oh7mIA%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=5T8XcaAf9Zyzfg3QubR06s_kJZkArVEM2dwObrBVAU4%3D" alt="Hiratake" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1?token-time=2145916800&token-hash=D6QK3fPyqiYKJfOzc-QqaSSairUrWdjld-ewp2waj6s%3D" alt="Hekovic" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=Ksk_2l3gjPDbnzMUOCSW1E-hdPJsNs2tSR4_RAakRK8%3D" alt="dansup" width="100"></td>
<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" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1?token-time=2145916800&token-hash=xhR1n6NAAyEb-IUXLD6_dshkFa3mefU5ZZuk1L8qKTs%3D" alt="Nokotaro Takeda" width="100"></td>
@@ -144,13 +147,14 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr><tr>
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Fri, 01 Mar 2019 23:59:07 UTC
**Last updated:** Tue, 12 Mar 2019 00:50:06 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright
+4
View File
@@ -0,0 +1,4 @@
user-agent: *
allow: /
# todo: sitemap
+1 -1
View File
@@ -118,7 +118,7 @@ CentOSで1024以下のポートを使用してMisskeyを使用する場合は`Ex
4. `NODE_ENV=production npm run build`
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
なにか問題が発生した場合は、`npm run clean`すると直る場合があります。
なにか問題が発生した場合は、`npm run clean`または`npm run cleanall`すると直る場合があります。
----------------------------------------------------------------
-8
View File
@@ -5,7 +5,6 @@
import * as gulp from 'gulp';
import * as gutil from 'gulp-util';
import * as ts from 'gulp-typescript';
const yaml = require('gulp-yaml');
const sourcemaps = require('gulp-sourcemaps');
import tslint from 'gulp-tslint';
const cssnano = require('gulp-cssnano');
@@ -126,12 +125,6 @@ gulp.task('copy:client', () =>
.pipe(gulp.dest('./built/client/assets/'))
);
gulp.task('locales', () =>
gulp.src('./locales/*.yml')
.pipe(yaml({ schema: 'DEFAULT_SAFE_SCHEMA' }))
.pipe(gulp.dest('./built/client/assets/locales/'))
);
gulp.task('doc', () =>
gulp.src('./src/docs/**/*.styl')
.pipe(stylus())
@@ -149,7 +142,6 @@ gulp.task('build', gulp.parallel(
'build:ts',
'build:copy',
'build:client',
'locales',
'doc'
));
-1624
View File
File diff suppressed because it is too large Load Diff
+1192
View File
File diff suppressed because it is too large Load Diff
+117 -1061
View File
File diff suppressed because it is too large Load Diff
+48 -27
View File
@@ -1,7 +1,6 @@
---
meta:
lang: "English"
divider: ""
common:
misskey: "A ⭐ of the fediverse"
about-title: "A ⭐ of the fediverse."
@@ -25,8 +24,8 @@ common:
application-authorization: "Application authorizations"
close: "Close"
do-not-copy-paste: "Please do not enter or paste the code here. Account may be compromised."
load-more: "Load more"
enter-password: "Please enter the Password"
load-more: "Read more"
enter-password: "Enter your password"
2fa: "Two-factor authentication"
customize-home: "Customize home layout"
featured-notes: "Featured notes"
@@ -115,7 +114,7 @@ common:
a: "What are you doing?"
b: "What's happening?"
c: "Whats on your mind?"
d: "Would you post any words?"
d: "What do you want to say?"
e: "Write here"
f: "Waiting for your writing."
settings: "Settings"
@@ -170,9 +169,9 @@ common:
deck-column-align-flexible: "Flexible"
deck-column-width: "Deck column width"
deck-column-width-narrow: "Narrow"
deck-column-width-narrower: "Somewhat narrow"
deck-column-width-narrower: "Narrower"
deck-column-width-normal: "Regular"
deck-column-width-wider: "Somewhat wide"
deck-column-width-wider: "Slightly wide"
deck-column-width-wide: "Wide"
use-shadow: "Use shadows in the UI"
rounded-corners: "Round the corners of the UI"
@@ -224,8 +223,8 @@ common:
search: "Search"
delete: "Delete"
loading: "Loading"
ok: "It's OK"
cancel: "Quit"
ok: "Confirm"
cancel: "Exit"
update-available-title: "Update available"
update-available: "A new version of Misskey is now available({newer}, the current version is {current}). Reload the page to apply updates."
my-token-regenerated: "Your token has been regenerated, so you will be signed out."
@@ -286,7 +285,7 @@ auth/views/form.vue:
account-read: "View account information."
account-write: "Modify account information."
note-write: "Post."
like-write: "React to posts."
like-write: "Express yourself about this post."
following-write: "Follow and unfollow."
drive-read: "Read your drive."
drive-write: "Upload/delete files in your drive."
@@ -305,7 +304,7 @@ auth/views/index.vue:
error: "Session does not exist."
sign-in: "Please sign in."
common/views/pages/explore.vue:
verified-users: "Verified accounts"
verified-users: "Official accounts"
popular-users: "Popular users"
recently-updated-users: "Recently active users"
recently-registered-users: "Users who joined recently"
@@ -315,6 +314,7 @@ common/views/pages/explore.vue:
users-info: "Currently, {users} users are registered here"
common/views/components/url-preview.vue:
enable-player: "Enable playback"
disable-player: "Close the player"
common/views/components/user-list.vue:
no-users: "There are no users."
common/views/components/games/reversi/reversi.vue:
@@ -490,16 +490,35 @@ common/views/components/user-menu.vue:
common/views/components/poll.vue:
vote-to: "Vote for '{}'"
vote-count: "{} votes"
total-users: "{} users voted"
total-votes: "{} votes in total"
vote: "Vote"
show-result: "Show results"
voted: "Voted"
closed: "Ended"
remaining-days: "{d} days, {h} hours remain"
remaining-hours: "{h} hours, and {m} minutes remain"
remaining-minutes: "{m} minutes, and {s} seconds remaining"
remaining-seconds: "{s} seconds remaining"
common/views/components/poll-editor.vue:
no-only-one-choice: "At least two choices are required"
choice-n: "Choice {}"
remove: "Delete the choice"
add: "+ Add a choice"
destroy: "Discard the poll"
multiple: "More than one answer is allowed"
expiration: "Valid until"
infinite: "Indefinitely"
at: "Date and time pick"
after: "Progression specifics"
no-more: "You cannot add any more"
deadline-date: "Finish date"
deadline-time: "Time duration"
interval: "Duration"
unit: "Unit"
second: "Seconds"
minute: "Minutes"
hour: "Hours"
day: "S"
common/views/components/reaction-picker.vue:
choose-reaction: "Send a reaction"
common/views/components/emoji-picker.vue:
@@ -521,7 +540,7 @@ common/views/components/signin.vue:
signin-with-twitter: "Log in with Twitter"
signin-with-github: "Sign in with GitHub"
signin-with-discord: "Sign in with Discord"
login-failed: "Log in failed. Make sure you have entered your correct username and password."
login-failed: "Logging in has failed. Make sure you have entered the correct username and password."
common/views/components/signup.vue:
invitation-code: "Invitation code"
invitation-info: "If you do not have an invitation code, please contact an <a href=\"{}\">administrator</a>."
@@ -629,12 +648,16 @@ common/views/components/profile-editor.vue:
email-verified: "Your email has been verified."
email-not-verified: "Email address is not confirmed. Please check your inbox."
export: "Export"
import: "Import"
export-and-import: "Export and Import"
export-targets:
all-notes: "All posted Notes"
following-list: "List of followers"
mute-list: "List of muted accounts"
blocking-list: "List of blocked accounts"
user-lists: "Lists"
export-requested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive."
import-requested: "You have initiated an import. This may take quite some time."
enter-password: "Please enter your password"
danger-zone: "Cautious options"
delete-account: "Remove the account"
@@ -1034,7 +1057,7 @@ admin/views/dashboard.vue:
this-instance: "This instance"
federated: "Federated"
admin/views/queue.vue:
operation: "Action(s)"
title: "Queue"
remove-all-jobs: "Clear all queued jobs"
admin/views/abuse.vue:
title: "Abuse"
@@ -1197,7 +1220,7 @@ admin/views/users.vue:
updatedAtAsc: "Last Updated (Ascending)"
updatedAtDesc: "Last Updated (Descending)"
state:
title: "Status"
title: "Sort"
all: "All"
admin: "Administrator"
moderator: "Moderator"
@@ -1258,7 +1281,7 @@ admin/views/federation.vue:
users: "Users"
following: "Following"
followers: "Followers"
status: "Status"
status: "Statuses"
latest-request-sent-at: "Time of last request sent"
latest-request-received-at: "Last request received at"
remove-all-following: "Withold all followers"
@@ -1274,19 +1297,19 @@ admin/views/federation.vue:
caughtAtDesc: "Date of discovery (Descending)"
lastCommunicatedAtAsc: "The date and time of the older interactions"
lastCommunicatedAtDesc: "The date and time of the newer interactions"
notesAsc: "Order by least Notes posted"
notesDesc: "Order by most Notes posted"
notesAsc: "Least Notes posted"
notesDesc: "Most Notes posted"
usersAsc: "Less followers"
usersDesc: "More followers"
followingAsc: "Least followed"
followingDesc: "Has more followers"
followersAsc: "Sort by having less followers"
followersDesc: "Sort by the larger number of followers"
followingDesc: "Most followed"
followersAsc: "Having less followers"
followersDesc: "The largest number of followers"
driveUsageAsc: "Least storage used"
driveUsageDesc: "Most storage used"
driveFilesAsc: "By the smallest number of files stored on Drive"
driveFilesDesc: "By the largest number of files stored on Drive"
state: "Status"
driveFilesAsc: "Least files stored on Drive"
driveFilesDesc: "The largest number of files stored on Drive"
state: "Sort"
states:
all: "All"
blocked: "Blocked"
@@ -1329,8 +1352,6 @@ desktop/views/pages/selectdrive.vue:
desktop/views/pages/search.vue:
not-available: "Search feature is turned off in the settings for this instance."
not-found: "No posts were found for '{q}'"
desktop/views/pages/share.vue:
share-with: "Share on {name}"
desktop/views/pages/tag.vue:
no-posts-found: "No posts contains \"{q}\" found."
desktop/views/pages/user-list.users.vue:
@@ -1354,7 +1375,7 @@ desktop/views/pages/user/user.header.vue:
following: "Following"
followers: "Followers"
is-bot: "This account is a Bot"
no-description: "The user has not written their profile introduction"
no-description: "This user has not written their profile introduction"
years-old: "{age} years old"
year: "/"
month: "/"
@@ -1366,7 +1387,7 @@ desktop/views/pages/user/user.timeline.vue:
with-media: "Media"
my-posts: "My posts"
desktop/views/widgets/messaging.vue:
title: "Message"
title: "Messaging"
desktop/views/widgets/notifications.vue:
title: "Notifications"
desktop/views/widgets/polls.vue:
+181 -858
View File
File diff suppressed because it is too large Load Diff
+15 -140
View File
@@ -1,7 +1,6 @@
---
meta:
lang: "Français"
divider: ""
common:
misskey: "Une ⭐ du fédiverse"
about-title: "Une ⭐ du fédiverse."
@@ -30,15 +29,9 @@ common:
2fa: "Authentification à deux facteurs"
customize-home: "Personnaliser la disposition de votre accueil"
featured-notes: "Les notes mises en avant"
dark-mode: "ダークモード"
signin: "ログイン"
signup: "新規登録"
signout: "ログアウト"
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
got-it: "Jai compris !"
customization-tips:
title: "Conseils de personnalisation"
paragraph: "<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p><p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p><p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p><p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>"
gotit: "Compris !"
notification:
file-uploaded: "Le fichier a été téléversé !"
@@ -69,7 +62,7 @@ common:
explore: "Découvrir"
following: "Suit"
followers: "Abonné·e·s"
favorites: "お気に入り"
favorites: "Mettre cette note en favoris"
empty-timeline-info:
follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil."
explore: "Trouver des utilisateurs"
@@ -118,114 +111,17 @@ common:
d: "Désirez-vous publier quelques mots ?"
e: "Écrivez ici"
f: "En attente de vos écrits"
settings: "設定"
_settings:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
tags: "ハッシュタグ"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "ログイン履歴"
password: "パスワード"
other: "その他"
appearance: "デザイン"
behavior: "動作"
fetch-on-scroll: "スクロールで自動読み込み"
fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。"
note-visibility: "投稿の公開範囲"
default-note-visibility: "デフォルトの公開範囲"
remember-note-visibility: "投稿の公開範囲を記憶する"
web-search-engine: "ウェブ検索エンジン"
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
keep-cw: "CW保持"
keep-cw-desc: "投稿にリプライする際、リプライ元の投稿にCWが設定されていたとき、デフォルトで同じCWを設定するようにします。"
i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示"
use-avatar-reversi-stones: "リバーシの石にアバターを使う"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
show-full-acct: "ユーザー名のホストを省略しない"
show-via: "viaを表示する"
reduce-motion: "UIの動きを減らす"
this-setting-is-this-device-only: "このデバイスのみ"
use-os-default-emojis: "OS標準の絵文字を使用"
line-width: "線の太さ"
line-width-thin: "細い"
line-width-normal: "普通"
line-width-thick: "太い"
font-size: "文字の大きさ"
font-size-x-small: "小さい"
font-size-small: "少し小さい"
font-size-medium: "普通"
font-size-large: "少し大きい"
font-size-x-large: "大きい"
deck-column-align: "デッキのカラムの配置"
deck-column-align-center: "中央"
deck-column-align-left: "左"
deck-column-align-flexible: "フレキシブル"
deck-column-width: "デッキのカラムの幅"
deck-column-width-narrow: "狭"
deck-column-width-narrower: "やや狭"
deck-column-width-normal: "普通"
deck-column-width-wider: "やや広"
deck-column-width-wide: "広"
use-shadow: "UIに影を使用"
rounded-corners: "UIの角を丸める"
circle-icons: "円形のアイコンを使用"
contrasted-acct: "ユーザー名にコントラストを付ける"
wallpaper: "壁紙"
choose-wallpaper: "壁紙を選択"
delete-wallpaper: "壁紙を削除"
post-form-on-timeline: "タイムライン上部に投稿フォームを表示する"
show-clock-on-header: "右上に時計を表示する"
show-reply-target: "リプライ先を表示する"
timeline: "タイムライン"
show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する"
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
remain-deleted-note: "削除された投稿を表示し続ける"
sound: "サウンド"
enable-sounds: "サウンドを有効にする"
enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。"
volume: "ボリューム"
test: "テスト"
update: "Misskey Update"
version: "バージョン:"
latest-version: "最新のバージョン:"
update-checking: "アップデートを確認中"
do-update: "アップデートを確認"
update-settings: "詳細設定"
no-updates: "利用可能な更新はありません"
no-updates-desc: "お使いのMisskeyは最新です。"
update-available: "新しいバージョンが利用可能です"
update-available-desc: "ページを再度読み込みすると更新が適用されます。"
advanced-settings: "高度な設定"
debug-mode: "デバッグモードを有効にする"
debug-mode-desc: "この設定はブラウザに記憶されます。"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
i-am-under-limited-internet: "私は通信を制限されている"
post-style: "投稿の表示スタイル"
post-style-standard: "標準"
post-style-smart: "スマート"
notification-position: "通知の表示"
notification-position-bottom: "下"
notification-position-top: "上"
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
load-raw-images: "添付された画像を高画質で表示する"
load-remote-media: "リモートサーバーのメディアを表示する"
profile: "Votre profil"
notification: "Notifications"
tags: "Hashtags"
blocking: "En cours blocage"
password: "Mot de passe"
other: "Avancé"
timeline: "Fil dactualité"
search: "Recherche"
delete: "Supprimer"
loading: "Chargement en cours …"
ok: "おk"
cancel: "やめる"
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."
my-token-regenerated: "Votre jeton vient d’être généré, vous allez maintenant être déconnecté."
@@ -313,8 +209,6 @@ common/views/pages/explore.vue:
federated: "Du Fédiverse"
explore: "Explorer {host}"
users-info: "Actuellement, {users} utilisateurs se sont inscrit ici"
common/views/components/url-preview.vue:
enable-player: "プレイヤーを開く"
common/views/components/user-list.vue:
no-users: "Il n'y a aucun utilisateur"
common/views/components/games/reversi/reversi.vue:
@@ -349,7 +243,6 @@ common/views/components/games/reversi/reversi.room.vue:
black-or-white: "Noirs/Blancs"
black-is: "{} Noirs"
rules: "Règles"
is-llotheo: "石の少ない方が勝ち(ロセオ)"
looped-map: "Carte en boucle"
can-put-everywhere: "Peut poser partout"
settings-of-the-bot: "Configuration du bot"
@@ -490,7 +383,6 @@ common/views/components/user-menu.vue:
common/views/components/poll.vue:
vote-to: "Voter pour '{}'"
vote-count: "{} votes"
total-users: "{} utilisateur·rice·s ont voté"
vote: "Vote"
show-result: "Montrer les résultats"
voted: "Voté"
@@ -500,6 +392,7 @@ common/views/components/poll-editor.vue:
remove: "Supprimer ce choix"
add: "+ Ajouter un choix"
destroy: "Annuler ce sondage"
day: "D"
common/views/components/reaction-picker.vue:
choose-reaction: "Choisissez votre réaction"
common/views/components/emoji-picker.vue:
@@ -629,11 +522,13 @@ common/views/components/profile-editor.vue:
email-verified: "Ladresse du courrier électronique a été vérifiée."
email-not-verified: "Adresse de courriel nest pas confirmée. Veuillez vérifier votre boite de réception."
export: "Exporter"
import: "Importer"
export-targets:
all-notes: "Toutes les notes publiées"
following-list: "Liste des abonnements"
mute-list: "Liste des comptes mis en sourdine"
blocking-list: "Liste des comptes bloqués"
user-lists: "Listes"
export-requested: "Vous avez demandé une exportation. Cela peut prendre un certain temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive."
enter-password: "Veuillez saisir votre mot de passe"
danger-zone: "Zone de danger"
@@ -695,7 +590,6 @@ common/views/widgets/tips.vue:
tips-line19: "Plusieurs fenêtres peuvent être détachées en dehors du navigateur."
tips-line20: "Pourcentage sur le widget calendrier qui indique le pourcentage de temps passé"
tips-line21: "Vous pouvez aussi utiliser l'API pour développer des Bots."
tips-line23: "藍かわいいよ藍"
tips-line24: "Misskey est fonctionnel depuis 2014"
tips-line25: "Vous pouvez recevoir les notifications de Misskey dans un navigateur web compatible"
common/views/pages/not-found.vue:
@@ -1023,7 +917,6 @@ admin/views/index.vue:
hashtags: "Hashtags"
abuse: "Abus"
queue: "File dattente"
logs: "ログ"
back-to-misskey: "Retour vers Misskey"
admin/views/dashboard.vue:
dashboard: "Tableau de bord"
@@ -1034,7 +927,6 @@ admin/views/dashboard.vue:
this-instance: "Cette instance"
federated: "Fédérées"
admin/views/queue.vue:
operation: "Action(s)"
remove-all-jobs: "Enlever toutes les tâches en attente"
admin/views/abuse.vue:
title: "Abus"
@@ -1056,7 +948,6 @@ admin/views/instance.vue:
maintainer-email: "Contact administratif"
drive-config: "Paramètres du lecteur"
cache-remote-files: "Mettre en cache des fichiers distants"
cache-remote-files-desc: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。そのためサーバーのストレージを節約できますが、プライバシー設定で直リンクを無効にしているユーザーにはファイルが見えなくなったり、サムネイルが生成されないので通信量が増加します。通常はこの設定をオンにしておくことをおすすめします。"
local-drive-capacity-mb: "Volume du lecteur par utilisateur"
remote-drive-capacity-mb: "Volume du lecteur par utilisateur distant"
mb: "en mégaoctets"
@@ -1081,7 +972,6 @@ admin/views/instance.vue:
discord-integration-client-id: "ID client"
discord-integration-client-secret: "Secret client"
proxy-account-config: "Compte proxy"
proxy-account-info: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。"
proxy-account-username: "Nom dutilisateur du compte proxy"
proxy-account-username-desc: "Spécifiez le nom dutilisateur du compte utilisé comme proxy."
proxy-account-warn: "Avant dentamer cette action, vous devez au préalable avoir créé un compte avec ce nom dutilisateur."
@@ -1261,38 +1151,29 @@ admin/views/federation.vue:
status: "Statuts"
latest-request-sent-at: "Dernière requête envoyée"
latest-request-received-at: "Dernière requête reçue"
remove-all-following: "フォローを全解除"
remove-all-following-info: "Se désabonner de tous les comptes de {host}. Exécutez cette commande si l'instance n'existe plus."
block: "Bloquer"
marked-as-closed: "Marquées comme fermées"
lookup: "Recherche"
instances: "Instances"
instance-not-registered: "そのインスタンスは登録されていません"
sort: "Trier par"
sorts:
caughtAtAsc: "Date dinscription (Ascendant)"
caughtAtDesc: "Date dinscription (Descendant)"
lastCommunicatedAtAsc: "La date et l'heure des interactions plus anciennes"
lastCommunicatedAtDesc: "La date et l'heure des nouvelles interactions"
notesAsc: "投稿が少ない順"
notesDesc: "Description des notes"
usersAsc: "ユーザーが少ない順"
usersDesc: "ユーザーが多い順"
followingAsc: "Les moins suivies"
followingDesc: "Ayant le plus d'abonné·e·s"
followersAsc: "Ayant le moins d'abonné·e·s"
followersDesc: "Ayant le plus d'abonné·e·s"
driveUsageAsc: "Moins d'espace de stockage utilisé"
driveUsageDesc: "ドライブ使用量が多い順"
driveFilesAsc: "ドライブのファイル数が少ない順"
driveFilesDesc: "ドライブのファイル数が多い順"
state: "État"
states:
all: "Tout"
blocked: "Bloquées"
not-responding: "Sans réponse"
marked-as-closed: "Marquée comme fermée"
result-is-truncated: "上位{n}件を表示しています。"
charts: "Graphs"
chart-srcs:
requests: "Requêtes"
@@ -1301,10 +1182,8 @@ admin/views/federation.vue:
notes: "Augmentation/diminution du nombre des notes"
notes-total: "Nombre total des notes"
ff: "Augmentation des abonné·e·s"
ff-total: "フォロー/フォロワーの積算"
drive-usage: "Augmentation et diminution de la capacité stockage"
drive-usage-total: "Utilisation totale du stockage"
drive-files: "ドライブファイル数の増減"
drive-files-total: "Nombre total des fichiers sur le Drive"
chart-spans:
hour: "Par heure"
@@ -1329,8 +1208,6 @@ desktop/views/pages/selectdrive.vue:
desktop/views/pages/search.vue:
not-available: "La fonction de recherche est désactivée dans les paramètres de linstance."
not-found: "Aucune publication trouvée pour « {q} »."
desktop/views/pages/share.vue:
share-with: "Partager avec {name}"
desktop/views/pages/tag.vue:
no-posts-found: "Aucune publication contenant « {q} » na été trouvée."
desktop/views/pages/user-list.users.vue:
@@ -1397,7 +1274,6 @@ mobile/views/components/drive.vue:
prompt: "Que veux-tu faire ? (Entrez un nombre): <1 → Télécharger le fichier | 2 → Télécharger le fichier avec l'URL | 3 → Créer le dossier | 4 → Modifier le nom du dossier | 5 → Déplacer ce dossier | 6 → Supprimer ce dossier >"
deletion-alert: "Désolé ! La suppression dun dossier nest pas encore implémentée."
folder-name: "Nom du dossier"
here-is-root: "現在いる場所はルートで、フォルダではありません。"
url-prompt: "URL du fichier que vous souhaitez téléverser"
uploading: "Envoi demandé. Le téléversement pourrait prendre un certain temps avant de s'achever."
mobile/views/components/drive-file-chooser.vue:
@@ -1492,10 +1368,9 @@ mobile/views/pages/home.vue:
mobile/views/pages/tag.vue:
no-posts-found: "Aucune publication ayant pour hashtag « {q} » na été trouvée."
mobile/views/pages/widgets.vue:
dashboard: "ダッシュボード"
widgets-hints: "ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。"
add-widget: "追加"
customization-tips: "カスタマイズのヒント"
dashboard: "Tableau de bord"
add-widget: "Ajouter"
customization-tips: "Conseils de personnalisation"
mobile/views/pages/widgets/activity.vue:
activity: "Activité"
mobile/views/pages/share.vue:
@@ -1548,7 +1423,7 @@ deck:
direct: "Messages directs"
notifications: "Notifications"
list: "Listes"
select-list: "リストを選択してください"
select-list: "Sélectionnez une liste"
swap-left: "Déplacer à gauche"
swap-right: "Déplacer à droite"
swap-up: "Déplacer vers le haut"
+44 -17
View File
@@ -5,22 +5,49 @@
const fs = require('fs');
const yaml = require('js-yaml');
const langs = ['de-DE', 'en-US', 'fr-FR', 'ja-JP', 'ja-KS', 'pl-PL', 'es-ES', 'nl-NL', 'zh-CN', 'ko-KR'];
const merge = (...args) => args.reduce((a, c) => ({
...a,
...c,
...Object.entries(a)
.filter(([k]) => c && typeof c[k] === 'object')
.reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {})
}), {});
const loadLocale = lang => yaml.safeLoad(fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8'));
const locales = langs
.map(lang => [lang, loadLocale(lang)])
.map(([lang, locale], _, locales) => {
switch (lang) {
case 'ja-JP': return [lang, locale];
case 'en-US': return [lang, { ...locales['ja-JP'], ...locale }];
default: return [lang, {
...(lang.startsWith('ja-') ? {} : locales['en-US']),
...locales['ja-JP'],
...locale
}];
const languages = [
'cs-CZ',
'de-DE',
'en-US',
'es-ES',
'fr-FR',
'ja-JP',
'ja-KS',
'ko-KR',
'nl-NL',
'pl-PL',
'zh-CN',
'zh-TW',
];
const primaries = {
'en': 'US',
'ja': 'JP',
'zh': 'CN',
};
const locales = languages.reduce((a, c) => (a[c] = yaml.safeLoad(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8')) || {}, a), {});
module.exports = Object.entries(locales)
.reduce((a, [k ,v]) => (a[k] = (() => {
const [lang] = k.split('-');
switch (k) {
case 'ja-JP': return v;
case 'ja-KS':
case 'en-US': return merge(locales['ja-JP'], v);
default: return merge(
locales['ja-JP'],
locales['en-US'],
locales[`${lang}-${primaries[lang]}`] || {},
v
);
}
})
.map(([lang, locale]) => ({ [lang]: loadLocale(lang) }));
module.exports = locales.reduce((a, b) => ({ ...a, ...b }));
})(), a), {});
+1 -1620
View File
File diff suppressed because it is too large Load Diff
+29 -7
View File
@@ -334,6 +334,7 @@ common/views/pages/explore.vue:
common/views/components/url-preview.vue:
enable-player: "プレイヤーを開く"
disable-player: "プレイヤーを閉じる"
common/views/components/user-list.vue:
no-users: "ユーザーがいません"
@@ -527,10 +528,15 @@ common/views/components/user-menu.vue:
common/views/components/poll.vue:
vote-to: "「{}」に投票する"
vote-count: "{}票"
total-users: "{}人が投票"
total-votes: "{}票"
vote: "投票する"
show-result: "結果を見る"
voted: "投票済み"
closed: "終了済み"
remaining-days: "終了まであと{d}日{h}時間"
remaining-hours: "終了まであと{h}時間{m}分"
remaining-minutes: "終了まであと{m}分{s}秒"
remaining-seconds: "終了まであと{s}秒"
common/views/components/poll-editor.vue:
no-only-one-choice: "アンケートには、選択肢が最低2つ必要です"
@@ -538,6 +544,20 @@ common/views/components/poll-editor.vue:
remove: "この選択肢を削除"
add: "+選択肢を追加"
destroy: "アンケートを破棄"
multiple: "複数回答可"
expiration: "期限"
infinite: "無期限"
at: "日時指定"
after: "経過指定"
no-more: "これ以上追加できません"
deadline-date: "期日"
deadline-time: "時間"
interval: "期間"
unit: "単位"
second: "秒"
minute: "分"
hour: "時間"
day: "日"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
@@ -682,12 +702,16 @@ common/views/components/profile-editor.vue:
email-verified: "メールアドレスが確認されました"
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
export: "エクスポート"
import: "インポート"
export-and-import: "エクスポートとインポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
user-lists: "リスト"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
import-requested: "インポートをリクエストしました。これには時間がかかる場合があります。"
enter-password: "パスワードを入力してください"
danger-zone: "危険な設定"
delete-account: "アカウントを削除"
@@ -1156,7 +1180,7 @@ admin/views/dashboard.vue:
federated: "連合"
admin/views/queue.vue:
operation: "操作"
title: "キュー"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
@@ -1384,12 +1408,13 @@ admin/views/hashtags.vue:
hided-tags: "Hidden Tags"
admin/views/federation.vue:
federation: "連合"
instance: "インスタンス"
host: "ホスト"
notes: "投稿"
users: "ユーザー"
following: "フォロー中"
followers: "フォロワー"
caught-at: "登録日時"
status: "ステータス"
latest-request-sent-at: "直近のリクエスト送信"
latest-request-received-at: "直近のリクエスト受信"
@@ -1398,7 +1423,7 @@ admin/views/federation.vue:
block: "ブロック"
marked-as-closed: "閉鎖されているとマーク"
lookup: "照会"
instances: "インスタンス"
instances: "連合"
instance-not-registered: "そのインスタンスは登録されていません"
sort: "ソート"
sorts:
@@ -1467,9 +1492,6 @@ desktop/views/pages/search.vue:
not-available: "検索機能はインスタンスの設定で無効になっています。"
not-found: "「{q}」に関する投稿は見つかりませんでした。"
desktop/views/pages/share.vue:
share-with: "{name}で共有"
desktop/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
+23 -333
View File
@@ -1,7 +1,6 @@
---
meta:
lang: "日本語 (関西弁)"
divider: ""
common:
misskey: "A ⭐ of fediverse"
about-title: "A ⭐ of fediverse."
@@ -28,13 +27,6 @@ common:
load-more: "もっとあらへんのか!"
enter-password: "パスワードを入れてや"
2fa: "二段階認証"
customize-home: "ホームをカスタマイズ"
featured-notes: "ハイライト"
dark-mode: "ダークモード"
signin: "ログイン"
signup: "新規登録"
signout: "ログアウト"
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
got-it: "ほい"
customization-tips:
title: "カスタマイズのヒント"
@@ -64,15 +56,10 @@ common:
drive: "ドライブ"
messaging: "トーク"
home: "ホーム"
deck: "デッキ"
timeline: "タイムライン"
explore: "みつける"
following: "フォロー中"
following: "フォローしとる"
followers: "フォロワー"
favorites: "お気に入り"
empty-timeline-info:
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
explore: "ユーザーを探索する"
weekday-short:
sunday: "日"
monday: "月"
@@ -118,129 +105,25 @@ common:
d: "言うときたいことは?"
e: "ここに書いてや"
f: "あんさんが書くんを待っちょります..."
settings: "設定"
_settings:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
tags: "ハッシュタグ"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "ログイン履歴"
password: "パスワード"
other: "その他"
appearance: "デザイン"
behavior: "動作"
fetch-on-scroll: "スクロールで自動読み込み"
fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。"
note-visibility: "投稿の公開範囲"
default-note-visibility: "デフォルトの公開範囲"
remember-note-visibility: "投稿の公開範囲を記憶する"
web-search-engine: "ウェブ検索エンジン"
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
keep-cw: "CW保持"
keep-cw-desc: "投稿にリプライする際、リプライ元の投稿にCWが設定されていたとき、デフォルトで同じCWを設定するようにします。"
i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示"
use-avatar-reversi-stones: "リバーシの石にアバターを使う"
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
disable-showing-animated-images: "アニメーション画像を再生しない"
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
show-full-acct: "ユーザー名のホストを省略しない"
show-via: "viaを表示する"
reduce-motion: "UIの動きを減らす"
this-setting-is-this-device-only: "このデバイスのみ"
use-os-default-emojis: "OS標準の絵文字を使用"
line-width: "線の太さ"
line-width-thin: "細い"
line-width-normal: "普通"
line-width-thick: "太い"
font-size: "文字の大きさ"
font-size-x-small: "小さい"
font-size-small: "少し小さい"
font-size-medium: "普通"
font-size-large: "少し大きい"
font-size-x-large: "大きい"
deck-column-align: "デッキのカラムの配置"
deck-column-align-center: "中央"
deck-column-align-left: "左"
deck-column-align-flexible: "フレキシブル"
deck-column-width: "デッキのカラムの幅"
deck-column-width-narrow: "狭"
deck-column-width-narrower: "やや狭"
deck-column-width-normal: "普通"
deck-column-width-wider: "やや広"
deck-column-width-wide: "広"
use-shadow: "UIに影を使用"
rounded-corners: "UIの角を丸める"
circle-icons: "円形のアイコンを使用"
contrasted-acct: "ユーザー名にコントラストを付ける"
wallpaper: "壁紙"
choose-wallpaper: "壁紙を選択"
delete-wallpaper: "壁紙を削除"
post-form-on-timeline: "タイムライン上部に投稿フォームを表示する"
show-clock-on-header: "右上に時計を表示する"
show-reply-target: "リプライ先を表示する"
timeline: "タイムライン"
show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する"
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
remain-deleted-note: "削除された投稿を表示し続ける"
sound: "サウンド"
enable-sounds: "サウンドを有効にする"
enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。"
volume: "ボリューム"
test: "テスト"
update: "Misskey Update"
version: "バージョン:"
latest-version: "最新のバージョン:"
update-checking: "アップデートを確認中"
do-update: "アップデートを確認"
update-settings: "詳細設定"
no-updates: "利用可能な更新はありません"
no-updates-desc: "お使いのMisskeyは最新です。"
update-available: "新しいバージョンが利用可能です"
update-available-desc: "ページを再度読み込みすると更新が適用されます。"
advanced-settings: "高度な設定"
debug-mode: "デバッグモードを有効にする"
debug-mode-desc: "この設定はブラウザに記憶されます。"
navbar-position: "ナビゲーションバーの位置"
navbar-position-top: "上"
navbar-position-left: "左"
navbar-position-right: "右"
i-am-under-limited-internet: "私は通信を制限されている"
post-style: "投稿の表示スタイル"
post-style-standard: "標準"
post-style-smart: "スマート"
notification-position: "通知の表示"
notification-position-bottom: "下"
notification-position-top: "上"
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
load-raw-images: "添付された画像を高画質で表示する"
load-remote-media: "リモートサーバーのメディアを表示する"
search: "検索"
delete: "削除"
loading: "読み込み中"
ok: "おk"
cancel: "やめる"
update-available-title: "更新があんで"
update-available: "Misskeyの新しいバージョンがあんで({newer}。現在{current}をつこてるわ)。ページを再度読み込みしたると更新が適用されるわ。"
my-token-regenerated: "あんさんのトークンが更新されたらしいわ。すまんがとりあえずサインアウトすんで。"
verified-user: "アメちゃん付きアカウント"
hide-password: "パスワードを隠す"
show-password: "パスワードを表示する"
do-not-use-in-production: "開発ビルドや。本番環境で使わんといて!知らんで!"
user-suspended: "このユーザーは凍結されています。"
is-remote-user: "このユーザー情報は不正確な可能性があります。"
is-remote-post: "この投稿情報はコピーです。"
view-on-remote: "ちゃんとした情報見せてや!"
renoted-by: "{user}がRenote"
no-notes: "投稿がありません"
turn-on-darkmode: "闇に飲まれる"
turn-off-darkmode: "光あれ"
error:
title: "問題が起こったわ"
retry: "もっぺん"
@@ -279,7 +162,7 @@ common:
hashtags: "ハッシュタグ"
dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
ai-chan-kawaii: "藍ちゃめっさべっぴんさんや"
you: "あなた"
you: "あんさん"
auth/views/form.vue:
share-access: "あんたのアカウントに<i>{name}</i>がアクセスしようとしてるで?ええか?"
permission-ask: "このアプリは次の権限を要求してんで:"
@@ -305,18 +188,8 @@ auth/views/index.vue:
error: "セッションが存在してへん。"
sign-in: "サインインしてや"
common/views/pages/explore.vue:
verified-users: "公式アカウント"
popular-users: "人気のユーザー"
recently-updated-users: "最近投稿したユーザー"
recently-registered-users: "新規ユーザー"
popular-tags: "人気のタグ"
verified-users: "アメちゃん付きアカウント"
federated: "連合"
explore: "{host}を探索"
users-info: "現在{users}ユーザーが登録されています"
common/views/components/url-preview.vue:
enable-player: "プレイヤーを開く"
common/views/components/user-list.vue:
no-users: "ユーザーがいません"
common/views/components/games/reversi/reversi.vue:
matching:
waiting-for: "{}を待っとります"
@@ -388,7 +261,6 @@ common/views/components/media-banner.vue:
sensitive: "見せたらあかん"
click-to-show: "押してみ、見せたるわ"
common/views/components/theme.vue:
theme: "テーマ"
light-theme: "ナイトゲームちゃう時のテーマどないする?"
dark-theme: "ナイトゲームの時のテーマどないする?"
light-themes: "デイゲーム"
@@ -405,7 +277,6 @@ common/views/components/theme.vue:
base-theme: "この色が背景や!"
base-theme-light: "Light"
base-theme-dark: "Dark"
find-more-theme: "その他のテーマを入手"
theme-name: "テーマ名"
preview-created-theme: "試してみる"
invalid-theme: "このテーマあかんわ、なんか間違うとる"
@@ -427,8 +298,6 @@ common/views/components/theme.vue:
common/views/components/cw-button.vue:
hide: "もうええわ"
show: "見たいやろ?"
chars: "{count}文字"
files: "{count}ファイル"
poll: "アンケート"
common/views/components/messaging.vue:
search-user: "ユーザーを探す"
@@ -459,38 +328,22 @@ common/views/components/nav.vue:
develop: "開発者"
feedback: "フィードバック"
common/views/components/note-menu.vue:
mention: "メンション"
detail: "もっと"
copy-content: "内容をコピー"
copy-link: "リンクをコピー"
favorite: "お気に入り"
unfavorite: "お気に入りやめる"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
pin: "ピン留め"
unpin: "ピン留めやめる"
delete: "ほかす"
delete-confirm: "この投稿を削除してもええか?"
remote: "投稿元に行ってみよか"
common/views/components/user-menu.vue:
mention: "メンション"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
push-to-list: "リストに追加"
select-list: "リストを選択してください"
report-abuse: "スパムを報告"
report-abuse-detail: "どのような迷惑行為を行っていますか?"
report-abuse-reported: "管理者に報告されました。ご協力ありがとうございました。"
silence: "サイレンス"
unsilence: "サイレンス解除"
suspend: "凍結"
unsuspend: "凍結解除"
common/views/components/poll.vue:
vote-to: "「{}」に投票や!"
vote-count: "{}票"
total-users: "{}人が投票"
vote: "投票するで"
show-result: "結果を見よか"
voted: "投票済みや"
@@ -500,6 +353,7 @@ common/views/components/poll-editor.vue:
remove: "この選択肢を消すで"
add: "+選択肢を追加"
destroy: "アンケートをほかそ"
day: "日"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクション、どれにするんや?"
common/views/components/emoji-picker.vue:
@@ -554,11 +408,6 @@ common/views/components/stream-indicator.vue:
connected: "つないだわ"
common/views/components/notification-settings.vue:
title: "通知"
mark-as-read-all-notifications: "すべての通知を既読にする"
mark-as-read-all-unread-notes: "すべての投稿を既読にする"
mark-as-read-all-talk-messages: "すべてのトークを既読にする"
auto-watch: "投稿の自動ウォッチ"
auto-watch-desc: "リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。"
common/views/components/integration-settings.vue:
title: "サービス連携"
connect: "つなげる"
@@ -608,7 +457,6 @@ common/views/components/profile-editor.vue:
account: "アカウント"
location: "場所"
description: "自己紹介"
you-can-include-hashtags: "ハッシュタグを含めることができます。"
language: "言語"
birthday: "誕生日"
avatar: "アイコン"
@@ -617,7 +465,6 @@ common/views/components/profile-editor.vue:
is-bot: "このアカウントはBotやで"
is-locked: "他人のフォローは許可してからや!"
careful-bot: "Botからのフォローだけは許可制や"
auto-accept-followed: "フォローしているユーザーからのフォローを自動承認する"
advanced: "その他"
privacy: "プライバシーってなんや?オカンの年齢か?"
save: "保存"
@@ -629,23 +476,15 @@ common/views/components/profile-editor.vue:
email-verified: "このメールアドレスOKや!"
email-not-verified: "メールアドレスが確認されとらん。メールボックスもっぺん見てくれへん?"
export: "エクスポート"
import: "インポート"
export-targets:
all-notes: "すべての投稿データ"
following-list: "フォロー"
mute-list: "ミュート"
blocking-list: "ブロック"
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
enter-password: "パスワードを入力してください"
danger-zone: "危険な設定"
delete-account: "アカウントを削除"
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
user-lists: "リスト"
enter-password: "パスワードを入れてや"
common/views/components/user-list-editor.vue:
users: "ユーザー"
rename: "リスト名を変更"
delete: "リストを削除"
remove-user: "このリストから削除"
delete-are-you-sure: "リスト「$1」を削除しますか?"
deleted: "削除しました"
common/views/widgets/broadcast.vue:
fetching: "見てみるわ…"
no-broadcasts: "お知らせはあらへんで"
@@ -695,11 +534,8 @@ common/views/widgets/tips.vue:
tips-line19: "いくつかのウィンドウはブラウザの外に切り離すことができんで"
tips-line20: "カレンダーウィジェットのパーセンテージは、経過の割合を示してんねん"
tips-line21: "APIをつこてbotの開発なども行えるで"
tips-line23: "藍かわいいよ藍"
tips-line24: "Misskeyは2014年にサービスを開始したんよ"
tips-line25: "対応ブラウザやったらMisskeyを開いとらんでも通知を受け取れんで"
common/views/pages/not-found.vue:
page-not-found: "ページが見つかりませんでした"
common/views/pages/follow.vue:
signed-in-as: "{}としてサインイン中"
following: "フォローしとる"
@@ -826,12 +662,10 @@ desktop/views/components/note-detail.vue:
location: "ここおるで:"
renote: "Renote"
add-reaction: "リアクション"
undo-reaction: "リアクション解除"
desktop/views/components/note.vue:
reply: "返す"
renote: "Renote"
add-reaction: "リアクション"
undo-reaction: "リアクション解除"
detail: "もっと"
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
@@ -859,7 +693,6 @@ desktop/views/components/post-form.vue:
attach-media-from-local: "PCからメディア持ってくる"
attach-media-from-drive: "ドライブからメディア持ってくる"
attach-cancel: "くっつけるのやめよか"
insert-a-kao: "v('ω')v"
create-poll: "アンケートを作成"
text-remain: "残り{}文字"
recent-tags: "最近のタグ"
@@ -910,8 +743,8 @@ desktop/views/components/settings.2fa.vue:
failed: "なんか設定に失敗したで。トークンを間違えとらんか確認してや。"
info: "次のサインインからは、パスワードに加えてデバイスに出とるトークンを入力してな。"
common/views/components/media-image.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
sensitive: "ちょっと見せられへんわ"
click-to-show: "クリックして見せるで"
common/views/components/api-settings.vue:
intro: "API使うんやったらこのトークンを「i」っちゅうパラメータにくっつけてリクエストできるで。"
caution: "アカウント勝手にいじられるかも知れんから、このトークンは教えたらあかんし、アプリにも書いたらあかんで(これはフリちゃうで)"
@@ -950,16 +783,13 @@ common/views/components/password-settings.vue:
enter-new-password-again: "もっぺん入れてや"
not-match: "パスワードがおうとらん"
changed: "パスワード変えたわ"
failed: "パスワード変更に失敗しました"
desktop/views/components/sub-note-content.vue:
private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ"
media-count: "{}つのメディア"
poll: "アンケート"
desktop/views/components/settings.tags.vue:
title: "タグ"
query: "クエリ (省略可)"
add: "追加"
add: "増やす"
save: "保存"
desktop/views/components/taskmanager.vue:
title: "タスクマネージャ"
@@ -1021,9 +851,6 @@ admin/views/index.vue:
federation: "連合"
announcements: "知っといてや"
hashtags: "ハッシュタグ"
abuse: "スパム報告"
queue: "ジョブキュー"
logs: "ログ"
back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue:
dashboard: "ダッシュボード"
@@ -1033,14 +860,8 @@ admin/views/dashboard.vue:
instances: "インスタンス"
this-instance: "ワイのインスタンス"
federated: "連合"
admin/views/queue.vue:
operation: "操作"
remove-all-jobs: "すべてのジョブをクリア"
admin/views/abuse.vue:
title: "スパム報告"
target: "対象"
reporter: "報告者"
details: "詳細"
details: "もっと"
remove-report: "削除"
admin/views/instance.vue:
instance: "インスタンス"
@@ -1048,7 +869,6 @@ admin/views/instance.vue:
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
error-image-url: "エラー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できるで。"
maintainer-config: "管理者情報"
@@ -1088,8 +908,6 @@ admin/views/instance.vue:
max-note-text-length: "投稿の最大文字数"
disable-registration: "ユーザー登録の受付を止める"
disable-local-timeline: "ローカルタイムラインを使えんようにする"
disable-global-timeline: "グローバルタイムラインを無効にする"
disabling-timelines-info: "これらのタイムラインを無効にしても、管理者およびモデレーターは引き続き利用できます。"
invite: "来てや"
save: "保存"
saved: "保存したで!"
@@ -1107,15 +925,8 @@ admin/views/instance.vue:
smtp-secure-info: "STARTTLS使用時はオフにします。"
smtp-host: "SMTPホスト"
smtp-port: "SMTPポート"
smtp-auth: "SMTP認証を行う"
smtp-user: "SMTPユーザー"
smtp-pass: "SMTPパスワード"
serviceworker-config: "ServiceWorker"
enable-serviceworker: "ServiceWorkerを有効にする"
serviceworker-info: "プッシュ通知を行うには有効する必要があります。"
vapid-publickey: "VAPID公開鍵"
vapid-privatekey: "VAPID秘密鍵"
vapid-info: "ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります。シェルで次のようにします:"
admin/views/charts.vue:
title: "チャート"
per-day: "1日ごと"
@@ -1134,7 +945,6 @@ admin/views/charts.vue:
notes-total: "投稿の積算"
users: "ユーザーの増減"
users-total: "ユーザーの積算"
active-users: "アクティブユーザー数"
drive: "ドライブ使用量の増減"
drive-total: "ドライブ使用量の積算"
drive-files: "ドライブのファイル数の増減"
@@ -1144,168 +954,61 @@ admin/views/charts.vue:
network-usage: "通信量"
admin/views/drive.vue:
operation: "操作"
fileid-or-url: "ファイルIDまたはファイルURL"
file-not-found: "ファイルが見つかりません"
lookup: "照会"
sort:
title: "ソート"
createdAtAsc: "アップロード日時が古い順"
createdAtDesc: "アップロード日時が新しい順"
sizeAsc: "サイズが小さい順"
sizeDesc: "サイズが大きい順"
origin:
title: "オリジン"
combined: "ローカル+リモート"
local: "ローカル"
remote: "リモート"
delete: "削除"
deleted: "削除しました"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
marked-as-sensitive: "閲覧注意に設定しました"
unmarked-as-sensitive: "閲覧注意を解除しました"
mark-as-sensitive: "見たらあかん感じにしとく"
unmark-as-sensitive: "やっぱ見せたるわ"
admin/views/users.vue:
operation: "操作"
username-or-userid: "ユーザー名またはユーザーID"
user-not-found: "ユーザーが見つからへん!"
lookup: "照会"
reset-password: "パスワードをリセット"
reset-password-confirm: "パスワードをリセットしますか?"
password-updated: "パスワードは現在「{password} 」やで"
suspend: "凍結"
suspend-confirm: "凍結しますか?"
suspended: "凍結しました"
unsuspend: "凍結の解除"
unsuspend-confirm: "凍結を解除しますか?"
unsuspended: "凍結を解除しました"
make-silence: "サイレンス"
unmake-silence: "サイレンスの解除"
verify: "公式アカウントにする"
verify-confirm: "公式アカウントにしますか?"
verified: "公式アカウントにしました"
unverify: "公式アカウントを解除する"
unverify-confirm: "公式アカウントを解除しますか?"
unverified: "公式アカウントを解除しました"
update-remote-user: "リモートユーザー情報の更新"
remote-user-updated: "リモートユーザー情報を更新しました"
users:
title: "ユーザー"
sort:
title: "ソート"
createdAtAsc: "登録日時が古い順"
createdAtDesc: "登録日時が新しい順"
updatedAtAsc: "更新日時が古い順"
updatedAtDesc: "更新日時が新しい順"
state:
title: "状態"
all: "すべて"
admin: "管理者"
moderator: "モデレーター"
adminOrModerator: "管理者+モデレーター"
verified: "公式アカウント"
silenced: "サイレンス済み"
suspended: "凍結済み"
verified: "アメちゃん付きアカウント"
origin:
title: "オリジン"
combined: "ローカル+リモート"
local: "ローカル"
remote: "リモート"
createdAt: "登録日時"
updatedAt: "更新日時"
admin/views/moderators.vue:
add-moderator:
title: "モデレーターの登録"
add: "登録"
added: "モデレーターを登録しました"
remove: "解除"
removed: "モデレーター登録を解除しました"
admin/views/emoji.vue:
add-emoji:
title: "絵文字の登録"
name: "絵文字名"
name-desc: "a~z 0~9 _ の文字が使えます。"
aliases: "エイリアス"
aliases-desc: "スペースで区切って複数設定できます。"
url: "絵文字画像URL"
add: "追加"
info: "50KB以下のPNG画像をおすすめします。"
added: "絵文字を登録しました"
add: "増やす"
emojis:
title: "絵文字一覧"
update: "更新"
remove: "削除"
updated: "更新しました"
remove-emoji:
are-you-sure: "「$1」を削除しますか?"
removed: "削除しました"
admin/views/announcements.vue:
announcements: "お知らせ"
announcements: "知っときや"
save: "保存"
remove: "削除"
add: "追加"
title: "タイトル"
text: "内容"
saved: "保存しました"
_remove:
are-you-sure: "「$1」を削除しますか?"
removed: "削除しました"
admin/views/hashtags.vue:
hided-tags: "Hidden Tags"
add: "増やす"
saved: "保存したで!"
admin/views/federation.vue:
federation: "連合"
host: "ホスト"
notes: "投稿"
users: "ユーザー"
following: "フォロー"
following: "フォローしとる"
followers: "フォロワー"
status: "ステータス"
latest-request-sent-at: "直近のリクエスト送信"
latest-request-received-at: "直近のリクエスト受信"
remove-all-following: "フォローを全解除"
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
block: "ブロック"
marked-as-closed: "閉鎖されているとマーク"
lookup: "照会"
instances: "インスタンス"
instance-not-registered: "そのインスタンスは登録されていません"
sort: "ソート"
sorts:
caughtAtAsc: "登録日時が古い順"
caughtAtDesc: "登録日時が新しい順"
lastCommunicatedAtAsc: "最後にやり取りした日時が古い順"
lastCommunicatedAtDesc: "最後にやり取りした日時が新しい順"
notesAsc: "投稿が少ない順"
notesDesc: "投稿が多い順"
usersAsc: "ユーザーが少ない順"
usersDesc: "ユーザーが多い順"
followingAsc: "フォローが少ない順"
followingDesc: "フォローが多い順"
followersAsc: "フォロワーが少ない順"
followersDesc: "フォロワーが多い順"
driveUsageAsc: "ドライブ使用量が少ない順"
driveUsageDesc: "ドライブ使用量が多い順"
driveFilesAsc: "ドライブのファイル数が少ない順"
driveFilesDesc: "ドライブのファイル数が多い順"
state: "状態"
states:
all: "すべて"
blocked: "ブロック"
not-responding: "応答なし"
marked-as-closed: "閉鎖とマーク済み"
result-is-truncated: "上位{n}件を表示しています。"
charts: "チャート"
chart-srcs:
requests: "リクエスト"
users: "ユーザーの増減"
users-total: "ユーザーの積算"
notes: "投稿の増減"
notes-total: "投稿の積算"
ff: "フォロー/フォロワーの増減"
ff-total: "フォロー/フォロワーの積算"
drive-usage: "ドライブ使用量の増減"
drive-usage-total: "ドライブ使用量の積算"
drive-files: "ドライブファイル数の増減"
drive-files-total: "ドライブファイル数の積算"
chart-spans:
hour: "1時間ごと"
day: "1日ごと"
@@ -1328,11 +1031,6 @@ desktop/views/pages/selectdrive.vue:
upload: "PCからドライブにファイル上げる"
desktop/views/pages/search.vue:
not-available: "検索機能は使えへんわ。管理者がそう言うとる。"
not-found: "「{q}」に関する投稿は見つかりませんでした。"
desktop/views/pages/share.vue:
share-with: "{name}で共有"
desktop/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
desktop/views/pages/user-list.users.vue:
users: "ユーザー"
add-user: "ユーザー増やす"
@@ -1354,17 +1052,15 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotや"
no-description: "自己紹介はありません"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
follows-you: "フォローされています"
follows-you: "フォローされとるで"
desktop/views/pages/user/user.timeline.vue:
default: "投稿"
with-replies: "投稿と返信"
with-media: "メディア"
my-posts: "私の投稿"
desktop/views/widgets/messaging.vue:
title: "メッセージ"
desktop/views/widgets/notifications.vue:
@@ -1397,7 +1093,6 @@ mobile/views/components/drive.vue:
prompt: "何すんの?(数字を入れてや): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
deletion-alert: "フォルダの削除は未実装やねん...。堪忍な!"
folder-name: "フォルダー名"
here-is-root: "現在いる場所はルートで、フォルダではありません。"
url-prompt: "このURLのファイルをアップロードしたいねん"
uploading: "アップロードをリクエストしたで。アップロードが完了するまで時間がかかるかも分からん、知らんけど。"
mobile/views/components/drive-file-chooser.vue:
@@ -1493,8 +1188,7 @@ mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿はあらへんかった。"
mobile/views/pages/widgets.vue:
dashboard: "ダッシュボード"
widgets-hints: "ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。"
add-widget: "追加"
add-widget: "増やす"
customization-tips: "カスタマイズのヒント"
mobile/views/pages/widgets/activity.vue:
activity: "やっとること"
@@ -1532,7 +1226,7 @@ mobile/views/pages/user/home.vue:
activity: "やっとること"
keywords: "キーワード"
domains: "よく出るドメイン"
frequently-replied-users: "よ話すユーザー"
frequently-replied-users: "よ話すツレ"
followers-you-know: "知っとるフォロワー"
last-used-at: "最後いつ来た?"
mobile/views/pages/user/home.photos.vue:
@@ -1548,7 +1242,6 @@ deck:
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
select-list: "リストを選択してください"
swap-left: "左に移動や!"
swap-right: "右に移動や!"
swap-up: "上に移動や!"
@@ -1558,14 +1251,11 @@ deck:
rename: "名前を変えるで"
stack-left: "左に重ねんで!"
pop-right: "右に出すで!"
disabled-timeline:
title: "無効化されたタイムライン"
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
deck/deck.tl-column.vue:
is-media-only: "メディア投稿だけや"
edit: "オプション"
deck/deck.user-column.vue:
follows-you: "フォローされています"
follows-you: "フォローされとるで"
posts: "投稿"
following: "フォロー"
followers: "フォロワー"
+26 -5
View File
@@ -1,7 +1,6 @@
---
meta:
lang: "한국어"
divider: ""
common:
misskey: "연합우주의 ⭐"
about-title: "연합우주의 ⭐."
@@ -315,6 +314,7 @@ common/views/pages/explore.vue:
users-info: "현재 {users} 사용자가 등록되어 있습니다"
common/views/components/url-preview.vue:
enable-player: "플레이어 열기"
disable-player: "플레이어 닫기"
common/views/components/user-list.vue:
no-users: "사용자가 없습니다"
common/views/components/games/reversi/reversi.vue:
@@ -490,16 +490,35 @@ common/views/components/user-menu.vue:
common/views/components/poll.vue:
vote-to: "\"{}\"에 투표하기"
vote-count: "{}표"
total-users: "{}명이 투표"
total-votes: "{}표"
vote: "투표하기"
show-result: "결과 보기"
voted: "투표함"
closed: "종료됨"
remaining-days: "종료까지 앞으로 {d}일 {h}시간"
remaining-hours: "종료까지 앞으로 {h}시간 {m}분"
remaining-minutes: "종료까지 앞으로 {m}분 {s}초"
remaining-seconds: "종료까지 앞으로 {s}초"
common/views/components/poll-editor.vue:
no-only-one-choice: "투표에는 선택지가 최소한 두 개 필요합니다"
choice-n: "선택지 {}"
remove: "이 선택지를 제거"
add: "+선택지 추가"
destroy: "투표 제거"
multiple: "복수 응답 가능"
expiration: "기한"
infinite: "무기한"
at: "일시 지정"
after: "기간 지정"
no-more: "더 이상 추가할 수 없습니다"
deadline-date: "기한"
deadline-time: "시간"
interval: "기간"
unit: "단위"
second: "초"
minute: "분"
hour: "시간"
day: "일"
common/views/components/reaction-picker.vue:
choose-reaction: "반응 선택"
common/views/components/emoji-picker.vue:
@@ -629,12 +648,16 @@ common/views/components/profile-editor.vue:
email-verified: "매일 주소가 확인되었습니다"
email-not-verified: "메일 주소가 확인되지 않았습니다. 받은 편지함을 확인하여 주시기 바랍니다."
export: "내보내기"
import: "가져오기"
export-and-import: "내보내기와 가져오기"
export-targets:
all-notes: "모든 글 데이터"
following-list: "팔로잉"
mute-list: "뮤트"
blocking-list: "차단"
user-lists: "리스트"
export-requested: "내보내기를 요청하였습니다. 이 작업은 시간이 걸릴 수 있습니다. 내보내기가 완료되면 드라이브에 파일이 추가됩니다."
import-requested: "가져오기를 요청하였습니다. 이 작업에는 시간이 걸릴 수 있습니다."
enter-password: "비밀번호를 입력하여 주십시오"
danger-zone: "위험한 설정"
delete-account: "계정 삭제"
@@ -1034,7 +1057,7 @@ admin/views/dashboard.vue:
this-instance: "이 인스턴스"
federated: "연합"
admin/views/queue.vue:
operation: "동작"
title: ""
remove-all-jobs: "모든 작업 제거"
admin/views/abuse.vue:
title: "스팸 신고"
@@ -1329,8 +1352,6 @@ desktop/views/pages/selectdrive.vue:
desktop/views/pages/search.vue:
not-available: "검색 기능은 인스턴스 설정에서 비활성화되어 있습니다."
not-found: "\"{q}\" 와 일치하는 글을 찾을 수 없습니다."
desktop/views/pages/share.vue:
share-with: "{name}(으)로 공유"
desktop/views/pages/tag.vue:
no-posts-found: "해시태그 \"{q}\"가 붙은 글을 찾을 수 없습니다."
desktop/views/pages/user-list.users.vue:
+123 -1111
View File
File diff suppressed because it is too large Load Diff
+83 -1203
View File
File diff suppressed because it is too large Load Diff
+91 -485
View File
File diff suppressed because it is too large Load Diff
+69 -1396
View File
File diff suppressed because it is too large Load Diff
+12 -1489
View File
File diff suppressed because it is too large Load Diff
+26 -5
View File
@@ -1,7 +1,6 @@
---
meta:
lang: "中文(简体)"
divider: ""
common:
misskey: "Fediverse中的一颗⭐"
about-title: "Fediverse中的一颗⭐"
@@ -315,6 +314,7 @@ common/views/pages/explore.vue:
users-info: "当前有{users}个注册用户"
common/views/components/url-preview.vue:
enable-player: "打开播放器"
disable-player: "关闭播放器"
common/views/components/user-list.vue:
no-users: "无用户"
common/views/components/games/reversi/reversi.vue:
@@ -490,16 +490,35 @@ common/views/components/user-menu.vue:
common/views/components/poll.vue:
vote-to: "为\"{}\"投票"
vote-count: "{}票"
total-users: "{} 人投票"
total-votes: "总票数{}"
vote: "投票"
show-result: "显示结果"
voted: "已投票"
closed: "已截止"
remaining-days: "{d}天{h}小时后截止"
remaining-hours: "{h}小时{m}分后截止"
remaining-minutes: "{m}分{s}秒后截止"
remaining-seconds: "{s}秒后截止"
common/views/components/poll-editor.vue:
no-only-one-choice: "至少选择两个选项"
choice-n: "选择{}"
remove: "删除选项"
add: "+添加一个选项"
destroy: "放弃投票"
multiple: "允许多个投票"
expiration: "截止时间"
infinite: "永久"
at: "指定日期"
after: "指定时间"
no-more: "最多只能添加十个回答"
deadline-date: "日期"
deadline-time: "时间"
interval: "时长"
unit: "单位"
second: "秒"
minute: "分"
hour: "小时"
day: "日"
common/views/components/reaction-picker.vue:
choose-reaction: "选择回应"
common/views/components/emoji-picker.vue:
@@ -629,12 +648,16 @@ common/views/components/profile-editor.vue:
email-verified: "电子邮件地址已验证"
email-not-verified: "邮件地址尚未验证。 请检查您的邮箱。"
export: "导出"
import: "导入"
export-and-import: "导出/导入"
export-targets:
all-notes: "所有发帖"
following-list: "关注列表"
mute-list: "屏蔽列表"
blocking-list: "黑名单"
user-lists: "列表"
export-requested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
import-requested: "导入请求已提交。这可能需要花一点时间。"
enter-password: "请输入您的密码"
danger-zone: "危险选项"
delete-account: "删除帐户"
@@ -1034,7 +1057,7 @@ admin/views/dashboard.vue:
this-instance: "此实例"
federated: "联合"
admin/views/queue.vue:
operation: "操作"
title: "队列"
remove-all-jobs: "清除所有作业"
admin/views/abuse.vue:
title: "举报垃圾信息"
@@ -1329,8 +1352,6 @@ desktop/views/pages/selectdrive.vue:
desktop/views/pages/search.vue:
not-available: "在此实例的设置中关闭搜索功能。"
not-found: "没有找到“{q}”的帖子"
desktop/views/pages/share.vue:
share-with: "共享{name}"
desktop/views/pages/tag.vue:
no-posts-found: "没有找到带有主题标签“{q}”的帖子"
desktop/views/pages/user-list.users.vue:
+88
View File
@@ -0,0 +1,88 @@
---
meta:
lang: "中文(繁体)"
common:
intro:
title: "什麽是 Misskey 呢?"
rich-contents: "發佈"
reaction: "回應"
drive: "雲端硬碟"
adblock:
detected: "請禁用廣告封鎖器"
close: "關閉"
enter-password: "請輸入密碼"
2fa: "雙重身份驗證"
dark-mode: "夜間模式"
signup: "註冊"
signout: "登出"
notification:
reversi-invited: "您已被邀請加入壹場遊戲"
reversi-invited-by: "來自{}的邀請"
notified-by: "來自{}的邀請"
time:
future: "未來"
just_now: "剛剛"
drive: "雲端硬碟"
weekday:
sunday: "週日"
monday: "週一"
tuesday: "週二"
wednesday: "週三"
thursday: "週四"
friday: "週五"
saturday: "週六"
reactions:
like: "贊"
love: "喜歡"
congrats: "恭喜"
_settings:
password: "密碼"
font-size: "字體大小"
font-size-x-small: "小"
font-size-small: "較小"
deck-column-width-wide: "寬"
timeline: "時間軸"
common/views/components/connect-failed.troubleshooter.vue:
flush: "清除快取"
common/views/components/theme.vue:
light-themes: "淺色主題"
dark-themes: "深色主題"
install-a-theme: "安裝主題"
save-created-theme: "保存主題"
common/views/components/signin.vue:
signin-with-twitter: "用 Twitter 帳號登入"
signin-with-github: "用 GitHub 帳號登入"
signin-with-discord: "用 Discord 帳號登入"
login-failed: "登錄失敗。 請檢查用戶名和密碼。"
common/views/components/signup.vue:
invitation-code: "邀請碼"
username: "用戶名"
available: "可用"
too-long: "請不要超過20個字元"
password: "密碼"
password-placeholder: "建議至少8個字元"
common/views/components/stream-indicator.vue:
connecting: "正在連線"
reconnecting: "正在重新連線"
connected: "已建立連線"
common/views/components/integration-settings.vue:
disconnect: "中斷連線"
common/views/components/github-setting.vue:
reconnect: "重新連線"
disconnect: "中斷連線"
common/views/components/discord-setting.vue:
reconnect: "重新連線"
disconnect: "中斷連線"
common/views/components/language-settings.vue:
recommended: "推薦"
auto: "自動"
specify-language: "指定語言"
common/views/components/profile-editor.vue:
title: "個人資料"
name: "名稱"
birthday: "生日:"
privacy: "隱私"
admin/views/dashboard.vue:
drive: "雲端硬碟"
admin/views/charts.vue:
drive: "雲端硬碟"
+30 -32
View File
@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.91.1",
"version": "10.96.0",
"codename": "nighthike",
"repository": {
"type": "git",
@@ -32,6 +32,7 @@
"@prezzemolo/rap": "0.1.2",
"@prezzemolo/zip": "0.0.3",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.5.8",
"@types/chai-http": "3.0.5",
"@types/dateformat": "3.0.0",
"@types/deep-equal": "1.0.1",
@@ -45,7 +46,6 @@
"@types/gulp-uglify": "3.0.6",
"@types/gulp-util": "3.0.34",
"@types/is-root": "1.0.0",
"@types/is-svg": "3.0.0",
"@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.0",
"@types/jsdom": "12.2.3",
@@ -58,7 +58,7 @@
"@types/koa-logger": "3.1.1",
"@types/koa-mount": "3.0.1",
"@types/koa-multer": "1.0.0",
"@types/koa-router": "7.0.39",
"@types/koa-router": "7.0.40",
"@types/koa-send": "4.1.1",
"@types/koa-views": "2.0.3",
"@types/koa__cors": "2.2.3",
@@ -66,7 +66,7 @@
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.5",
"@types/mongodb": "3.1.20",
"@types/node": "10.12.24",
"@types/node": "11.10.4",
"@types/nodemailer": "4.6.6",
"@types/nprogress": "0.0.29",
"@types/oauth": "0.9.1",
@@ -84,7 +84,7 @@
"@types/seedrandom": "2.4.27",
"@types/sharp": "0.21.2",
"@types/showdown": "1.9.2",
"@types/speakeasy": "2.0.3",
"@types/speakeasy": "2.0.4",
"@types/systeminformation": "3.23.1",
"@types/tinycolor2": "1.4.1",
"@types/tmp": "0.0.33",
@@ -95,34 +95,34 @@
"@types/websocket": "0.0.40",
"@types/ws": "6.0.1",
"animejs": "3.0.1",
"apexcharts": "3.5.0",
"apexcharts": "3.6.2",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
"bcryptjs": "2.4.3",
"bee-queue": "1.2.2",
"bootstrap-vue": "2.0.0-rc.11",
"bootstrap-vue": "2.0.0-rc.13",
"bull": "3.7.0",
"cafy": "15.1.0",
"chai": "4.2.0",
"chai-http": "4.2.1",
"chalk": "2.4.2",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "2.1.0",
"css-loader": "2.1.1",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"deep-equal": "1.0.1",
"deepcopy": "0.6.3",
"diskusage": "1.0.0",
"double-ended-queue": "2.1.0-0",
"elasticsearch": "15.3.1",
"elasticsearch": "15.4.1",
"emojilib": "2.4.0",
"escape-regexp": "0.0.1",
"eslint": "5.12.0",
"eslint": "5.15.1",
"eslint-plugin-vue": "5.2.2",
"eventemitter3": "3.1.0",
"feed": "2.0.2",
"file-type": "10.7.1",
"feed": "2.0.4",
"file-type": "10.9.0",
"fuckadblock": "3.2.1",
"gulp": "4.0.0",
"gulp-cssnano": "2.1.3",
@@ -130,21 +130,20 @@
"gulp-mocha": "6.0.0",
"gulp-rename": "1.4.0",
"gulp-replace": "1.0.0",
"gulp-sourcemaps": "2.6.4",
"gulp-sourcemaps": "2.6.5",
"gulp-stylus": "2.7.0",
"gulp-tslint": "8.1.3",
"gulp-tslint": "8.1.4",
"gulp-typescript": "5.0.0",
"gulp-uglify": "3.0.1",
"gulp-uglify": "3.0.2",
"gulp-util": "3.0.8",
"gulp-yaml": "2.0.3",
"hard-source-webpack-plugin": "0.13.1",
"html-minifier": "3.5.21",
"http-signature": "1.2.0",
"insert-text-at-cursor": "0.1.2",
"is-root": "2.0.0",
"is-svg": "3.0.0",
"js-yaml": "3.12.1",
"jsdom": "13.2.0",
"is-svg": "4.0.0",
"js-yaml": "3.12.2",
"jsdom": "14.0.0",
"json5": "2.1.0",
"json5-loader": "1.0.1",
"katex": "0.10.1",
@@ -176,7 +175,6 @@
"nodemailer": "5.1.1",
"nprogress": "0.2.0",
"object-assign-deep": "0.4.0",
"on-build-webpack": "0.1.0",
"os-utils": "0.0.14",
"parse5": "5.1.0",
"parsimmon": "1.12.0",
@@ -190,13 +188,13 @@
"pug": "2.0.3",
"punycode": "2.1.1",
"qrcode": "1.3.3",
"randomcolor": "0.5.3",
"ratelimiter": "3.2.0",
"randomcolor": "0.5.4",
"ratelimiter": "3.3.0",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.10",
"redis": "2.8.0",
"request": "2.88.0",
"request-promise-native": "1.0.5",
"request-promise-native": "1.0.7",
"request-stats": "3.0.0",
"rimraf": "2.6.3",
"rndstr": "1.0.0",
@@ -211,14 +209,14 @@
"stylus": "0.54.5",
"stylus-loader": "3.0.2",
"summaly": "2.2.0",
"systeminformation": "4.0.14",
"systeminformation": "4.0.16",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.2.3",
"textarea-caret": "3.1.0",
"tinycolor2": "1.4.1",
"tmp": "0.0.33",
"ts-loader": "5.3.3",
"ts-node": "8.0.2",
"ts-node": "8.0.3",
"tslint": "5.13.1",
"tslint-sonarts": "1.9.0",
"typescript": "3.3.3333",
@@ -233,27 +231,27 @@
"vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "3.0.0",
"vue-i18n": "8.8.2",
"vue-i18n": "8.9.0",
"vue-js-modal": "1.3.28",
"vue-json-viewer": "2.0.6",
"vue-json-pretty": "1.4.1",
"vue-loader": "15.7.0",
"vue-marquee-text-component": "1.1.1",
"vue-prism-component": "1.1.1",
"vue-router": "3.0.2",
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.12",
"vue-svg-inline-loader": "1.2.13",
"vue-template-compiler": "2.6.8",
"vuedraggable": "2.18.1",
"vuedraggable": "2.19.2",
"vuewordcloud": "18.7.11",
"vuex": "3.1.0",
"vuex-persistedstate": "2.5.4",
"web-push": "3.3.3",
"webfinger.js": "2.7.0",
"webpack": "4.28.4",
"webpack-cli": "3.2.1",
"webpack-cli": "3.2.3",
"websocket": "1.0.28",
"ws": "6.1.4",
"ws": "6.2.0",
"xev": "2.0.1"
}
}
+1 -3
View File
@@ -5,8 +5,7 @@ program
.version(pkg.version)
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
.option('--disable-clustering', 'Disable clustering')
.option('--disable-queue', 'Disable job queue processing')
.option('--only-server', 'Run server only (without job queue)')
.option('--only-server', 'Run server only (without job queue processing)')
.option('--only-queue', 'Pocessing job queue only (without server)')
.option('--quiet', 'Suppress all logs')
.option('--verbose', 'Enable all logs')
@@ -15,7 +14,6 @@ program
.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.')
.parse(process.argv);
/*if (process.env.MK_DISABLE_QUEUE)*/ program.disableQueue = true;
if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true;
export { program };
@@ -181,7 +181,12 @@ export default Vue.extend({
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
borderColor: 'rgba(0, 0, 0, 0.1)',
xaxis: {
lines: {
show: true,
}
},
},
stroke: {
curve: 'straight',
@@ -0,0 +1,196 @@
<template>
<div class="mzxlfysy">
<div>
<header>
<span><fa :icon="faInbox"/> In</span>
<span v-if="latestStats">{{ latestStats.inbox.activeSincePrevTick | number }} / {{ latestStats.inbox.delayed | number }}</span>
</header>
<div ref="in"></div>
</div>
<div>
<header>
<span><fa :icon="faPaperPlane"/> Out</span>
<span v-if="latestStats">{{ latestStats.deliver.activeSincePrevTick | number }} / {{ latestStats.deliver.delayed | number }}</span>
</header>
<div ref="out"></div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faInbox } from '@fortawesome/free-solid-svg-icons';
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
import ApexCharts from 'apexcharts';
const limit = 150;
export default Vue.extend({
data() {
return {
stats: [],
inChart: null,
outChart: null,
faInbox, faPaperPlane
};
},
computed: {
latestStats(): any {
return this.stats[this.stats.length - 1];
}
},
watch: {
stats(stats) {
this.inChart.updateSeries([{
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
}]);
this.outChart.updateSeries([{
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
}]);
}
},
mounted() {
const chartOpts = {
chart: {
type: 'area',
height: 200,
animations: {
dynamicAnimation: {
enabled: false
}
},
toolbar: {
show: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
},
stroke: {
curve: 'straight',
width: 2
},
tooltip: {
enabled: false
},
legend: {
show: false
},
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
series: [{ data: [] }, { data: [] }, { data: [] }, { data: [] }] as any,
xaxis: {
type: 'numeric',
labels: {
show: false
},
tooltip: {
enabled: false
}
},
yaxis: {
show: false,
min: 0,
}
};
this.inChart = new ApexCharts(this.$refs.in, chartOpts);
this.outChart = new ApexCharts(this.$refs.out, chartOpts);
this.inChart.render();
this.outChart.render();
const connection = this.$root.stream.useSharedConnection('queueStats');
connection.on('stats', this.onStats);
connection.on('statsLog', this.onStatsLog);
connection.send('requestLog', {
id: Math.random().toString().substr(2, 8),
length: limit
});
this.$once('hook:beforeDestroy', () => {
connection.dispose();
this.inChart.destroy();
this.outChart.destroy();
});
},
methods: {
onStats(stats) {
this.stats.push(stats);
if (this.stats.length > limit) this.stats.shift();
},
onStatsLog(statsLog) {
for (const stats of statsLog.reverse()) {
this.onStats(stats);
}
}
}
});
</script>
<style lang="stylus" scoped>
.mzxlfysy
display flex
> div
display block
flex 1
padding 20px 12px 0 12px
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
background var(--face)
border-radius 8px
&:first-child
margin-right 16px
> header
display flex
padding 0 8px
margin-bottom -16px
color var(--adminDashboardCardFg)
font-size 14px
> span
&:last-child
margin-left auto
opacity 0.7
> span
opacity 0.7
> div
margin-bottom -10px
@media (max-width 1000px)
display block
margin-bottom 26px
> div
&:first-child
margin-right 0
margin-bottom 26px
</style>
+12 -3
View File
@@ -73,6 +73,10 @@
<x-charts ref="charts"/>
</div>
<div class="queue">
<x-queue/>
</div>
<div class="cpu-memory">
<x-cpu-memory :connection="connection"/>
</div>
@@ -86,9 +90,10 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import XCpuMemory from "./cpu-memory.vue";
import XCharts from "./charts.vue";
import XApLog from "./ap-log.vue";
import XCpuMemory from "./dashboard.cpu-memory.vue";
import XQueue from "./dashboard.queue-charts.vue";
import XCharts from "./dashboard.charts.vue";
import XApLog from "./dashboard.ap-log.vue";
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
import MarqueeText from 'vue-marquee-text-component';
import randomColor from 'randomcolor';
@@ -98,6 +103,7 @@ export default Vue.extend({
components: {
XCpuMemory,
XQueue,
XCharts,
XApLog,
MarqueeText
@@ -274,6 +280,9 @@ export default Vue.extend({
> .charts
margin-bottom 16px
> .queue
margin-bottom 16px
> .cpu-memory
margin-bottom 16px
+29 -9
View File
@@ -1,43 +1,58 @@
<template>
<div>
<ui-card>
<template #title><fa :icon="faTerminal"/> {{ $t('federation') }}</template>
<template #title><fa :icon="faTerminal"/> {{ $t('instance') }}</template>
<section class="fit-top">
<ui-input class="target" v-model="target" type="text" @enter="showInstance()">
<span>{{ $t('host') }}</span>
<template #prefix><fa :icon="faServer"/></template>
</ui-input>
<ui-button @click="showInstance()"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
<div class="instance" v-if="instance">
<ui-input :value="instance.host" type="text" readonly>
<span>{{ $t('host') }}</span>
</ui-input>
<ui-horizon-group inputs>
<ui-input :value="instance.host" type="text" readonly>
<span>{{ $t('host') }}</span>
<template #prefix><fa :icon="faServer"/></template>
</ui-input>
<ui-input :value="instance.caughtAt | date" type="text" readonly>
<span>{{ $t('caught-at') }}</span>
<template #prefix><fa :icon="faCrosshairs"/></template>
</ui-input>
</ui-horizon-group>
<ui-horizon-group inputs>
<ui-input :value="instance.notesCount | number" type="text" readonly>
<span>{{ $t('notes') }}</span>
<template #prefix><fa :icon="faEnvelopeOpenText"/></template>
</ui-input>
<ui-input :value="instance.usersCount | number" type="text" readonly>
<span>{{ $t('users') }}</span>
<template #prefix><fa :icon="faUsers"/></template>
</ui-input>
</ui-horizon-group>
<ui-horizon-group inputs>
<ui-input :value="instance.followingCount | number" type="text" readonly>
<span>{{ $t('following') }}</span>
<template #prefix><fa :icon="faCaretDown"/></template>
</ui-input>
<ui-input :value="instance.followersCount | number" type="text" readonly>
<span>{{ $t('followers') }}</span>
<template #prefix><fa :icon="faCaretUp"/></template>
</ui-input>
</ui-horizon-group>
<ui-horizon-group inputs>
<ui-input :value="instance.latestRequestSentAt" type="text" readonly>
<ui-input :value="instance.latestRequestSentAt | date" type="text" readonly>
<span>{{ $t('latest-request-sent-at') }}</span>
<template #prefix><fa :icon="faPaperPlane"/></template>
</ui-input>
<ui-input :value="instance.latestStatus" type="text" readonly>
<span>{{ $t('status') }}</span>
<template #prefix><fa :icon="faTrafficLight"/></template>
</ui-input>
</ui-horizon-group>
<ui-input :value="instance.latestRequestReceivedAt" type="text" readonly>
<ui-input :value="instance.latestRequestReceivedAt | date" type="text" readonly>
<span>{{ $t('latest-request-received-at') }}</span>
<template #prefix><fa :icon="faInbox"/></template>
</ui-input>
<ui-switch v-model="instance.isBlocked" @change="updateInstance()">{{ $t('block') }}</ui-switch>
<ui-switch v-model="instance.isMarkedAsClosed" @change="updateInstance()">{{ $t('marked-as-closed') }}</ui-switch>
@@ -133,7 +148,8 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { faGlobe, faTerminal, faSearch, faMinusCircle, faServer } from '@fortawesome/free-solid-svg-icons';
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
import { faGlobe, faTerminal, faSearch, faMinusCircle, faServer, faCrosshairs, faEnvelopeOpenText, faUsers, faCaretDown, faCaretUp, faTrafficLight, faInbox } from '@fortawesome/free-solid-svg-icons';
import ApexCharts from 'apexcharts';
import * as tinycolor from 'tinycolor2';
@@ -144,19 +160,23 @@ const negate = arr => arr.map(x => -x);
export default Vue.extend({
i18n: i18n('admin/views/federation.vue'),
filters: {
date: v => v ? new Date(v).toLocaleString() : 'N/A'
},
data() {
return {
instance: null,
target: null,
sort: '+lastCommunicatedAt',
state: 'all',
limit: 50,
limit: 100,
instances: [],
chart: null,
chartSrc: 'requests',
chartSpan: 'hour',
chartInstance: null,
faGlobe, faTerminal, faSearch, faMinusCircle, faServer
faGlobe, faTerminal, faSearch, faMinusCircle, faServer, faCrosshairs, faEnvelopeOpenText, faUsers, faCaretDown, faCaretUp, faPaperPlane, faTrafficLight, faInbox
};
},
+4
View File
@@ -6,6 +6,7 @@
<ui-input :value="host" readonly>{{ $t('host') }}</ui-input>
<ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
<ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
<ui-input v-model="iconUrl"><template #icon><fa icon="link"/></template>{{ $t('icon-url') }}</ui-input>
<ui-input v-model="mascotImageUrl"><template #icon><fa icon="link"/></template>{{ $t('logo-url') }}</ui-input>
<ui-input v-model="bannerUrl"><template #icon><fa icon="link"/></template>{{ $t('banner-url') }}</ui-input>
<ui-input v-model="errorImageUrl"><template #icon><fa icon="link"/></template>{{ $t('error-image-url') }}</ui-input>
@@ -157,6 +158,7 @@ export default Vue.extend({
mascotImageUrl: null,
bannerUrl: null,
errorImageUrl: null,
iconUrl: null,
name: null,
description: null,
languages: null,
@@ -207,6 +209,7 @@ export default Vue.extend({
this.mascotImageUrl = meta.mascotImageUrl;
this.bannerUrl = meta.bannerUrl;
this.errorImageUrl = meta.errorImageUrl;
this.iconUrl = meta.iconUrl;
this.name = meta.name;
this.description = meta.description;
this.languages = meta.langs.join(' ');
@@ -267,6 +270,7 @@ export default Vue.extend({
mascotImageUrl: this.mascotImageUrl,
bannerUrl: this.bannerUrl,
errorImageUrl: this.errorImageUrl,
iconUrl: this.iconUrl,
name: this.name,
description: this.description,
langs: this.languages.split(' '),
+5 -6
View File
@@ -4,7 +4,7 @@
<template #title><fa :icon="faStream"/> {{ $t('logs') }}</template>
<section class="fit-top">
<ui-horizon-group inputs>
<ui-input v-model="domain" debounce>
<ui-input v-model="domain" :debounce="true">
<span>{{ $t('domain') }}</span>
</ui-input>
<ui-select v-model="level">
@@ -22,7 +22,7 @@
<code v-for="log in logs" :key="log._id" :class="log.level">
<details>
<summary><mk-time :time="log.createdAt"/> [{{ log.domain.join('.') }}] {{ log.message }}</summary>
<json-viewer v-if="log.data" :value="log.data"></json-viewer>
<vue-json-pretty v-if="log.data" :data="log.data"></vue-json-pretty>
</details>
</code>
</div>
@@ -35,13 +35,13 @@
import Vue from 'vue';
import i18n from '../../i18n';
import { faStream } from '@fortawesome/free-solid-svg-icons';
import JsonViewer from 'vue-json-viewer';
import VueJsonPretty from 'vue-json-pretty';
export default Vue.extend({
i18n: i18n('admin/views/logs.vue'),
components: {
JsonViewer
VueJsonPretty
},
data() {
@@ -85,11 +85,10 @@ export default Vue.extend({
<style lang="stylus" scoped>
.nqjzuvev
white-space nowrap
overflow auto
padding 8px
background #000
color #fff
font-size 14px
> code
display block
+269 -1
View File
@@ -1,26 +1,261 @@
<template>
<div>
<ui-card>
<template #title>{{ $t('operation') }}</template>
<template #title><fa :icon="faChartBar"/> {{ $t('title') }}</template>
<section class="wptihjuy">
<header><fa :icon="faPaperPlane"/> Deliver</header>
<ui-info warn v-if="latestStats && latestStats.deliver.waiting > 0">The queue is jammed.</ui-info>
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
<ui-input :value="latestStats.deliver.activeSincePrevTick | number" type="text" readonly>
<span>Process</span>
<template #prefix><fa :icon="fasPlayCircle"/></template>
<template #suffix>jobs/tick</template>
</ui-input>
<ui-input :value="latestStats.deliver.active | number" type="text" readonly>
<span>Active</span>
<template #prefix><fa :icon="farPlayCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.deliver.waiting | number" type="text" readonly>
<span>Waiting</span>
<template #prefix><fa :icon="faStopCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.deliver.delayed | number" type="text" readonly>
<span>Delayed</span>
<template #prefix><fa :icon="faStopwatch"/></template>
<template #suffix>jobs</template>
</ui-input>
</ui-horizon-group>
<div ref="deliverChart" class="chart"></div>
</section>
<section class="wptihjuy">
<header><fa :icon="faInbox"/> Inbox</header>
<ui-info warn v-if="latestStats && latestStats.inbox.waiting > 0">The queue is jammed.</ui-info>
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
<ui-input :value="latestStats.inbox.activeSincePrevTick | number" type="text" readonly>
<span>Process</span>
<template #prefix><fa :icon="fasPlayCircle"/></template>
<template #suffix>jobs/tick</template>
</ui-input>
<ui-input :value="latestStats.inbox.active | number" type="text" readonly>
<span>Active</span>
<template #prefix><fa :icon="farPlayCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.inbox.waiting | number" type="text" readonly>
<span>Waiting</span>
<template #prefix><fa :icon="faStopCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.inbox.delayed | number" type="text" readonly>
<span>Delayed</span>
<template #prefix><fa :icon="faStopwatch"/></template>
<template #suffix>jobs</template>
</ui-input>
</ui-horizon-group>
<div ref="inboxChart" class="chart"></div>
</section>
<section>
<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>
</section>
</ui-card>
<ui-card>
<template #title><fa :icon="faTasks"/> {{ $t('jobs') }}</template>
<section class="fit-top">
<ui-horizon-group inputs>
<ui-select v-model="domain">
<template #label>{{ $t('queue') }}</template>
<option value="deliver">{{ $t('domains.deliver') }}</option>
<option value="inbox">{{ $t('domains.inbox') }}</option>
</ui-select>
<ui-select v-model="state">
<template #label>{{ $t('state') }}</template>
<option value="delayed">{{ $t('states.delayed') }}</option>
</ui-select>
</ui-horizon-group>
<sequential-entrance animation="entranceFromTop" delay="25">
<div class="xvvuvgsv" v-for="job in jobs">
<b>{{ job.id }}</b>
<template v-if="domain === 'deliver'">
<span>{{ job.data.to }}</span>
</template>
<template v-if="domain === 'inbox'">
<span>{{ job.activity.id }}</span>
</template>
</div>
</sequential-entrance>
<ui-info v-if="jobs.length == jobsLimit">{{ $t('result-is-truncated', { n: jobsLimit }) }}</ui-info>
</section>
</ui-card>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import ApexCharts from 'apexcharts';
import * as tinycolor from 'tinycolor2';
import { faTasks, faInbox, faStopwatch, faPlayCircle as fasPlayCircle } from '@fortawesome/free-solid-svg-icons';
import { faPaperPlane, faStopCircle, faPlayCircle as farPlayCircle, faChartBar } from '@fortawesome/free-regular-svg-icons';
const limit = 200;
export default Vue.extend({
i18n: i18n('admin/views/queue.vue'),
data() {
return {
stats: [],
deliverChart: null,
inboxChart: null,
jobs: [],
jobsLimit: 50,
domain: 'deliver',
state: 'delayed',
faTasks, faPaperPlane, faInbox, faStopwatch, faStopCircle, farPlayCircle, fasPlayCircle, faChartBar
};
},
computed: {
latestStats(): any {
return this.stats[this.stats.length - 1];
}
},
watch: {
stats(stats) {
this.inboxChart.updateSeries([{
name: 'Process',
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
}, {
name: 'Active',
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
}, {
name: 'Waiting',
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
}, {
name: 'Delayed',
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
}]);
this.deliverChart.updateSeries([{
name: 'Process',
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
}, {
name: 'Active',
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
}, {
name: 'Waiting',
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
}, {
name: 'Delayed',
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
}]);
},
domain() {
this.jobs = [];
this.fetchJobs();
},
state() {
this.jobs = [];
this.fetchJobs();
},
},
mounted() {
this.fetchJobs();
const chartOpts = id => ({
chart: {
id,
group: 'queue',
type: 'area',
height: 200,
animations: {
dynamicAnimation: {
enabled: false
}
},
toolbar: {
show: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)',
xaxis: {
lines: {
show: true,
}
},
},
stroke: {
curve: 'straight',
width: 2
},
tooltip: {
enabled: false
},
legend: {
labels: {
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
},
},
series: [] as any,
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
xaxis: {
type: 'numeric',
labels: {
show: false
},
tooltip: {
enabled: false
}
},
yaxis: {
show: false,
min: 0,
}
});
this.inboxChart = new ApexCharts(this.$refs.inboxChart, chartOpts('a'));
this.deliverChart = new ApexCharts(this.$refs.deliverChart, chartOpts('b'));
this.inboxChart.render();
this.deliverChart.render();
const connection = this.$root.stream.useSharedConnection('queueStats');
connection.on('stats', this.onStats);
connection.on('statsLog', this.onStatsLog);
connection.send('requestLog', {
id: Math.random().toString().substr(2, 8),
length: limit
});
this.$once('hook:beforeDestroy', () => {
connection.dispose();
this.inboxChart.destroy();
this.deliverChart.destroy();
});
},
methods: {
async removeAllJobs() {
const process = async () => {
@@ -38,6 +273,39 @@ export default Vue.extend({
});
});
},
onStats(stats) {
this.stats.push(stats);
if (this.stats.length > limit) this.stats.shift();
},
onStatsLog(statsLog) {
for (const stats of statsLog.reverse()) {
this.onStats(stats);
}
},
fetchJobs() {
this.$root.api('admin/queue/jobs', {
domain: this.domain,
state: this.state,
limit: this.jobsLimit
}).then(jobs => {
this.jobs = jobs;
});
},
}
});
</script>
<style lang="stylus" scoped>
.wptihjuy
> .chart
min-height 200px !important
margin 0 -8px
.xvvuvgsv
> b
margin-right 16px
</style>
@@ -69,7 +69,7 @@ export default Vue.extend({
},
plotOptions: {
bar: {
columnWidth: '90%'
columnWidth: '80%'
}
},
grid: {
@@ -4,7 +4,7 @@
<li v-for="user in users" @click="complete(type, user)" @keydown="onKeydown" tabindex="-1">
<img class="avatar" :src="user.avatarUrl" alt=""/>
<span class="name">
<mk-user-name :user="user"/>
<mk-user-name :user="user" :key="user.id"/>
</span>
<span class="username">@{{ user | acct }}</span>
</li>
@@ -3,32 +3,31 @@
<header>
<button v-for="category in categories"
:title="category.text"
@click="go(category.ref)"
@click="go(category)"
:class="{ active: category.isActive }"
>
<fa :icon="category.icon" fixed-width/>
</button>
</header>
<div class="emojis" ref="emojis" @scroll.passive="onScroll">
<section v-for="category in categories" :ref="category.ref">
<header><fa :icon="category.icon" fixed-width/> {{ category.text }}</header>
<div v-if="category.name">
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === category.name)"
:title="emoji[0]"
@click="chosen(emoji[1].char)"
>
<mk-emoji :emoji="emoji[1].char"/>
</button>
</div>
<div v-else>
<button v-for="emoji in customEmojis"
:title="emoji.name"
@click="chosen(`:${emoji.name}:`)"
>
<img :src="emoji.url" :alt="emoji.name"/>
</button>
</div>
</section>
<div class="emojis">
<header><fa :icon="categories.find(x => x.isActive).icon" fixed-width/> {{ categories.find(x => x.isActive).text }}</header>
<div v-if="categories.find(x => x.isActive).name">
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === categories.find(x => x.isActive).name)"
:title="emoji[0]"
@click="chosen(emoji[1].char)"
:key="emoji[0]"
>
<mk-emoji :emoji="emoji[1].char"/>
</button>
</div>
<div v-else>
<button v-for="emoji in customEmojis"
:title="emoji.name"
@click="chosen(`:${emoji.name}:`)"
>
<img :src="emoji.url" :alt="emoji.name"/>
</button>
</div>
</div>
</div>
</template>
@@ -48,55 +47,46 @@ export default Vue.extend({
lib,
customEmojis: [],
categories: [{
ref: 'customEmojiSection',
text: this.$t('custom-emoji'),
icon: faAsterisk,
isActive: true
}, {
name: 'people',
ref: 'peopleSection',
text: this.$t('people'),
icon: ['far', 'laugh'],
isActive: false
}, {
name: 'animals_and_nature',
ref: 'animalsAndNatureSection',
text: this.$t('animals-and-nature'),
icon: faLeaf,
isActive: false
}, {
name: 'food_and_drink',
ref: 'foodAndDrinkSection',
text: this.$t('food-and-drink'),
icon: faUtensils,
isActive: false
}, {
name: 'activity',
ref: 'activitySection',
text: this.$t('activity'),
icon: faFutbol,
isActive: false
}, {
name: 'travel_and_places',
ref: 'travelAndPlacesSection',
text: this.$t('travel-and-places'),
icon: faCity,
isActive: false
}, {
name: 'objects',
ref: 'objectsSection',
text: this.$t('objects'),
icon: faDice,
isActive: false
}, {
name: 'symbols',
ref: 'symbolsSection',
text: this.$t('symbols'),
icon: faHeart,
isActive: false
}, {
name: 'flags',
ref: 'flagsSection',
text: this.$t('flags'),
icon: faFlag,
isActive: false
@@ -109,15 +99,9 @@ export default Vue.extend({
},
methods: {
go(ref) {
this.$refs.emojis.scrollTop = this.$refs[ref][0].offsetTop;
},
onScroll(e) {
for (const x of this.categories) {
const top = e.target.scrollTop;
const el = this.$refs[x.ref][0];
x.isActive = el.offsetTop <= top && el.offsetTop + el.offsetHeight > top;
go(category) {
for (const c of this.categories) {
c.isActive = c.name === category.name;
}
},
@@ -156,47 +140,46 @@ export default Vue.extend({
overflow-y auto
overflow-x hidden
> section
> header
position sticky
top 0
left 0
z-index 1
padding 8px
background var(--faceHeader)
color var(--text)
font-size 12px
> header
position sticky
top 0
left 0
z-index 1
padding 8px
background var(--faceHeader)
color var(--text)
font-size 12px
> div
display grid
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
gap 4px
padding 8px
> div
display grid
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
gap 4px
padding 8px
> button
padding 0
width 100%
> button
padding 0
width 100%
&:before
content ''
display block
width 1px
height 0
padding-bottom 100%
&:hover
> *
transform scale(1.2)
transition transform 0s
&:before
content ''
display block
width 1px
height 0
padding-bottom 100%
&:hover
> *
position absolute
top 0
left 0
width 100%
height 100%
font-size 28px
transition transform 0.2s ease
pointer-events none
transform scale(1.2)
transition transform 0s
> *
position absolute
top 0
left 0
width 100%
height 100%
font-size 28px
transition transform 0.2s ease
pointer-events none
</style>
@@ -1,5 +1,5 @@
<template>
<a class="a" href="https://github.com/syuilo/misskey" target="_blank" title="View source on GitHub">
<a class="a" :href="repo" target="_blank" title="View source on GitHub">
<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="aria-hidden">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor"></path>
@@ -8,9 +8,25 @@
</a>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
data() {
return {
repositoryUrl: 'https://github.com/syuilo/misskey'
};
},
created() {
this.$root.getMeta().then(meta => {
if (meta.maintainer)
this.repositoryUrl = meta.maintainer.repository_url;
});
}
});
</script>
<style lang="stylus" scoped>
.a
display block
@@ -5,17 +5,17 @@
<div style="overflow: hidden; line-height: 28px;">
<p class="turn" v-if="!iAmPlayer && !game.isEnded">
<mfm :text="$t('@.reversi.turn-of', { name: $options.filters.userName(turnUser) })" :should-break="false" :plain-text="true" :custom-emojis="turnUser.emojis"/>
<mfm :key="'turn:' + $options.filters.userName(turnUser)" :text="$t('@.reversi.turn-of', { name: $options.filters.userName(turnUser) })" :should-break="false" :plain-text="true" :custom-emojis="turnUser.emojis"/>
<mk-ellipsis/>
</p>
<p class="turn" v-if="logPos != logs.length">
<mfm :text="$t('@.reversi.past-turn-of', { name: $options.filters.userName(turnUser) })" :should-break="false" :plain-text="true" :custom-emojis="turnUser.emojis"/>
<mfm :key="'past-turn-of:' + $options.filters.userName(turnUser)" :text="$t('@.reversi.past-turn-of', { name: $options.filters.userName(turnUser) })" :should-break="false" :plain-text="true" :custom-emojis="turnUser.emojis"/>
</p>
<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">{{ $t('@.reversi.opponent-turn') }}<mk-ellipsis/></p>
<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">{{ $t('@.reversi.my-turn') }}</p>
<p class="result" v-if="game.isEnded && logPos == logs.length">
<template v-if="game.winner">
<mfm :text="$t('@.reversi.won', { name: $options.filters.userName(game.winner) })" :should-break="false" :plain-text="true" :custom-emojis="game.winner.emojis"/>
<mfm :key="'won'" :text="$t('@.reversi.won', { name: $options.filters.userName(game.winner) })" :should-break="false" :plain-text="true" :custom-emojis="game.winner.emojis"/>
<span v-if="game.surrendered != null"> ({{ $t('surrendered') }})</span>
</template>
<template v-else>{{ $t('@.reversi.drawn') }}</template>
@@ -12,21 +12,54 @@
</li>
</ul>
<button class="add" v-if="choices.length < 10" @click="add">{{ $t('add') }}</button>
<button class="add" v-else disabled>{{ $t('no-more') }}</button>
<button class="destroy" @click="destroy" :title="$t('destroy')">
<fa icon="times"/>
</button>
<section>
<ui-switch v-model="multiple">{{ $t('multiple') }}</ui-switch>
<div>
<ui-select v-model="expiration">
<template #label>{{ $t('expiration') }}</template>
<option value="infinite">{{ $t('infinite') }}</option>
<option value="at">{{ $t('at') }}</option>
<option value="after">{{ $t('after') }}</option>
</ui-select>
<section v-if="expiration === 'at'">
<ui-input v-model="atDate" type="date">{{ $t('deadline-date') }}</ui-input>
<ui-input v-model="atTime" type="time">{{ $t('deadline-time') }}</ui-input>
</section>
<section v-if="expiration === 'after'">
<ui-input v-model="after" type="number">{{ $t('interval') }}</ui-input>
<ui-select v-model="unit">
<template #label>{{ $t('unit') }}</template>
<option value="second">{{ $t('second') }}</option>
<option value="minute">{{ $t('minute') }}</option>
<option value="hour">{{ $t('hour') }}</option>
<option value="day">{{ $t('day') }}</option>
</ui-select>
</section>
</div>
</section>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as moment from 'moment';
import i18n from '../../../i18n';
import { erase } from '../../../../../prelude/array';
export default Vue.extend({
i18n: i18n('common/views/components/poll-editor.vue'),
data() {
return {
choices: ['', '']
choices: ['', ''],
multiple: false,
expiration: 'infinite',
atDate: moment().add(1, 'day').toISOString().split('T')[0],
atTime: '00:00',
after: 0,
unit: 'second'
};
},
watch: {
@@ -55,15 +88,46 @@ export default Vue.extend({
},
get() {
const at = () => {
const [date] = moment(this.atDate).toISOString().split('T');
const [hour, minute] = this.atTime.split(':');
return moment(`${date}T${hour}:${minute}Z`).valueOf();
};
const after = () => {
let base = parseInt(this.after);
switch (this.unit) {
case 'day': base *= 24;
case 'hour': base *= 60;
case 'minute': base *= 60;
case 'second': return base *= 1000;
default: return null;
}
};
return {
choices: erase('', this.choices)
}
choices: erase('', this.choices),
multiple: this.multiple,
...(
this.expiration === 'at' ? { expiresAt: at() } :
this.expiration === 'after' ? { expiredAfter: after() } : {})
};
},
set(data) {
if (data.choices.length == 0) return;
this.choices = data.choices;
if (data.choices.length == 1) this.choices = this.choices.concat('');
this.multiple = data.multiple;
if (data.expiresAt) {
this.expiration = 'at';
this.atDate = this.atTime = data.expiresAt;
} else if (typeof data.expiredAfter === 'number') {
this.expiration = 'after';
this.after = data.expiredAfter;
} else {
this.expiration = 'infinite';
}
}
}
});
@@ -128,6 +192,7 @@ export default Vue.extend({
margin 8px 0 0 0
vertical-align top
color var(--primary)
z-index 1
> .destroy
position absolute
@@ -142,4 +207,23 @@ export default Vue.extend({
&:active
color var(--primaryDarken30)
> section
margin 16px 0 -16px 0
> div
margin 0 8px
&:last-child
flex 1 0 auto
> section
align-items center
display flex
margin -32px 0 0
> :first-child
margin-right 16px
> .ui-input
flex 1 0 auto
</style>
+38 -11
View File
@@ -1,8 +1,8 @@
<template>
<div class="mk-poll" :data-is-voted="isVoted">
<div class="mk-poll" :data-done="closed || isVoted">
<ul>
<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? $t('vote-to').replace('{}', choice.text) : ''">
<div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div>
<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!closed && !isVoted ? $t('vote-to').replace('{}', choice.text) : ''">
<div class="backdrop" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div>
<span>
<template v-if="choice.isVoted"><fa icon="check"/></template>
<mfm :text="choice.text" :should-break="false" :plain-text="true" :custom-emojis="note.emojis"/>
@@ -10,11 +10,13 @@
</span>
</li>
</ul>
<p v-if="total > 0">
<span>{{ $t('total-users').replace('{}', total) }}</span>
<span></span>
<a v-if="!isVoted" @click="toggleShowResult">{{ showResult ? $t('vote') : $t('show-result') }}</a>
<p>
<span>{{ $t('total-votes').replace('{}', total) }}</span>
<span> · </span>
<a v-if="!closed && !isVoted" @click="toggleShowResult">{{ showResult ? $t('vote') : $t('show-result') }}</a>
<span v-if="isVoted">{{ $t('voted') }}</span>
<span v-else-if="closed">{{ $t('closed') }}</span>
<span v-if="remaining > 0"> · {{ timer }}</span>
</p>
</div>
</template>
@@ -28,6 +30,7 @@ export default Vue.extend({
props: ['note'],
data() {
return {
remaining: -1,
showResult: false
};
},
@@ -38,19 +41,43 @@ export default Vue.extend({
total(): number {
return sum(this.poll.choices.map(x => x.votes));
},
closed(): boolean {
return !this.remaining;
},
timer(): string {
return this.$t(
this.remaining > 86400 ? 'remaining-days' :
this.remaining > 3600 ? 'remaining-hours' :
this.remaining > 60 ? 'remaining-minutes' : 'remaining-seconds')
.replace('{s}', Math.floor(this.remaining % 60))
.replace('{m}', Math.floor(this.remaining / 60) % 60)
.replace('{h}', Math.floor(this.remaining / 3600) % 24)
.replace('{d}', Math.floor(this.remaining / 86400));
},
isVoted(): boolean {
return this.poll.choices.some(c => c.isVoted);
return !this.poll.multiple && this.poll.choices.some(c => c.isVoted);
}
},
created() {
this.showResult = this.isVoted;
if (this.note.poll.expiresAt) {
const update = () => {
if (this.remaining = Math.floor(Math.max(new Date(this.note.poll.expiresAt).getTime() - Date.now(), 0) / 1000))
requestAnimationFrame(update);
else
this.showResult = true;
};
update();
}
},
methods: {
toggleShowResult() {
this.showResult = !this.showResult;
},
vote(id) {
if (this.poll.choices.some(c => c.isVoted)) return;
if (this.closed || !this.poll.multiple && this.poll.choices.some(c => c.isVoted)) return;
this.$root.api('notes/polls/vote', {
noteId: this.note.id,
choice: id
@@ -61,7 +88,7 @@ export default Vue.extend({
Vue.set(c, 'isVoted', true);
}
}
this.showResult = true;
if (!this.showResult) this.showResult = !this.poll.multiple;
});
}
}
@@ -114,7 +141,7 @@ export default Vue.extend({
a
color inherit
&[data-is-voted]
&[data-done]
> ul > li
cursor default
@@ -93,12 +93,17 @@ export default Vue.extend({
},
plotOptions: {
bar: {
columnWidth: '90%'
columnWidth: '80%'
}
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
borderColor: 'rgba(0, 0, 0, 0.1)',
xaxis: {
lines: {
show: true,
}
},
},
tooltip: {
shared: true,
@@ -51,12 +51,12 @@
<template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
</ui-input>
<ui-button @click="save(true)">{{ $t('save') }}</ui-button>
<ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
</ui-form>
</section>
<section>
<header>{{ $t('advanced') }}</header>
<header><fa :icon="faCogs"/> {{ $t('advanced') }}</header>
<div>
<ui-switch v-model="isCat" @change="save(false)">{{ $t('is-cat') }}</ui-switch>
@@ -66,7 +66,7 @@
</section>
<section>
<header>{{ $t('privacy') }}</header>
<header><fa :icon="faUnlockAlt"/> {{ $t('privacy') }}</header>
<div>
<ui-switch v-model="isLocked" @change="save(false)">{{ $t('is-locked') }}</ui-switch>
@@ -76,7 +76,7 @@
</section>
<section v-if="enableEmail">
<header>{{ $t('email') }}</header>
<header><fa :icon="faEnvelope"/> {{ $t('email') }}</header>
<div>
<template v-if="$store.state.i.email != null">
@@ -84,12 +84,12 @@
<ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info>
</template>
<ui-input v-model="email" type="email"><span>{{ $t('email-address') }}</span></ui-input>
<ui-button @click="updateEmail()">{{ $t('save') }}</ui-button>
<ui-button @click="updateEmail()"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
</div>
</section>
<section>
<header>{{ $t('export') }}</header>
<header><fa :icon="faBoxes"/> {{ $t('export-and-import') }}</header>
<div>
<ui-select v-model="exportTarget">
@@ -97,8 +97,12 @@
<option value="following">{{ $t('export-targets.following-list') }}</option>
<option value="mute">{{ $t('export-targets.mute-list') }}</option>
<option value="blocking">{{ $t('export-targets.blocking-list') }}</option>
<option value="user-lists">{{ $t('export-targets.user-lists') }}</option>
</ui-select>
<ui-button @click="doExport()"><fa :icon="faDownload"/> {{ $t('export') }}</ui-button>
<ui-horizon-group class="fit-bottom">
<ui-button @click="doExport()"><fa :icon="faDownload"/> {{ $t('export') }}</ui-button>
<ui-button @click="doImport()" :disabled="!['following', 'user-lists'].includes(exportTarget)"><fa :icon="faUpload"/> {{ $t('import') }}</ui-button>
</ui-horizon-group>
</div>
</section>
@@ -118,7 +122,8 @@ import { apiUrl, host } from '../../../../config';
import { toUnicode } from 'punycode';
import langmap from 'langmap';
import { unique } from '../../../../../../prelude/array';
import { faDownload } from '@fortawesome/free-solid-svg-icons';
import { faDownload, faUpload, faUnlockAlt, faBoxes, faCogs } from '@fortawesome/free-solid-svg-icons';
import { faSave, faEnvelope } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/components/profile-editor.vue'),
@@ -147,7 +152,7 @@ export default Vue.extend({
avatarUploading: false,
bannerUploading: false,
exportTarget: 'notes',
faDownload
faDownload, faUpload, faSave, faEnvelope, faUnlockAlt, faBoxes, faCogs
};
},
@@ -284,6 +289,7 @@ export default Vue.extend({
this.exportTarget == 'following' ? 'i/export-following' :
this.exportTarget == 'mute' ? 'i/export-mute' :
this.exportTarget == 'blocking' ? 'i/export-blocking' :
this.exportTarget == 'user-lists' ? 'i/export-user-lists' :
null, {});
this.$root.dialog({
@@ -292,6 +298,22 @@ export default Vue.extend({
});
},
doImport() {
this.$chooseDriveFile().then(file => {
this.$root.api(
this.exportTarget == 'following' ? 'i/import-following' :
this.exportTarget == 'user-lists' ? 'i/import-user-lists' :
null, {
fileId: file.id
});
this.$root.dialog({
type: 'info',
text: this.$t('import-requested')
});
});
},
async deleteAccount() {
const { canceled: canceled, result: password } = await this.$root.dialog({
title: this.$t('enter-password'),
@@ -386,7 +386,7 @@ export default Vue.extend({
height: 50px;
background-color: #83D8FF;
border-radius: 90px - 6;
transition: background-color 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: background-color 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
&:before {
content: 'Light';
@@ -418,14 +418,14 @@ export default Vue.extend({
background-color: #FFCF96;
border-radius: 50px;
box-shadow: 0 2px 6px rgba(0,0,0,.3);
transition: all 400ms cubic-bezier(0.68, -0.55, 0.265, 1.55);
transition: all 400ms cubic-bezier(0.68, -0.55, 0.265, 1.55) !important;
transform: rotate(-45deg);
.crater {
position: absolute;
background-color: #E8CDA5;
opacity: 0;
transition: opacity 200ms ease-in-out;
transition: opacity 200ms ease-in-out !important;
border-radius: 100%;
}
@@ -454,7 +454,7 @@ export default Vue.extend({
.star {
position: absolute;
background-color: #ffffff;
transition: all 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
border-radius: 50%;
}
@@ -486,7 +486,7 @@ export default Vue.extend({
.star--5,
.star--6 {
opacity: 0;
transition: all 300ms 0 cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 300ms 0 cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.star--4 {
@@ -559,13 +559,13 @@ export default Vue.extend({
transform: translate3d(0,0,0);
}
.star--4 {
transition: all 300ms 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 300ms 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.star--5 {
transition: all 300ms 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 300ms 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.star--6 {
transition: all 300ms 400ms cubic-bezier(0.445, 0.05, 0.55, 0.95);
transition: all 300ms 400ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
}
}
@@ -1,5 +1,6 @@
<template>
<div v-if="playerEnabled" class="player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`">
<button class="disablePlayer" @click="playerEnabled = false" :title="$t('disable-player')"><fa icon="times"/></button>
<iframe :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
</div>
<div v-else-if="tweetUrl && detail" class="twitter">
@@ -126,6 +127,22 @@ export default Vue.extend({
position relative
width 100%
> button
position absolute
top -1.5em
right 0
font-size 1em
width 1.5em
height 1.5em
padding 0
margin 0
color var(--text)
background rgba(128, 128, 128, 0.2)
opacity 0.7
&:hover
opacity 0.9
> iframe
height 100%
left 0
@@ -6,7 +6,7 @@
<div class="xroyrflcmhhtmlwmyiwpfqiirqokfueb">
<div ref="chart" class="chart"></div>
<x-hashtag-tl :tag-tl="tagTl" class="tl"/>
<x-hashtag-tl :tag-tl="tagTl" class="tl" :key="JSON.stringify(tagTl)"/>
</div>
</x-column>
</template>
@@ -172,7 +172,7 @@ export default Vue.extend({
},
plotOptions: {
bar: {
columnWidth: '90%'
columnWidth: '80%'
}
},
grid: {
@@ -26,6 +26,7 @@
<option value="hashtags">{{ $t('@.widgets.hashtags') }}</option>
<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
<option value="server">{{ $t('@.widgets.server') }}</option>
<option value="queue">{{ $t('@.widgets.queue') }}</option>
<option value="nav">{{ $t('@.widgets.nav') }}</option>
<option value="tips">{{ $t('@.widgets.tips') }}</option>
</select>
@@ -3,7 +3,7 @@
<h1>{{ $t('share-with', { name }) }}</h1>
<div>
<mk-signin v-if="!$store.getters.isSignedIn"/>
<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
<mk-post-form v-else-if="!posted" :initial-text="template" :instant="true" @posted="posted = true"/>
<p v-if="posted" class="posted"><fa icon="check"/></p>
</div>
<ui-button class="close" v-if="posted" @click="close">{{ $t('@.close') }}</ui-button>
@@ -20,9 +20,21 @@ export default Vue.extend({
return {
name: null,
posted: false,
text: new URLSearchParams(location.search).get('text')
text: new URLSearchParams(location.search).get('text'),
url: new URLSearchParams(location.search).get('url'),
title: new URLSearchParams(location.search).get('title'),
};
},
computed: {
template(): string {
let t = '';
if (this.title && this.url) t += `【[${this.title}](${this.url})】\n`;
if (this.title && !this.url) t += `${this.title}\n`;
if (this.text) t += `${this.text}\n`;
if (!this.title && this.url) t += `${this.url}`;
return t.trim();
}
},
methods: {
close() {
window.close();
@@ -31,3 +31,4 @@ Vue.component('mkw-version', wVersion);
Vue.component('mkw-hashtags', wHashtags);
Vue.component('mkw-instance', wInstance);
Vue.component('mkw-post-form', wPostForm);
Vue.component('mkw-queue', () => import('./queue.vue').then(m => m.default));
@@ -0,0 +1,170 @@
<template>
<div>
<ui-container :show-header="!props.compact">
<template #header><fa :icon="faTasks"/>Queue</template>
<div class="mntrproz">
<div>
<b>In</b>
<span v-if="latestStats">{{ latestStats.inbox.activeSincePrevTick | number }} / {{ latestStats.inbox.delayed | number }}</span>
<div ref="in"></div>
</div>
<div>
<b>Out</b>
<span v-if="latestStats">{{ latestStats.deliver.activeSincePrevTick | number }} / {{ latestStats.deliver.delayed | number }}</span>
<div ref="out"></div>
</div>
</div>
</ui-container>
</div>
</template>
<script lang="ts">
import define from '../../define-widget';
import { faTasks } from '@fortawesome/free-solid-svg-icons';
import ApexCharts from 'apexcharts';
export default define({
name: 'queue',
props: () => ({
compact: false
})
}).extend({
data() {
return {
stats: [],
inChart: null,
outChart: null,
faTasks
};
},
watch: {
stats(stats) {
this.inChart.updateSeries([{
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
}, {
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
}, {
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
}, {
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
}]);
this.outChart.updateSeries([{
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
}, {
type: 'area',
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
}, {
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
}, {
type: 'line',
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
}]);
}
},
computed: {
latestStats(): any {
return this.stats[this.stats.length - 1];
}
},
mounted() {
const chartOpts = {
chart: {
type: 'area',
height: 70,
animations: {
dynamicAnimation: {
enabled: false
}
},
sparkline: {
enabled: true,
}
},
tooltip: {
enabled: false
},
stroke: {
curve: 'straight',
width: 1
},
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
series: [{ data: [] }, { data: [] }, { data: [] }, { data: [] }] as any,
yaxis: {
min: 0,
}
};
this.inChart = new ApexCharts(this.$refs.in, chartOpts);
this.outChart = new ApexCharts(this.$refs.out, chartOpts);
this.inChart.render();
this.outChart.render();
const connection = this.$root.stream.useSharedConnection('queueStats');
connection.on('stats', this.onStats);
connection.on('statsLog', this.onStatsLog);
connection.send('requestLog', {
id: Math.random().toString().substr(2, 8),
length: 50
});
this.$once('hook:beforeDestroy', () => {
connection.dispose();
this.inChart.destroy();
this.outChart.destroy();
});
},
methods: {
func() {
this.props.compact = !this.props.compact;
this.save();
},
onStats(stats) {
this.stats.push(stats);
if (this.stats.length > 50) this.stats.shift();
},
onStatsLog(statsLog) {
for (const stats of statsLog.reverse()) {
this.onStats(stats);
}
}
}
});
</script>
<style lang="stylus" scoped>
.mntrproz
display flex
padding 4px
> div
width 50%
padding 4px
> b
display block
font-size 12px
color var(--text)
> span
position absolute
top 4px
right 4px
opacity 0.7
font-size 12px
color var(--text)
</style>
+1 -1
View File
@@ -18,7 +18,7 @@ import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue';
import MkMessagingRoom from './views/pages/messaging-room.vue';
import MkReversi from './views/pages/games/reversi.vue';
import MkShare from './views/pages/share.vue';
import MkShare from '../common/views/pages/share.vue';
import MkFollow from '../common/views/pages/follow.vue';
import MkNotFound from '../common/views/pages/not-found.vue';
import MkSettings from './views/pages/settings.vue';
@@ -129,9 +129,9 @@ export default Vue.extend({
mounted() {
// Get replies
if (!this.compact) {
this.$root.api('notes/replies', {
this.$root.api('notes/children', {
noteId: this.appearNote.id,
limit: 8
limit: 30
}).then(replies => {
this.replies = replies;
});
@@ -123,9 +123,9 @@ export default Vue.extend({
created() {
if (this.detail) {
this.$root.api('notes/replies', {
this.$root.api('notes/children', {
noteId: this.appearNote.id,
limit: 8
limit: 30
}).then(replies => {
this.replies = replies;
});
@@ -115,6 +115,8 @@ export default Vue.extend({
uploadings: [],
poll: false,
pollChoices: [],
pollMultiple: false,
pollExpiration: [],
useCw: false,
cw: null,
geo: null,
@@ -295,7 +297,10 @@ export default Vue.extend({
},
onPollUpdate() {
this.pollChoices = this.$refs.poll.get().choices;
const got = this.$refs.poll.get();
this.pollChoices = got.choices;
this.pollMultiple = got.multiple;
this.pollExpiration = [got.expiration, got.expiresAt || got.expiredAfter];
this.saveDraft();
},
@@ -475,7 +480,7 @@ export default Vue.extend({
});
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.type == 'hashtag').map(x => x.hashtag);
const hashtags = parse(this.text).filter(x => x.node.type === 'hashtag').map(x => x.node.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
@@ -27,6 +27,7 @@
<option value="hashtags">{{ $t('@.widgets.hashtags') }}</option>
<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
<option value="server">{{ $t('@.widgets.server') }}</option>
<option value="queue">{{ $t('@.widgets.queue') }}</option>
<option value="nav">{{ $t('@.widgets.nav') }}</option>
<option value="tips">{{ $t('@.widgets.tips') }}</option>
</select>
@@ -1,66 +0,0 @@
<template>
<div class="pptjhabgjtt7kwskbfv4y3uml6fpuhmr">
<h1>{{ this.$t('share-with', { name }) }}</h1>
<div>
<mk-signin v-if="!$store.getters.isSignedIn"/>
<mk-post-form v-else-if="!posted" :initial-text="text" :instant="true" @posted="posted = true"/>
<p v-if="posted" class="posted"><fa icon="check"/></p>
</div>
<button v-if="posted" class="ui button" @click="close">{{ $t('@.close') }}</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/pages/share.vue'),
data() {
return {
name: null,
posted: false,
text: new URLSearchParams(location.search).get('text')
};
},
methods: {
close() {
window.close();
}
},
mounted() {
this.$root.getMeta().then(meta => {
this.name = meta.name;
});
}
});
</script>
<style lang="stylus" scoped>
.pptjhabgjtt7kwskbfv4y3uml6fpuhmr
padding 16px
> h1
margin 0 0 8px 0
color #555
font-size 20px
text-align center
> div
max-width 500px
margin 0 auto
background #fff
border solid 1px rgba(#000, 0.1)
border-radius 6px
overflow hidden
> .posted
display block
margin 0
padding 64px
text-align center
> button
display block
margin 16px auto
</style>
+2 -2
View File
@@ -16,11 +16,11 @@ import App from './app.vue';
import checkForUpdate from './common/scripts/check-for-update';
import MiOS from './mios';
import { version, codename, lang, locale } from './config';
import { builtinThemes, lightTheme, applyTheme } from './theme';
import { builtinThemes, applyTheme, darkTheme } from './theme';
import Dialog from './common/views/components/dialog.vue';
if (localStorage.getItem('theme') == null) {
applyTheme(lightTheme);
applyTheme(darkTheme);
}
//#region FontAwesome
+7 -3
View File
@@ -172,7 +172,11 @@ export default class MiOS extends EventEmitter {
callback();
// Init service worker
if (this.shouldRegisterSw) this.registerSw();
if (this.shouldRegisterSw) {
this.getMeta().then(data => {
this.registerSw(data.swPublickey);
});
}
};
// キャッシュがあったとき
@@ -302,7 +306,7 @@ export default class MiOS extends EventEmitter {
* Register service worker
*/
@autobind
private registerSw() {
private registerSw(swPublickey) {
// Check whether service worker and push manager supported
const isSwSupported =
('serviceWorker' in navigator) && ('PushManager' in window);
@@ -328,7 +332,7 @@ export default class MiOS extends EventEmitter {
// A public key your push server will use to send
// messages to client apps via a push server.
applicationServerKey: urlBase64ToUint8Array(this.meta.data.swPublickey)
applicationServerKey: urlBase64ToUint8Array(swPublickey)
};
// Subscribe push notification
+1 -1
View File
@@ -26,7 +26,7 @@ import MkUserLists from './views/pages/user-lists.vue';
import MkUserList from './views/pages/user-list.vue';
import MkReversi from './views/pages/games/reversi.vue';
import MkTag from './views/pages/tag.vue';
import MkShare from './views/pages/share.vue';
import MkShare from '../common/views/pages/share.vue';
import MkFollow from '../common/views/pages/follow.vue';
import MkNotFound from '../common/views/pages/not-found.vue';
@@ -135,9 +135,9 @@ export default Vue.extend({
methods: {
fetchReplies() {
if (this.compact) return;
this.$root.api('notes/replies', {
this.$root.api('notes/children', {
noteId: this.appearNote.id,
limit: 8
limit: 30
}).then(replies => {
this.replies = replies;
});
@@ -115,9 +115,9 @@ export default Vue.extend({
created() {
if (this.detail) {
this.$root.api('notes/replies', {
this.$root.api('notes/children', {
noteId: this.appearNote.id,
limit: 8
limit: 30
}).then(replies => {
this.replies = replies;
});
@@ -105,6 +105,7 @@ export default Vue.extend({
files: [],
poll: false,
pollChoices: [],
pollMultiple: false,
geo: null,
visibility: 'public',
visibleUsers: [],
@@ -273,7 +274,9 @@ export default Vue.extend({
},
onPollUpdate() {
this.pollChoices = this.$refs.poll.get().choices;
const got = this.$refs.poll.get();
this.pollChoices = got.choices;
this.pollMultiple = got.multiple;
},
upload(file) {
@@ -364,7 +367,7 @@ export default Vue.extend({
});
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.type == 'hashtag').map(x => x.hashtag);
const hashtags = parse(this.text).filter(x => x.node.type === 'hashtag').map(x => x.node.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
@@ -19,6 +19,7 @@
<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
<option value="version">{{ $t('@.widgets.version') }}</option>
<option value="server">{{ $t('@.widgets.server') }}</option>
<option value="queue">{{ $t('@.widgets.queue') }}</option>
<option value="memo">{{ $t('@.widgets.memo') }}</option>
<option value="nav">{{ $t('@.widgets.nav') }}</option>
<option value="tips">{{ $t('@.widgets.tips') }}</option>
+3 -3
View File
@@ -43,11 +43,11 @@ export const builtinThemes = [
];
export function applyTheme(theme: Theme, persisted = true) {
document.documentElement.classList.add('change-theme');
document.documentElement.classList.add('changing-theme');
setTimeout(() => {
document.documentElement.classList.remove('change-theme');
}, 500);
document.documentElement.classList.remove('changing-theme');
}, 1000);
// Deep copy
const _theme = JSON.parse(JSON.stringify(theme));
+6 -1
View File
@@ -43,6 +43,11 @@
}
],
"share_target": {
"url_template": "share?text=【{title}】%0A{text}%0A{url}"
"action": "/share/",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
}
}
+2 -4
View File
@@ -20,11 +20,9 @@ html, body
text-size-adjust 100%
font-family sans-serif
html.change-theme
html.changing-theme
&, *
transition-property all
transition-duration 0.5s
transition-timing-function ease
transition background 1s ease !important
a
text-decoration none
+2
View File
@@ -19,6 +19,8 @@ export type Source = {
host: string;
port: number;
pass: string;
db?: number;
prefix?: string;
};
elasticsearch: {
host: string;
+61
View File
@@ -0,0 +1,61 @@
import * as Deque from 'double-ended-queue';
import Xev from 'xev';
import { deliverQueue, inboxQueue } from '../queue';
const ev = new Xev();
const interval = 3000;
/**
* Report queue stats regularly
*/
export default function() {
const log = new Deque<any>();
ev.on('requestQueueStatsLog', x => {
ev.emit(`queueStatsLog:${x.id}`, log.toArray().slice(0, x.length || 50));
});
let activeDeliverJobs = 0;
let activeInboxJobs = 0;
deliverQueue.on('global:active', () => {
activeDeliverJobs++;
});
inboxQueue.on('global:active', () => {
activeInboxJobs++;
});
async function tick() {
const deliverJobCounts = await deliverQueue.getJobCounts();
const inboxJobCounts = await inboxQueue.getJobCounts();
const stats = {
deliver: {
activeSincePrevTick: activeDeliverJobs,
active: deliverJobCounts.active,
waiting: deliverJobCounts.waiting,
delayed: deliverJobCounts.delayed
},
inbox: {
activeSincePrevTick: activeInboxJobs,
active: inboxJobCounts.active,
waiting: inboxJobCounts.waiting,
delayed: inboxJobCounts.delayed
}
};
ev.emit('queueStats', stats);
log.unshift(stats);
if (log.length > 200) log.pop();
activeDeliverJobs = 0;
activeInboxJobs = 0;
}
tick();
setInterval(tick, interval);
}
+3 -1
View File
@@ -5,6 +5,8 @@ export default config.redis ? redis.createClient(
config.redis.port,
config.redis.host,
{
auth_pass: config.redis.pass
auth_pass: config.redis.pass,
prefix: config.redis.prefix,
db: config.redis.db || 0
}
) : null;
+13 -12
View File
@@ -16,6 +16,7 @@ import Xev from 'xev';
import Logger from './services/logger';
import serverStats from './daemons/server-stats';
import notesStats from './daemons/notes-stats';
import queueStats from './daemons/queue-stats';
import loadConfig from './config/load';
import { Config } from './config/types';
import { lessThan } from './prelude/array';
@@ -50,6 +51,7 @@ function main() {
if (program.daemons) {
serverStats();
notesStats();
queueStats();
}
}
@@ -68,13 +70,16 @@ function greet() {
console.log(' |_|_|_|_|___|___|_,_|___|_ |');
console.log(' ' + chalk.gray(v) + (' |___|\n'.substr(v.length)));
//#endregion
}
console.log(chalk`${os.hostname()} {gray (PID: ${process.pid.toString()})}`);
console.log(' Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
console.log(chalk.keyword('orange')(' If you like Misskey, please donate to support development. https://www.patreon.com/syuilo'));
console.log('');
console.log(chalk`< ${os.hostname()} {gray (PID: ${process.pid.toString()})} >`);
}
bootLogger.info('Welcome to Misskey!');
bootLogger.info(`Misskey v${pkg.version}`, null, true);
bootLogger.info('Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
}
/**
@@ -114,9 +119,6 @@ async function masterMain() {
await spawnWorkers(config.clusterLimit);
}
// start queue
require('./queue').default();
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);
}
@@ -127,6 +129,9 @@ async function workerMain() {
// start server
await require('./server').default();
// start job queue
require('./queue').default();
if (cluster.isWorker) {
// Send a 'ready' message to parent process
process.send('ready');
@@ -147,13 +152,9 @@ async function queueMain() {
bootLogger.succ('Misskey initialized');
// start processor
const queue = require('./queue').default();
require('./queue').default();
if (queue) {
bootLogger.succ('Queue started', null, true);
} else {
bootLogger.error('Queue not available');
}
bootLogger.succ('Queue started', null, true);
}
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
+5 -3
View File
@@ -1,5 +1,6 @@
import { parseFragment, DefaultTreeDocumentFragment } from 'parse5';
import { URL } from 'url';
import { urlRegex } from './prelude';
export function fromHtml(html: string): string {
if (html == null) return null;
@@ -14,7 +15,7 @@ export function fromHtml(html: string): string {
return text.trim();
function getText(node: any) {
function getText(node: any): string {
if (node.nodeName == '#text') return node.value;
if (node.childNodes) {
@@ -38,10 +39,11 @@ export function fromHtml(html: string): string {
const txt = getText(node);
const rel = node.attrs.find((x: any) => x.name == 'rel');
const href = node.attrs.find((x: any) => x.name == 'href');
const isHashtag = rel && rel.value.match('tag') !== null;
// ハッシュタグ / hrefがない / txtがURL
if ((rel && rel.value.match('tag') !== null) || !href || href.value == txt) {
text += txt;
if (isHashtag || !href || href.value == txt) {
text += isHashtag || txt.match(urlRegex) ? txt : `<${txt}>`;
// メンション
} else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) {
const part = txt.split('@');
+12 -5
View File
@@ -1,5 +1,5 @@
import * as P from 'parsimmon';
import { createLeaf, createTree } from './types';
import { createLeaf, createTree, urlRegex } from './prelude';
import { takeWhile, cumulativeSum } from '../prelude/array';
import parseAcct from '../misc/acct/parse';
import { toUnicode } from 'punycode';
@@ -142,7 +142,7 @@ export const mfmLanguage = P.createLanguage({
},
hashtag: () => P((input, i) => {
const text = input.substr(i);
const match = text.match(/^#([^\s\.,!\?'"#:\/]+)/i);
const match = text.match(/^#([^\s\.,!\?'"#:\/\[\]]+)/i);
if (!match) return P.makeFailure(i, 'not a hashtag');
let hashtag = match[1];
hashtag = removeOrphanedBrackets(hashtag);
@@ -154,9 +154,16 @@ export const mfmLanguage = P.createLanguage({
url: () => {
return P((input, i) => {
const text = input.substr(i);
const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/);
if (!match) return P.makeFailure(i, 'not a url');
let url = match[0];
const match = text.match(urlRegex);
let url: string;
if (!match) {
const match = text.match(/^<(https?:\/\/.*?)>/);
if (!match)
return P.makeFailure(i, 'not a url');
url = match[1];
i += 2;
} else
url = match[0];
url = removeOrphanedBrackets(url);
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
+1 -1
View File
@@ -1,6 +1,6 @@
import * as A from '../prelude/array';
import * as S from '../prelude/string';
import { MfmForest, MfmTree } from './types';
import { MfmForest, MfmTree } from './prelude';
import { createTree, createLeaf } from '../prelude/tree';
function isEmptyTextTree(t: MfmTree): boolean {
+1 -1
View File
@@ -1,5 +1,5 @@
import { mfmLanguage } from './language';
import { MfmForest } from './types';
import { MfmForest } from './prelude';
import { normalize } from './normalize';
export function parse(source: string): MfmForest {
+2
View File
@@ -35,3 +35,5 @@ export function createLeaf(type: string, props: any): MfmTree {
export function createTree(type: string, children: MfmForest, props: any): MfmTree {
return T.createTree({ type, props }, children);
}
export const urlRegex = /^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/;
+1 -1
View File
@@ -2,7 +2,7 @@ import { JSDOM } from 'jsdom';
import config from '../config';
import { INote } from '../models/note';
import { intersperse } from '../prelude/array';
import { MfmForest, MfmTree } from './types';
import { MfmForest, MfmTree } from './prelude';
export function toHtml(tokens: MfmForest, mentionedRemoteUsers: INote['mentionedRemoteUsers'] = []) {
if (tokens == null) {
+1 -1
View File
@@ -1,5 +1,5 @@
import * as fs from 'fs';
import * as isSvg from 'is-svg';
import isSvg from 'is-svg';
export default function(path: string) {
try {
+27
View File
@@ -0,0 +1,27 @@
import config from '../config';
import { toUnicode, toASCII } from 'punycode';
import { URL } from 'url';
export function getFullApAccount(username: string, host: string) {
return host ? `${username}@${toApHost(host)}` : `${username}@${toApHost(config.host)}`;
}
export function isSelfHost(host: string) {
if (host == null) return true;
return toApHost(config.host) === toApHost(host);
}
export function extractDbHost(uri: string) {
const url = new URL(uri);
return toDbHost(url.hostname);
}
export function toDbHost(host: string) {
if (host == null) return null;
return toUnicode(host.toLowerCase());
}
export function toApHost(host: string) {
if (host == null) return null;
return toASCII(host.toLowerCase());
}
+79
View File
@@ -0,0 +1,79 @@
import * as tmp from 'tmp';
import * as fs from 'fs';
import * as util from 'util';
import chalk from 'chalk';
import * as request from 'request';
import Logger from '../services/logger';
import config from '../config';
const logger = new Logger('download-text-file');
export async function downloadTextFile(url: string): Promise<string> {
// Create temp file
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
tmp.file((e, path, fd, cleanup) => {
if (e) return rej(e);
res([path, cleanup]);
});
});
logger.info(`Temp file is ${path}`);
// write content at URL to temp file
await new Promise((res, rej) => {
logger.info(`Downloading ${chalk.cyan(url)} ...`);
const writable = fs.createWriteStream(path);
writable.on('finish', () => {
logger.succ(`Download finished: ${chalk.cyan(url)}`);
res();
});
writable.on('error', error => {
logger.error(`Download failed: ${chalk.cyan(url)}: ${error}`, {
url: url,
e: error
});
rej(error);
});
const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url;
const req = request({
url: requestUrl,
proxy: config.proxy,
timeout: 10 * 1000,
headers: {
'User-Agent': config.userAgent
}
});
req.pipe(writable);
req.on('response', response => {
if (response.statusCode !== 200) {
logger.error(`Got ${response.statusCode} (${url})`);
writable.close();
rej(response.statusCode);
}
});
req.on('error', error => {
logger.error(`Failed to start download: ${chalk.cyan(url)}: ${error}`, {
url: url,
e: error
});
writable.close();
rej(error);
});
});
logger.succ(`Downloaded to: ${path}`);
const text = await util.promisify(fs.readFile)(path, 'utf8');
cleanup();
return text;
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { EmojiNode, MfmForest } from '../mfm/types';
import { EmojiNode, MfmForest } from '../mfm/prelude';
import { preorderF } from '../prelude/tree';
import { unique } from '../prelude/array';
+1 -1
View File
@@ -1,4 +1,4 @@
import { HashtagNode, MfmForest } from '../mfm/types';
import { HashtagNode, MfmForest } from '../mfm/prelude';
import { preorderF } from '../prelude/tree';
import { unique } from '../prelude/array';
+1 -1
View File
@@ -1,6 +1,6 @@
// test is located in test/extract-mentions
import { MentionNode, MfmForest } from '../mfm/types';
import { MentionNode, MfmForest } from '../mfm/prelude';
import { preorderF } from '../prelude/tree';
export default function(mfmForest: MfmForest): MentionNode['props'][] {
+7
View File
@@ -4,6 +4,7 @@ import AccessToken from './access-token';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
import config from '../config';
import { dbLogger } from '../db/logger';
const App = db.get<IApp>('apps');
App.createIndex('secret');
@@ -66,6 +67,12 @@ export const pack = (
}
}
// (データベースの欠損などで)アプリがデータベース上に見つからなかったとき
if (_app == null) {
dbLogger.warn(`[DAMAGED DB] (missing) pkg: app :: ${app}`);
return null;
}
// Rename _id to id
_app.id = _app._id;
delete _app._id;
+2
View File
@@ -3,6 +3,8 @@ import db from '../db/mongodb';
const Log = db.get<ILog>('logs');
Log.createIndex('createdAt', { expireAfterSeconds: 3600 * 24 * 3 });
Log.createIndex('level');
Log.createIndex('domain');
export default Log;
export interface ILog {
+1
View File
@@ -198,6 +198,7 @@ export type IMeta = {
mascotImageUrl?: string;
bannerUrl?: string;
errorImageUrl?: string;
iconUrl?: string;
cacheRemoteFiles?: boolean;
+29 -4
View File
@@ -19,6 +19,7 @@ Note.createIndex('userId');
Note.createIndex('mentions');
Note.createIndex('visibleUserIds');
Note.createIndex('replyId');
Note.createIndex('renoteId');
Note.createIndex('tagsLower');
Note.createIndex('_user.host');
Note.createIndex('_files._id');
@@ -35,10 +36,12 @@ export type INote = {
_id: mongo.ObjectID;
createdAt: Date;
deletedAt: Date;
updatedAt?: Date;
fileIds: mongo.ObjectID[];
replyId: mongo.ObjectID;
renoteId: mongo.ObjectID;
poll: IPoll;
name?: string;
text: string;
tags: string[];
tagsLower: string[];
@@ -99,7 +102,9 @@ export type INote = {
};
export type IPoll = {
choices: IChoice[]
choices: IChoice[];
multiple?: boolean;
expiresAt?: Date;
};
export type IChoice = {
@@ -313,15 +318,31 @@ export const pack = async (
// Poll
if (meId && _note.poll) {
_note.poll = (async poll => {
if (poll.multiple) {
const votes = await PollVote.find({
userId: meId,
noteId: id
});
const myChoices = (poll.choices as IChoice[]).filter(x => votes.some(y => x.id == y.choice));
for (const myChoice of myChoices) {
(myChoice as any).isVoted = true;
}
return poll;
} else {
poll.multiple = false;
}
const vote = await PollVote
.findOne({
userId: meId,
noteId: id
});
if (vote != null) {
const myChoice = poll.choices
.filter((c: any) => c.id == vote.choice)[0];
if (vote) {
const myChoice = (poll.choices as IChoice[])
.filter(x => x.id == vote.choice)[0] as any;
myChoice.isVoted = true;
}
@@ -371,6 +392,10 @@ export const pack = async (
}
//#endregion
if (_note.name) {
_note.text = `${_note.name}\n${_note.text}`;
}
if (_note.user.isCat && _note.text) {
_note.text = (_note.text
// ja-JP
+2 -1
View File
@@ -2,9 +2,10 @@ import * as mongo from 'mongodb';
import db from '../db/mongodb';
const PollVote = db.get<IPollVote>('pollVotes');
PollVote.dropIndex(['userId', 'noteId'], { unique: true }).catch(() => {});
PollVote.createIndex('userId');
PollVote.createIndex('noteId');
PollVote.createIndex(['userId', 'noteId'], { unique: true });
PollVote.createIndex(['userId', 'noteId', 'choice'], { unique: true });
export default PollVote;
export interface IPollVote {
+1
View File
@@ -20,6 +20,7 @@ User.createIndex('createdAt');
User.createIndex('updatedAt');
User.createIndex('followersCount');
User.createIndex('tags');
User.createIndex('isSuspended');
User.createIndex('username');
User.createIndex('usernameLower');
User.createIndex('host');
+129 -106
View File
@@ -1,164 +1,187 @@
import * as Queue from 'bee-queue';
import * as Queue from 'bull';
import * as httpSignature from 'http-signature';
import config from '../config';
import { ILocalUser } from '../models/user';
import { program } from '../argv';
import handler from './processors';
import processDeliver from './processors/deliver';
import processInbox from './processors/inbox';
import processDb from './processors/db';
import { queueLogger } from './logger';
import { IDriveFile } from '../models/drive-file';
const enableQueue = !program.disableQueue;
const enableQueueProcessing = !program.onlyServer && enableQueue;
const queueAvailable = config.redis != null;
const queue = initializeQueue();
function initializeQueue() {
if (queueAvailable && enableQueue) {
return new Queue('misskey-queue', {
redis: {
port: config.redis.port,
host: config.redis.host,
password: config.redis.pass
},
removeOnSuccess: true,
removeOnFailure: true,
getEvents: false,
sendEvents: false,
storeJobs: false
});
} else {
return null;
}
function initializeQueue(name: string) {
return new Queue(name, config.redis != null ? {
redis: {
port: config.redis.port,
host: config.redis.host,
password: config.redis.pass,
db: config.redis.db || 0,
},
prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue'
} : null);
}
export const deliverQueue = initializeQueue('deliver');
export const inboxQueue = initializeQueue('inbox');
export const dbQueue = initializeQueue('db');
const deliverLogger = queueLogger.createSubLogger('deliver');
const inboxLogger = queueLogger.createSubLogger('inbox');
deliverQueue
.on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`))
.on('active', (job) => deliverLogger.debug(`active id=${job.id} to=${job.data.to}`))
.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) id=${job.id} to=${job.data.to}`))
.on('failed', (job, err) => deliverLogger.warn(`failed(${err}) id=${job.id} to=${job.data.to}`))
.on('error', (error) => deliverLogger.error(`error ${error}`))
.on('stalled', (job) => deliverLogger.warn(`stalled id=${job.id} to=${job.data.to}`));
inboxQueue
.on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`))
.on('active', (job) => inboxLogger.debug(`active id=${job.id}`))
.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) id=${job.id}`))
.on('failed', (job, err) => inboxLogger.warn(`failed(${err}) id=${job.id} activity=${job.data.activity ? job.data.activity.id : 'none'}`))
.on('error', (error) => inboxLogger.error(`error ${error}`))
.on('stalled', (job) => inboxLogger.warn(`stalled id=${job.id} activity=${job.data.activity ? job.data.activity.id : 'none'}`));
export function deliver(user: ILocalUser, content: any, to: any) {
if (content == null) return;
if (content == null) return null;
const data = {
type: 'deliver',
user,
content,
to
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data)
.retries(8)
.backoff('exponential', 1000)
.save();
} else {
return handler({ data }, () => {});
}
return deliverQueue.add(data, {
attempts: 8,
backoff: {
type: 'exponential',
delay: 60 * 1000
},
removeOnComplete: true,
removeOnFail: true
});
}
export function processInbox(activity: any, signature: httpSignature.IParsedSignature) {
export function inbox(activity: any, signature: httpSignature.IParsedSignature) {
const data = {
type: 'processInbox',
activity: activity,
signature
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data)
.retries(3)
.backoff('exponential', 500)
.save();
} else {
return handler({ data }, () => {});
}
return inboxQueue.add(data, {
attempts: 8,
backoff: {
type: 'exponential',
delay: 1000
},
removeOnComplete: true,
removeOnFail: true
});
}
export function createDeleteNotesJob(user: ILocalUser) {
const data = {
type: 'deleteNotes',
return dbQueue.add('deleteNotes', {
user: user
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data).save();
} else {
return handler({ data }, () => {});
}
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createDeleteDriveFilesJob(user: ILocalUser) {
const data = {
type: 'deleteDriveFiles',
return dbQueue.add('deleteDriveFiles', {
user: user
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data).save();
} else {
return handler({ data }, () => {});
}
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createExportNotesJob(user: ILocalUser) {
const data = {
type: 'exportNotes',
return dbQueue.add('exportNotes', {
user: user
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data).save();
} else {
return handler({ data }, () => {});
}
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createExportFollowingJob(user: ILocalUser) {
const data = {
type: 'exportFollowing',
return dbQueue.add('exportFollowing', {
user: user
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data).save();
} else {
return handler({ data }, () => {});
}
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createExportMuteJob(user: ILocalUser) {
const data = {
type: 'exportMute',
return dbQueue.add('exportMute', {
user: user
};
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data).save();
} else {
return handler({ data }, () => {});
}
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createExportBlockingJob(user: ILocalUser) {
const data = {
type: 'exportBlocking',
return dbQueue.add('exportBlocking', {
user: user
};
}, {
removeOnComplete: true,
removeOnFail: true
});
}
if (queueAvailable && enableQueueProcessing) {
return queue.createJob(data).save();
} else {
return handler({ data }, () => {});
}
export function createExportUserListsJob(user: ILocalUser) {
return dbQueue.add('exportUserLists', {
user: user
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createImportFollowingJob(user: ILocalUser, fileId: IDriveFile['_id']) {
return dbQueue.add('importFollowing', {
user: user,
fileId: fileId
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export function createImportUserListsJob(user: ILocalUser, fileId: IDriveFile['_id']) {
return dbQueue.add('importUserLists', {
user: user,
fileId: fileId
}, {
removeOnComplete: true,
removeOnFail: true
});
}
export default function() {
if (queueAvailable && enableQueueProcessing) {
queue.process(128, handler);
queueLogger.succ('Processing started');
if (!program.onlyServer) {
deliverQueue.process(128, processDeliver);
inboxQueue.process(128, processInbox);
processDb(dbQueue);
}
return queue;
}
export function destroy() {
queue.destroy().then(n => {
queueLogger.succ(`All job removed (${n} jobs)`);
deliverQueue.once('cleaned', (jobs, status) => {
deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
});
deliverQueue.clean(0, 'delayed');
inboxQueue.once('cleaned', (jobs, status) => {
inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
});
inboxQueue.clean(0, 'delayed');
}
@@ -1,14 +1,14 @@
import * as bq from 'bee-queue';
import * as Bull from 'bull';
import * as mongo from 'mongodb';
import { queueLogger } from '../logger';
import User from '../../models/user';
import DriveFile from '../../models/drive-file';
import deleteFile from '../../services/drive/delete-file';
import { queueLogger } from '../../logger';
import User from '../../../models/user';
import DriveFile from '../../../models/drive-file';
import deleteFile from '../../../services/drive/delete-file';
const logger = queueLogger.createSubLogger('delete-drive-files');
export async function deleteDriveFiles(job: bq.Job, done: any): Promise<void> {
export async function deleteDriveFiles(job: Bull.Job, done: any): Promise<void> {
logger.info(`Deleting drive files of ${job.data.user._id} ...`);
const user = await User.findOne({
@@ -32,7 +32,7 @@ export async function deleteDriveFiles(job: bq.Job, done: any): Promise<void> {
if (files.length === 0) {
ended = true;
if (job.reportProgress) job.reportProgress(100);
job.progress(100);
break;
}
@@ -47,7 +47,7 @@ export async function deleteDriveFiles(job: bq.Job, done: any): Promise<void> {
userId: user._id,
});
if (job.reportProgress) job.reportProgress(deletedCount / total);
job.progress(deletedCount / total);
}
logger.succ(`All drive files (${deletedCount}) of ${user._id} has been deleted.`);
@@ -1,14 +1,14 @@
import * as bq from 'bee-queue';
import * as Bull from 'bull';
import * as mongo from 'mongodb';
import { queueLogger } from '../logger';
import Note from '../../models/note';
import deleteNote from '../../services/note/delete';
import User from '../../models/user';
import { queueLogger } from '../../logger';
import Note from '../../../models/note';
import deleteNote from '../../../services/note/delete';
import User from '../../../models/user';
const logger = queueLogger.createSubLogger('delete-notes');
export async function deleteNotes(job: bq.Job, done: any): Promise<void> {
export async function deleteNotes(job: Bull.Job, done: any): Promise<void> {
logger.info(`Deleting notes of ${job.data.user._id} ...`);
const user = await User.findOne({
@@ -32,7 +32,7 @@ export async function deleteNotes(job: bq.Job, done: any): Promise<void> {
if (notes.length === 0) {
ended = true;
if (job.reportProgress) job.reportProgress(100);
job.progress(100);
break;
}
@@ -47,7 +47,7 @@ export async function deleteNotes(job: bq.Job, done: any): Promise<void> {
userId: user._id,
});
if (job.reportProgress) job.reportProgress(deletedCount / total);
job.progress(deletedCount / total);
}
logger.succ(`All notes (${deletedCount}) of ${user._id} has been deleted.`);

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