Compare commits

..

176 Commits

Author SHA1 Message Date
syuilo
e41f74e77c 10.37.0 2018-11-01 11:52:33 +09:00
syuilo
c21caad1c5 Custom emoji (#3061)
* wip

* wip

* wip
2018-11-01 11:51:49 +09:00
syuilo
86fcd3a378 Fix bug 2018-11-01 10:00:15 +09:00
syuilo
2b3687b3cb 10.36.1 2018-11-01 09:35:24 +09:00
syuilo
5d61c7c691 Refactor and use original image for banner 2018-11-01 09:30:51 +09:00
syuilo
1bb266e7c7 Update package-lock.json 2018-11-01 09:19:31 +09:00
syuilo
1fca8d322c Clean up 2018-11-01 09:19:22 +09:00
syuilo
325cd03a59 Improve performance 2018-11-01 09:08:00 +09:00
syuilo
2f7e6baa05 Clean up 2018-11-01 09:02:54 +09:00
syuilo
d252e066fe Improve performance 2018-11-01 09:00:18 +09:00
dependabot[bot]
fe7bd9ab3c Bump typescript-eslint-parser from 20.0.0 to 20.1.1 (#3057)
Bumps [typescript-eslint-parser](https://github.com/eslint/typescript-eslint-parser) from 20.0.0 to 20.1.1.
- [Release notes](https://github.com/eslint/typescript-eslint-parser/releases)
- [Changelog](https://github.com/eslint/typescript-eslint-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/typescript-eslint-parser/compare/v20.0.0...v20.1.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-01 08:24:13 +09:00
dependabot[bot]
84e3f41305 Bump ts-loader from 5.2.2 to 5.3.0 (#3055)
Bumps [ts-loader](https://github.com/TypeStrong/ts-loader) from 5.2.2 to 5.3.0.
- [Release notes](https://github.com/TypeStrong/ts-loader/releases)
- [Changelog](https://github.com/TypeStrong/ts-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/TypeStrong/ts-loader/compare/v5.2.2...v5.3.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-01 08:24:05 +09:00
dependabot[bot]
3e8cccad0d Bump jsdom from 12.2.0 to 13.0.0 (#3058)
Bumps [jsdom](https://github.com/jsdom/jsdom) from 12.2.0 to 13.0.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/12.2.0...13.0.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-01 08:23:38 +09:00
Acid Chicken (硫酸鶏)
a2b94d67f7 Update README.md [AUTOGEN] (#3060) 2018-11-01 08:23:11 +09:00
dependabot[bot]
6ab61e73b0 Bump apexcharts from 2.1.6 to 2.1.9 (#3056)
Bumps [apexcharts](https://github.com/apexcharts/apexcharts.js) from 2.1.6 to 2.1.9.
- [Release notes](https://github.com/apexcharts/apexcharts.js/releases)
- [Changelog](https://github.com/apexcharts/apexcharts.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/apexcharts/apexcharts.js/compare/v2.1.6...v2.1.9)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-01 08:22:53 +09:00
Acid Chicken (硫酸鶏)
051c6973af Update README.md [AUTOGEN] (#3059) 2018-11-01 08:20:56 +09:00
syuilo
806a49ec3d 10.36.0 2018-11-01 00:12:13 +09:00
syuilo
3829fe128a Update src/server/api/endpoints/users/relation.ts 2018-11-01 00:11:52 +09:00
syuilo
649177985d [API] Implement users/relation 2018-11-01 00:11:21 +09:00
syuilo
c15148b23c [API] Include detailed user information for block/mute response 2018-10-31 23:34:35 +09:00
syuilo
261a3f5d91 Better rate limitting 2018-10-31 23:03:14 +09:00
syuilo
256ba78ba5 Fix 2018-10-31 22:55:17 +09:00
syuilo
04aff8866e [MFM] Better hashtag detection 2018-10-31 22:38:05 +09:00
syuilo
1a51b98700 Refactor 2018-10-31 22:35:02 +09:00
syuilo
f64100226d Revert "Clean up"
This reverts commit 8948a0d3a4.
2018-10-31 22:10:25 +09:00
syuilo
b7805e48a6 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-31 22:08:27 +09:00
syuilo
0d9556620d Update package-lock.json 2018-10-31 22:08:19 +09:00
Acid Chicken (硫酸鶏)
a51828a7a2 Update test.yml (#3052) 2018-10-31 22:07:26 +09:00
Acid Chicken (硫酸鶏)
7e2009f408 Update config.yml (#3051) 2018-10-31 22:00:21 +09:00
syuilo
008d950a39 10.35.1 2018-10-31 18:07:59 +09:00
syuilo
22d5862afb 🎨 2018-10-31 18:07:00 +09:00
syuilo
de569147a5 Fix #3041 2018-10-31 17:56:21 +09:00
syuilo
a82c3db750 Merge pull request #3038 from syuilo/l10n_develop
New Crowdin translations
2018-10-31 13:44:10 +09:00
dependabot[bot]
80706d10af Bump @types/speakeasy from 2.0.2 to 2.0.3 (#3012)
Bumps [@types/speakeasy](https://github.com/DefinitelyTyped/DefinitelyTyped) from 2.0.2 to 2.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-31 13:43:45 +09:00
dependabot[bot]
93f01ed4df Bump typescript from 3.1.3 to 3.1.4 (#3049)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v3.1.3...v3.1.4)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-31 13:42:47 +09:00
dependabot[bot]
a3a28e5557 Bump file-type from 10.1.0 to 10.2.0 (#3039)
Bumps [file-type](https://github.com/sindresorhus/file-type) from 10.1.0 to 10.2.0.
- [Release notes](https://github.com/sindresorhus/file-type/releases)
- [Commits](https://github.com/sindresorhus/file-type/compare/v10.1.0...v10.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-31 13:42:31 +09:00
syuilo
8948a0d3a4 Clean up 2018-10-31 13:28:05 +09:00
syuilo
d849ea9b41 Clean up 2018-10-31 13:23:12 +09:00
syuilo
0144575f3f Improve performance 2018-10-31 13:20:24 +09:00
syuilo
bdbe646ca7 Improve performance 2018-10-31 13:14:45 +09:00
syuilo
1a1483a242 Update package-lock.json 2018-10-31 12:29:38 +09:00
syuilo
962346785b New translations ja-JP.yml (English) 2018-10-31 11:31:00 +09:00
syuilo
a73da3cd70 10.35.0 2018-10-31 11:30:49 +09:00
Acid Chicken (硫酸鶏)
9c27d0ae3f Add CircleCI badge (#3050) 2018-10-31 11:29:25 +09:00
syuilo
525d5218c1 🎨 2018-10-31 11:29:03 +09:00
syuilo
e23b13ec7f [API] Include detailed user information of blocking API responses 2018-10-31 11:24:36 +09:00
syuilo
29b000e03c Remove needless async/await 2018-10-31 11:22:49 +09:00
syuilo
6a7b0df810 New translations ja-JP.yml (Norwegian) 2018-10-31 11:22:11 +09:00
syuilo
4142de9195 New translations ja-JP.yml (Dutch) 2018-10-31 11:22:07 +09:00
syuilo
9195e1be00 New translations ja-JP.yml (Japanese, Kansai) 2018-10-31 11:22:03 +09:00
syuilo
75382d13fd New translations ja-JP.yml (Spanish) 2018-10-31 11:21:59 +09:00
syuilo
d444280a28 New translations ja-JP.yml (Russian) 2018-10-31 11:21:52 +09:00
syuilo
52fc0fe04a New translations ja-JP.yml (Portuguese) 2018-10-31 11:21:48 +09:00
syuilo
216bebadf1 New translations ja-JP.yml (Polish) 2018-10-31 11:21:44 +09:00
syuilo
a5592931cb New translations ja-JP.yml (Korean) 2018-10-31 11:21:39 +09:00
syuilo
a2228417ff New translations ja-JP.yml (Italian) 2018-10-31 11:21:35 +09:00
syuilo
3e1e292c3e New translations ja-JP.yml (German) 2018-10-31 11:21:31 +09:00
syuilo
f2f039ae9e New translations ja-JP.yml (French) 2018-10-31 11:21:27 +09:00
syuilo
29dde1eda0 New translations ja-JP.yml (English) 2018-10-31 11:21:21 +09:00
syuilo
45d3792ce0 New translations ja-JP.yml (Chinese Simplified) 2018-10-31 11:21:17 +09:00
syuilo
875d0aaebb New translations ja-JP.yml (Catalan) 2018-10-31 11:21:13 +09:00
syuilo
26c9d8ff6f Clean up 2018-10-31 11:20:54 +09:00
syuilo
5e3372e932 Merge pull request #3047 from mei23/mei-1031-blokings-list
blockings list
2018-10-31 11:17:24 +09:00
syuilo
f7069dcd18 良い感じに 2018-10-31 11:16:13 +09:00
mei23
560bb65384 blockings list 2018-10-31 04:59:01 +09:00
Acid Chicken (硫酸鶏)
50cd6a036e Implement /api/v1/instance (#3045)
* Update mastodon.ts

* Update types.ts

* Update mastodon.ts
2018-10-31 02:17:54 +09:00
MeiMei
441ab2b5f8 Fix: can't recognize rebirthed instance user (#3046)
* resync uri from WebFinger

* trigger resync on user page

* allways update on resync

* Revert "trigger resync on user page"

This reverts commit 8ff139fb49.

* background resync
2018-10-31 02:16:13 +09:00
zwebmedia
ba5ed188a1 Add Crowdin info to translate docs (#3044) 2018-10-30 22:56:13 +09:00
syuilo
72e672f08d Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-30 21:55:25 +09:00
syuilo
120474ec6a Fix bug 2018-10-30 21:55:16 +09:00
かひわし4(バージョン1)
eee57c47f5 Use cache when default.yml update (#3042)
* Use cache when default.yml update

* Install latest npm in base image
2018-10-30 21:18:03 +09:00
syuilo
4c160869b8 Update test/api.ts 2018-10-30 21:17:26 +09:00
syuilo
3720a7fbe0 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-30 14:34:39 +09:00
syuilo
7afa541a53 Fix #3040 2018-10-30 14:34:32 +09:00
Acid Chicken (硫酸鶏)
6f979c8275 Configure CI (#3037)
* Update config.yml

* Configure CI

* Use Vesion 2.1

* Fix error

* Ensure binary builds

* Ensure misskey builds

* Store artifacts

* Ensure node-gyp builds

* Fix typo

* Fix typo

* Ensure binary builds

* Update working directory

* Cache test npm packages

* Revert "Update working directory"

* Ensure misskey builds

* Ensure node-gyp builds

* Fix missing configurations

* Configure deploy filters

* Use latest npm in Docker
2018-10-30 12:36:14 +09:00
syuilo
d399241e65 Refactor 2018-10-30 09:36:20 +09:00
syuilo
e85dec030a [Client] Fix bug 2018-10-30 09:27:57 +09:00
syuilo
d0220764cc New translations ja-JP.yml (French) 2018-10-30 05:31:25 +09:00
syuilo
75c1df9531 New translations ja-JP.yml (French) 2018-10-30 05:11:49 +09:00
syuilo
bca7156d6b New translations ja-JP.yml (French) 2018-10-30 05:02:28 +09:00
syuilo
64277b7157 10.34.0 2018-10-29 21:55:33 +09:00
syuilo
4a72543f65 Merge pull request #3019 from syuilo/l10n_develop
New Crowdin translations
2018-10-29 21:53:58 +09:00
syuilo
5b84d29807 Better indexes 2018-10-29 21:53:40 +09:00
syuilo
a11061ec2b New translations ja-JP.yml (English) 2018-10-29 21:51:37 +09:00
syuilo
24cfb93b2e Update .circleci/config.yml 2018-10-29 21:48:35 +09:00
syuilo
502b42d63a New translations ja-JP.yml (Norwegian) 2018-10-29 21:43:46 +09:00
syuilo
612672b79c New translations ja-JP.yml (Dutch) 2018-10-29 21:43:42 +09:00
syuilo
abc670e1b1 New translations ja-JP.yml (Japanese, Kansai) 2018-10-29 21:43:38 +09:00
syuilo
d589ccdd01 New translations ja-JP.yml (Spanish) 2018-10-29 21:43:34 +09:00
syuilo
acb07d9f7d New translations ja-JP.yml (Russian) 2018-10-29 21:43:29 +09:00
syuilo
f4d2186719 New translations ja-JP.yml (Portuguese) 2018-10-29 21:43:25 +09:00
syuilo
d0ede5c665 New translations ja-JP.yml (Polish) 2018-10-29 21:43:20 +09:00
syuilo
554cbb5e9b New translations ja-JP.yml (Korean) 2018-10-29 21:43:16 +09:00
syuilo
dbd32a56bf New translations ja-JP.yml (Italian) 2018-10-29 21:43:10 +09:00
syuilo
7f500235c6 New translations ja-JP.yml (German) 2018-10-29 21:43:05 +09:00
syuilo
39a58084c8 New translations ja-JP.yml (French) 2018-10-29 21:43:01 +09:00
syuilo
cde0fde836 New translations ja-JP.yml (English) 2018-10-29 21:42:56 +09:00
syuilo
e70cca0fda New translations ja-JP.yml (Chinese Simplified) 2018-10-29 21:42:52 +09:00
syuilo
919bd7eb82 New translations ja-JP.yml (Catalan) 2018-10-29 21:42:45 +09:00
syuilo
312cff3d6f Fix 2018-10-29 21:39:35 +09:00
syuilo
0d86eef3d7 Format 2018-10-29 21:38:09 +09:00
syuilo
13acf570e7 Improve performance 2018-10-29 21:35:46 +09:00
syuilo
fa17623fa8 モバイル版からブロックできるように 2018-10-29 21:32:38 +09:00
syuilo
06fd525950 Refactor 2018-10-29 21:32:21 +09:00
syuilo
4805b5115a 🎨 2018-10-29 21:09:32 +09:00
syuilo
108dcb3e61 物理削除系の処理を削除
これらの処理はパフォーマンス的に現実的でないし、すべてのモデルの関係を把握している必要があり保守が困難
論理削除でなんとかする
2018-10-29 21:06:23 +09:00
syuilo
780d272535 New translations ja-JP.yml (Norwegian) 2018-10-29 20:44:27 +09:00
syuilo
02ea4b81a5 New translations ja-JP.yml (Dutch) 2018-10-29 20:44:22 +09:00
syuilo
7c1bdc6d36 New translations ja-JP.yml (Japanese, Kansai) 2018-10-29 20:44:15 +09:00
syuilo
78c7b8b836 New translations ja-JP.yml (Spanish) 2018-10-29 20:44:10 +09:00
syuilo
227da30acb New translations ja-JP.yml (Russian) 2018-10-29 20:44:03 +09:00
syuilo
610805026f New translations ja-JP.yml (Portuguese) 2018-10-29 20:43:57 +09:00
syuilo
c02399c3d2 New translations ja-JP.yml (Polish) 2018-10-29 20:43:51 +09:00
syuilo
e0799d4153 New translations ja-JP.yml (Korean) 2018-10-29 20:43:45 +09:00
syuilo
6df83f1aa9 New translations ja-JP.yml (Italian) 2018-10-29 20:43:41 +09:00
syuilo
efb5ad1d9b New translations ja-JP.yml (German) 2018-10-29 20:43:35 +09:00
syuilo
716976f016 New translations ja-JP.yml (French) 2018-10-29 20:43:31 +09:00
syuilo
7892f41b84 New translations ja-JP.yml (English) 2018-10-29 20:43:26 +09:00
syuilo
d549e03b3f New translations ja-JP.yml (Chinese Simplified) 2018-10-29 20:43:22 +09:00
syuilo
c511ef21ff New translations ja-JP.yml (Catalan) 2018-10-29 20:43:18 +09:00
MeiMei
d64dc45899 User blocking (Following part) (#3035)
* block wip

* UndoBlock

* UnBlock

* wip

* follow

* UI

* fix
2018-10-29 20:32:42 +09:00
syuilo
bcb0588409 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-10-29 19:33:00 +09:00
syuilo
0975959eb9 Clean up 2018-10-29 19:32:20 +09:00
syuilo
e985a6d9d3 Update config.yml 2018-10-29 19:27:47 +09:00
syuilo
b893305974 Delete appveyor.yml 2018-10-29 19:19:41 +09:00
syuilo
724fdd44e4 Clean up 2018-10-29 19:13:16 +09:00
syuilo
b480ef669c Fix doc 2018-10-29 19:11:01 +09:00
syuilo
4b145da046 Fix MFM parsing 2018-10-29 19:09:24 +09:00
syuilo
83d168ece3 Fix API definition 2018-10-29 19:04:58 +09:00
syuilo
ae44fe7818 Refactor 2018-10-29 15:09:03 +09:00
syuilo
f8981b3acb Update src/server/api/endpoints/notes/reactions.ts 2018-10-29 15:07:50 +09:00
syuilo
050b324885 ? 2018-10-29 10:52:42 +09:00
syuilo
e74c0df6c6 Fix #3034 2018-10-29 10:52:36 +09:00
syuilo
22d0d11895 Update note-reaction.ts 2018-10-28 21:41:39 +09:00
dependabot[bot]
80d0c0cf74 Bump eslint from 5.7.0 to 5.8.0 (#3030)
Bumps [eslint](https://github.com/eslint/eslint) from 5.7.0 to 5.8.0.
- [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.7.0...v5.8.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-28 21:38:27 +09:00
MeiMei
518646b925 Fix: Unexpected remote user is selected (#3032) 2018-10-28 07:49:58 +09:00
syuilo
479d7e0087 Merge pull request #3031 from syuilo/patch-autogen 2018-10-27 13:37:02 +09:00
Acid Chicken (硫酸鶏)
8ea1a555f4 Update README.md [AUTOGEN] 2018-10-27 13:36:06 +09:00
Acid Chicken (硫酸鶏)
04024dc37c Update README.md [AUTOGEN] (#3024) 2018-10-27 03:47:04 +09:00
Acid Chicken (硫酸鶏)
060ff9288f Build assets in CircleCI. (#3021) 2018-10-27 03:46:48 +09:00
syuilo
197116ee78 New translations ja-JP.yml (Japanese, Kansai) 2018-10-26 22:12:16 +09:00
syuilo
a1e0015257 New translations ja-JP.yml (Japanese, Kansai) 2018-10-26 22:01:37 +09:00
syuilo
7e701ef9e0 10.33.0 2018-10-26 15:23:10 +09:00
syuilo
3d6fb661bb Update src/client/app/desktop/views/components/settings.2fa.vue 2018-10-26 15:17:50 +09:00
syuilo
fc372496da Client: Make drive folder deletable 2018-10-26 15:06:55 +09:00
syuilo
ad7258fe9c Fix bug 2018-10-26 15:06:27 +09:00
syuilo
bd707cb2a8 🎨 2018-10-26 14:51:57 +09:00
syuilo
1839b5f205 Improve usability 2018-10-26 14:47:30 +09:00
syuilo
02b47f963c API: Better error response 2018-10-26 14:38:34 +09:00
syuilo
f8a7f9378a Improve usability 2018-10-26 14:38:22 +09:00
syuilo
65cb253be4 Update src/client/app/common/views/components/ui/textarea.vue 2018-10-26 14:38:03 +09:00
syuilo
a12356b24b Merge pull request #3010 from syuilo/l10n_develop
New Crowdin translations
2018-10-26 08:55:11 +09:00
syuilo
6a67ad7f93 New translations ja-JP.yml (English) 2018-10-26 08:51:31 +09:00
syuilo
140a7f0b1c New translations ja-JP.yml (Norwegian) 2018-10-26 08:42:29 +09:00
syuilo
00159bc6b5 New translations ja-JP.yml (Dutch) 2018-10-26 08:42:24 +09:00
syuilo
9542260103 New translations ja-JP.yml (Japanese, Kansai) 2018-10-26 08:42:20 +09:00
syuilo
72074578df New translations ja-JP.yml (Spanish) 2018-10-26 08:42:15 +09:00
syuilo
3b4750a988 New translations ja-JP.yml (Russian) 2018-10-26 08:42:11 +09:00
syuilo
aeec5f0163 New translations ja-JP.yml (Portuguese) 2018-10-26 08:42:05 +09:00
syuilo
9c94d8c8d6 New translations ja-JP.yml (Polish) 2018-10-26 08:41:59 +09:00
syuilo
581712a2c8 New translations ja-JP.yml (Korean) 2018-10-26 08:41:54 +09:00
syuilo
b25b51aaca New translations ja-JP.yml (Italian) 2018-10-26 08:41:50 +09:00
syuilo
fb97e13a61 New translations ja-JP.yml (German) 2018-10-26 08:41:44 +09:00
syuilo
36e154fdb2 New translations ja-JP.yml (French) 2018-10-26 08:41:40 +09:00
syuilo
ca273a24b4 New translations ja-JP.yml (English) 2018-10-26 08:41:36 +09:00
syuilo
d828bf2889 New translations ja-JP.yml (Chinese Simplified) 2018-10-26 08:41:32 +09:00
syuilo
87efccef18 New translations ja-JP.yml (Catalan) 2018-10-26 08:41:28 +09:00
syuilo
e0bf522e7f Client: Improve API settings 2018-10-26 08:37:30 +09:00
syuilo
5b1cd3bd3c Fix bug 2018-10-26 08:36:50 +09:00
dependabot[bot]
f00489196d Bump @types/mocha from 5.2.3 to 5.2.5 (#3006)
Bumps [@types/mocha](https://github.com/DefinitelyTyped/DefinitelyTyped) from 5.2.3 to 5.2.5.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-26 06:40:31 +09:00
dependabot[bot]
dd53bf7e51 Bump html-minifier from 3.5.20 to 3.5.21 (#3015)
Bumps [html-minifier](https://github.com/kangax/html-minifier) from 3.5.20 to 3.5.21.
- [Release notes](https://github.com/kangax/html-minifier/releases)
- [Commits](https://github.com/kangax/html-minifier/compare/v3.5.20...v3.5.21)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-26 06:40:01 +09:00
dependabot[bot]
35a6da26d2 Bump apexcharts from 2.1.5 to 2.1.6 (#3013)
Bumps [apexcharts](https://github.com/apexcharts/apexcharts.js) from 2.1.5 to 2.1.6.
- [Release notes](https://github.com/apexcharts/apexcharts.js/releases)
- [Changelog](https://github.com/apexcharts/apexcharts.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/apexcharts/apexcharts.js/compare/v2.1.5...v2.1.6)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-26 06:39:48 +09:00
dependabot[bot]
c8c8748a0b Bump webpack from 4.23.0 to 4.23.1 (#3011)
Bumps [webpack](https://github.com/webpack/webpack) from 4.23.0 to 4.23.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.23.0...v4.23.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-10-26 06:38:08 +09:00
syuilo
46d0065a90 New translations ja-JP.yml (Japanese, Kansai) 2018-10-26 01:01:40 +09:00
syuilo
990b0180a8 New translations ja-JP.yml (Japanese, Kansai) 2018-10-26 00:51:35 +09:00
syuilo
f3bfb72251 New translations ja-JP.yml (Japanese, Kansai) 2018-10-26 00:42:19 +09:00
146 changed files with 2343 additions and 1616 deletions

View File

@@ -9,6 +9,7 @@ mongodb:
db: test-misskey
user: admin
pass: ''
# __REDIS__
redis:
host: localhost
port: 6379

137
.circleci/config.yml Normal file
View File

@@ -0,0 +1,137 @@
version: 2.1
general:
branches:
ignore:
- l10n_develop
- imgbot
executors:
default:
working_directory: /tmp/workspace
docker:
- image: misskey/ci:latest
- image: circleci/mongo:latest
- image: circleci/redis:latest
docker:
working_directory: /tmp/workspace
docker:
- image: docker:latest
jobs:
build:
executor: default
steps:
- checkout
- restore_cache:
name: Restore npm package caches
keys:
- npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-
- npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-
- npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-
- npm-v1-arch-{{ arch }}-
- npm-v1-
- run:
name: Install Dependencies
command: |
npm install
- run:
name: Configure
command: |
cp .ci/default.yml .config
cp .ci/test.yml .config
- run:
name: Build
command: |
npm run build || (echo -e '\033[0;34mRebuild modules\033[0;39m' && ls -1A node_modules | grep '^[^@]' | xargs npm rebuild && ls -1A node_modules | grep '^@' | xargs -I%1 sh -c 'ls -1A node_modules/'%1' | xargs -P0 -I%2 npm rebuild node_modules/'%1'/%2' && npm run build)
ls -1ARl node_modules > ls
- save_cache:
name: Cache npm packages
key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
paths:
- node_modules
- store_artifacts:
path: built
- persist_to_workspace:
root: .
paths:
- .
test:
parameters:
without_redis:
type: string
default: ""
executor: default
steps:
- attach_workspace:
at: /tmp/workspace
- when:
condition: <<parameters.without_redis>>
steps:
- run:
name: Configure
command: |
mv .config/test.yml .config/test_redis.yml
touch .config/test.yml
cat .config/test_redis.yml | while IFS= read line; do if [[ "$line" = '# __REDIS__' ]]; then break; else echo "$line" >> .config/test.yml; fi; done
- run:
name: Test
command: |
npm run test || (npm rebuild && npm run test) || ((node-gyp configure && node-gyp build && npm run build || (echo -e '\033[0;34mRebuild modules\033[0;39m' && ls -1A node_modules | grep '^[^@]' | xargs npm rebuild && ls -1A node_modules | grep '^@' | xargs -I%1 sh -c 'ls -1A node_modules/'%1' | xargs -P0 -I%2 npm rebuild node_modules/'%1'/%2' && npm run build)) && npm run test)
ls -1ARl node_modules > ls
- save_cache:
name: Cache npm packages
key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
paths:
- node_modules
docker:
parameters:
with_deploy:
type: string
default: ""
executor: docker
steps:
- checkout
- setup_remote_docker
- run:
name: Build
command: |
docker build . | tee docker.log
tail -n 1 docker.log | read __Successfully __built tag
- when:
condition: <<parameters.with_deploy>>
steps:
- run:
name: Deploy
command: |
if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
then
docker tag $tag misskey/misskey
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
docker push misskey/misskey
else
echo -e '\033[0;33mAborted deploying to Docker Hub\033[0;39m'
fi
workflows:
version: 2
build-and-test:
jobs:
- build
- test:
requires:
- build
- test:
without_redis: "true"
requires:
- build
- docker:
filters:
branches:
ignore: master
- docker:
with_deploy: "true"
filters:
branches:
only: master

View File

@@ -35,7 +35,7 @@ before_script:
- npm install
# 設定ファイルを配置
- cp ./.travis/default.yml ./.config
- cp ./.travis/test.yml ./.config
- cp ./.ci/default.yml ./.config
- cp ./.ci/test.yml ./.config
- travis_wait npm run build

View File

@@ -3,8 +3,9 @@ FROM alpine:3.8 AS base
ENV NODE_ENV=production
RUN apk add --no-cache nodejs nodejs-npm zlib
RUN npm i -g npm@latest
WORKDIR /misskey
COPY . ./
FROM base AS builder
@@ -21,18 +22,23 @@ RUN apk add --no-cache \
pkgconfig \
libtool \
zlib-dev
RUN npm install \
&& npm install -g node-gyp \
&& node-gyp configure \
&& node-gyp build \
&& npm run build
RUN npm i -g node-gyp
COPY ./package.json ./
RUN npm i
COPY . ./
RUN node-gyp configure \
&& node-gyp build \
&& npm run build
FROM base AS runner
COPY --from=builder /misskey/built ./built
COPY --from=builder /misskey/node_modules ./node_modules
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
COPY --from=builder /misskey/node_modules ./node_modules
COPY --from=builder /misskey/built ./built
COPY . ./
CMD ["npm", "start"]

View File

@@ -3,6 +3,7 @@
[![Misskey](/assets/title.png)](https://misskey.xyz/)
================================================================
[![CircleCI](https://circleci.com/gh/syuilo/misskey.svg?style=svg)](https://circleci.com/gh/syuilo/misskey)
[![][travis-badge]][travis-link]
[![][dependencies-badge]][dependencies-link]
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
@@ -71,39 +72,46 @@ Please see [Contribution guide](./CONTRIBUTING.md).
----------------------------------------------------------------
<!-- PATREON_START -->
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=CXe9AqlZy9AsYfiWd3OBYVOzvODoN47Litz0Tu4BFpU%3D" alt="Gargron"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1?token-time=2145916800&token-hash=d6P5MWHHsCMxUuBAEPAoVc5wLUR19mIhqAq7Ma9h9rI%3D" alt="ne_moni"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/2?token-time=2145916800&token-hash=mgPdX9TqZxEg4TTPuc477dxhIgYk9246qafjWZEqZ7g%3D" alt="Melilot"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/2?token-time=2145916800&token-hash=rwZ8qvbm_kpA4ib3kc07tVKupXeySpY5ATQFGxfL9v0%3D" alt="Xeltica"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/3384329/8b713330cb27404ea6e9fac50ff96efe/1?token-time=2145916800&token-hash=0eu4-m1gTWA9PhptVZt6rdKcusqcD7RB87rJT23VVFI%3D" alt="べすれい"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D" alt="gutfuckllc"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1?token-time=2145916800&token-hash=I8lJVM8LeW6TSo5W6uIIRZ42cw83zp1wK_FsbzY0mcQ%3D" alt="mydarkstar"></td>
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
</tr><tr>
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
<td><a href="https://www.patreon.com/negao">negao</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/AxellaMC">Xeltica</a></td>
<td><a href="https://www.patreon.com/user?u=3384329">べすれい</a></td>
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/2?token-time=2145916800&token-hash=zElv7ZcPL3viGsXbNG_KWiKrbV0vvw1gk0panx8DJoo%3D" alt="Naoki Kosaka"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3?token-time=2145916800&token-hash=qsdn0-e6yLaLI6hUX9JAkyTR6a5UdnSp7T1foniBvGQ%3D" alt="YUKIMOCHI"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/8241184/39e18850e87a449e9c9a71acb3310ebd/2?token-time=2145916800&token-hash=iUXOQzRyJDv3PJxwS7Mjwg1459dzh2trOq6NFtXu_OM%3D" alt="Acid Chicken"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=UERBN4OyP7Nh5XwwdDg0N0IE5cD6_qUQMO81Z5Wizso%3D" alt="Hiratake"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/10789744/97175095d8f04c0f86225ff47cb98d40/1?token-time=2145916800&token-hash=P4BIzCX2I1CkEP66ottfhsC8Wr6BUSamjA-vq3pLqFI%3D" alt="Naoki Hirayama"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D" alt="dansup"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=tB1e_r8RlZ5sFL0KV_e8dugapxatNBRK1Z3h67TO1g8%3D" alt="Gargron"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1?token-time=2145916800&token-hash=VZUtwrjQa8Jml4twCjHYQQZ64wHEY4oIlGl7Kc-VYUQ%3D" alt="Nokotaro Takeda"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D" alt="Takashi Shibuya"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td>
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
<td><a href="https://www.patreon.com/acid_chicken">Acid Chicken</a></td>
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
<td><a href="https://www.patreon.com/spinlock">Naoki Hirayama</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>
<table><tr>
</tr><tr>
</tr></table>
**Last updated:** Tue, 02 Oct 2018 09:25:07 UTC
**Last updated:** Wed, 31 Oct 2018 23:21:06 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright

View File

@@ -1,38 +0,0 @@
# appveyor file
# http://www.appveyor.com/docs/appveyor-yml
environment:
matrix:
- nodejs_version: 11.0.0
platform:
- x64
- Any CPU
build: off
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# Update node-gyp
# 必須! node-gyp のバージョンを上げないと、ネイティブモジュールのコンパイルに失敗します
- npm install -g node-gyp
- npm install
init:
# git clone の際の改行を変換しないようにします
- git config --global core.autocrlf false
before_test:
# 設定ファイルを配置
- cp ./.travis/default.yml ./.config
- cp ./.travis/test.yml ./.config
- npm run build
test_script:
- node --version
- npm --version
- npm test

View File

@@ -1,13 +0,0 @@
const deleteUser = require('../built/models/user').deleteUser;
const args = process.argv.slice(2);
const userId = args[0];
console.log(`deleting ${userId}...`);
deleteUser(userId).then(() => {
console.log('done');
}, e => {
console.error(e);
});

View File

@@ -1,23 +0,0 @@
const mongo = require('mongodb');
const User = require('../built/models/user').default;
const args = process.argv.slice(2);
const user = args[0];
const q = user.startsWith('@') ? {
username: user.split('@')[1],
host: user.split('@')[2] || null
} : { _id: new mongo.ObjectID(user) };
console.log(`Mark as verfied ${user}...`);
User.update(q, {
$set: {
isVerified: true
}
}).then(() => {
console.log(`Done ${user}`);
}, e => {
console.error(e);
});

View File

@@ -1,42 +0,0 @@
const { default: Note } = require('../built/models/note');
const { default: Meta } = require('../built/models/meta');
const { default: User } = require('../built/models/user');
async function main() {
const meta = await Meta.findOne({});
const notesCount = await Note.count();
const usersCount = await User.count();
const originalNotesCount = await Note.count({
'_user.host': null
});
const originalUsersCount = await User.count({
host: null
});
const stats = {
notesCount,
usersCount,
originalNotesCount,
originalUsersCount
};
if (meta) {
await Meta.update({}, {
$set: {
stats
}
});
} else {
await Meta.insert({
stats
});
}
}
main().then(() => {
console.log('done');
}).catch(console.error);

View File

@@ -18,6 +18,10 @@ If you find an untranslated part on Misskey:
4. Add the text property using the `foo` keyword below the path that you found or created in step 2. Make sure to type your text in quotation marks. Text should always be inside of quotes.
- For example, in this case we add timeline: `timeline: "タイムライン"` to `locales/ja-JP.yml`.
5. And done
5. When you add text to the ja-JP file (of syuilo/misskey), it will automatically be applied to all other local language files within 24-48 hours. Translations added in ja-JP file should contain the original Japanese strings (example see step 4).
6. The new strings will automatically appear in the localized language files in the original Japanese text. After that, please go to [CrowdIn](https://crowdin.com/project/misskey) to do the localized translations in your language.
7. And done
For more details, please refer to this [commit](https://github.com/syuilo/misskey/commit/10f6d5980fa7692ccb45fbc5f843458b69b7607c).

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "Profil"
notification: "Mitteilungen"
apps: "In App öffnen"
mute: "Stummschalten"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "Sicherheit"
signin: "サインイン履歴"
password: "Passwort"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "Bitte Passwort eingeben"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "Passwort ändern"
enter-current-password: "Derzeitiges Passwort eingeben"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "Profile"
notification: "Notification"
apps: "Apps"
mute: "Mute"
mute-and-block: "Mute / Block"
blocking: "Blocking"
security: "Security"
signin: "Sign in history"
password: "Password"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "Settings saved!"
failed: "Failed to setup. Please ensure that the token is correct."
info: "From the next time you sign in to Misskey, the token displayed on your device will be necessary too, as well as the password."
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "To access the API, set this token as the key 'i' of request parameters."
caution: "Do not enter this token to any apps nor tell this token to others otherwise your account may get compromised."
regeneration-of-token: "If your token gets leaked, you can regenerate it."
regenerate-token: "Regenerate the token"
token: "Token:"
enter-password: "Please enter the password"
enter-password: "Enter the password"
console:
title: 'API console'
endpoint: 'Endpoint'
parameter: 'Parameters'
send: 'Send'
sending: 'Sending'
response: 'Result'
desktop/views/components/settings.apps.vue:
no-apps: "No linked applications"
common/views/components/drive-settings.vue:
max: "Max"
in-use: "In use"
stats: "Statistics"
desktop/views/components/settings.mute.vue:
no-users: "No muted users"
common/views/components/mute-and-block.vue:
mute-and-block: "Mute / Block"
mute: "Mute"
block: "Blocking"
no-muted-users: "No muted users"
no-blocked-users: "No blocked users"
desktop/views/components/settings.password.vue:
reset: "Change password"
enter-current-password: "Enter the current password"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "Mute"
muted: "Muting"
unmute: "Unmute"
block: "Block"
unblock: "Unblock"
block-confirm: "Are you sure block this user?"
push-to-a-list: "Add to list"
list-pushed: "Successfully added {user} to {list}."
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "Timeline"
media: "Media"
is-suspended: "This account has been suspended."
mute: "Mute"
unmute: "Unmute"
block: "Block"
unblock: "Unblock"
mobile/views/pages/user/home.vue:
recent-notes: "Recent notes"
images: "Images"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "Perfil"
notification: "Notificación"
apps: "Aplicaciones"
mute: "Silenciar"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "Seguridad"
signin: "Historial de inicios de sesión"
password: "Contraseña"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "¡Configuraciones guardadas!"
failed: "Error al configurar. Por favor asegúrate de que el token es correcto."
info: "Desde ahora, ingresa el token que se muestra en tu dispositivo adicionalmente a tu contraseña cuando inicies sesión en Misskey"
desktop/views/components/settings.api.vue:
intro: "Para acceder al API, configura este token como la letra \"i\" de los parámetros requeridos."
caution: "Por favor no muestres este token a otros (no lo ingreses en otro lugar que no sea aquí). De otra forma, tu cuenta puede llegar a ser comprometida."
regeneration-of-token: "En el caso no deseado de que este token lo tenga otra persona, puedes regenerarlo."
regenerate-token: "Regenerar el token"
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "Por favor ingresa tu contraseña"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "No hay aplicaciones asociadas"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "No hay usuarios silenciados"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "Cambiar contraseña"
enter-current-password: "Ingresar contraseña actual"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@@ -28,11 +28,11 @@ common:
BSoD:
fatal-error: ":( 致命的な問題が発生しました。"
update-browser-os: "お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。"
error-code: "エラーコード"
browser-version: "ブラウザ バージョン"
client-version: "クライアント バージョン"
error-code: "Code derreur"
browser-version: "Version du navigateur"
client-version: "La version du client"
email-support: "問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。"
thanks: "Thank you for using Misskey."
thanks: "Merci davoir choisi dutiliser Misskey."
got-it: "J'ai compris !"
customization-tips:
title: "Conseils de personnalisation"
@@ -62,7 +62,7 @@ common:
years_ago: "Il y a {} an·s"
month-and-day: "{month} mois/{day} jour"
trash: "Corbeille"
drive: "ドライブ"
drive: "Drive"
weekday-short:
sunday: "D"
monday: "L"
@@ -124,12 +124,12 @@ common:
reduce-motion: "Réduire les animations dans linterface utilisateur"
this-setting-is-this-device-only: "Uniquement sur cet appareil"
do-not-use-in-production: 'Il sagit dune version de développement. Ne pas utiliser dans un environnement de production.'
is-remote-user: "このユーザー情報はコピーです。"
is-remote-user: "Ces informations utilisateur ont été copiées."
is-remote-post: "この投稿情報はコピーです。"
view-on-remote: "正確な情報を見る"
view-on-remote: "Consulter le profil complet"
error:
title: '問題が発生しました'
retry: 'やり直す'
title: 'Une erreur est survenue'
retry: 'Réessayer'
reversi:
drawn: "Partie nulle"
my-turn: "Cest votre tour"
@@ -185,7 +185,7 @@ common:
rename: "Renommer"
stack-left: "Vers la gauche"
pop-right: "Vers la droite"
dev: "アプリの作成に失敗しました。再度お試しください。"
dev: "Échec lors de la création de lapplication. Veuillez réessayer."
auth/views/form.vue:
share-access: "Désirez-vous <b>autoriser</b> <i>{{ app.name }}</i> à avoir accès à votre compte ?"
permission-ask: "Cette application nécessite les autorisations suivantes :"
@@ -542,24 +542,24 @@ desktop/views/components/charts.vue:
title: "Graphiques"
per-day: "par jour"
per-hour: "par heure"
federation: "フェデレーション"
federation: "Fédération"
notes: "Publications"
users: "Utilisateurs"
drive: "Drive"
network: "Réseau"
charts:
federation-instances: "インスタンスの増減"
federation-instances-total: "インスタンスの積算"
federation-instances-total: "Nombre total dinstances"
notes: "投稿の増減 (統合)"
local-notes: "投稿の増減 (ローカル)"
remote-notes: "投稿の増減 (リモート)"
notes-total: "Total des notes"
users: "Nombre dutilisateurs·trices : augmentation/diminution"
users-total: "ユーザーの積算"
users-total: "Nombre total des utilisateurs·rices"
drive: "ドライブ使用量の増減"
drive-total: "ドライブ使用量の積算"
drive-total: "Utilisation totale du lecteur"
drive-files: "ドライブのファイル数の増減"
drive-files-total: "ドライブのファイル数の積算"
drive-files-total: "Nombre total de fichiers sur le lecteur"
network-requests: "Requêtes"
network-time: "Temps de réponse"
network-usage: "Traffic"
@@ -679,10 +679,10 @@ desktop/views/components/note.vue:
reposted-by: "Partagé par {}"
reply: "Répondre"
renote: "Partager"
add-reaction: "リアクション"
detail: "詳細"
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
add-reaction: "Ajouter votre réaction"
detail: "Détails"
private: "Cette publication est privée"
deleted: "Cette publication a été supprimée"
desktop/views/components/notes.vue:
error: "Échec du chargement."
retry: "Réessayer"
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "Profil"
notification: "Notification"
apps: "Applications"
mute: "Mettre en sourdine"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "Sécurité"
signin: "Historique de connexion"
password: "Mot de Passe"
@@ -765,7 +766,7 @@ desktop/views/components/settings.vue:
deck-default: "デッキをデフォルトのUIにする"
display: "Affichage et design"
customize: "Personnaliser l'Accueil"
wallpaper: "壁紙"
wallpaper: "Arrière plan"
choose-wallpaper: "Sélectionner un fond d'écran"
delete-wallpaper: "Supprimer le fond d'écran"
dark-mode: "Mode nuit"
@@ -777,14 +778,14 @@ desktop/views/components/settings.vue:
suggest-recent-hashtags: "Afficher les hashtags populaires dans le champs de saisie"
show-clock-on-header: "Afficher l'horloge à droite sur le coté supérieur"
show-reply-target: "Afficher les réponses"
timeline: "タイムライン"
timeline: "Chronologie"
show-my-renotes: "Afficher mes republications dans le fil"
show-renoted-my-notes: "Afficher mes republications dans les fils"
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
show-maps: "Afficher la carte"
deck-column-align: "デッキのカラムの位置"
deck-column-align-center: "中央"
deck-column-align-left: ""
deck-column-align-center: "Centrer"
deck-column-align-left: "À gauche"
sound: "Son"
enable-sounds: "Activer le son"
enable-sounds-desc: "Jouer un son lorsque vous recevez un message. Ce paramètre est sauvegardé dans le navigateur."
@@ -825,7 +826,7 @@ desktop/views/components/settings.vue:
tools: "Outils"
task-manager: "Gestionnaire de tâches"
third-parties: "Services tiers"
navbar-position: "ナビゲーションバーの位置"
navbar-position: "Position de la barre de navigation"
navbar-position-top: "En haut"
navbar-position-left: "à gauche"
navbar-position-right: "à droite"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "L'operation a été complétée avec succès!"
failed: "L'operation a échoué. Veuillez vous assurer que le token a été entrer correctement."
info: "À partir de maintenant, à chaque fois que vous vous connecter entrez votre mot de passe ainsi que le token généré sur votre appareil."
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "Si votre jeton est compromis, vous pouvez le régénérer."
regenerate-token: "Regenerer le token"
regenerate-token: "Régénérer le jeton"
token: "Jeton :"
enter-password: "Veuillez entrer le mot de passe"
enter-password: "Entrez le mot de passe"
console:
title: 'Console API'
endpoint: 'Point de terminaison'
parameter: 'Paramètres'
send: 'Envoyer'
sending: 'Envoi en cours'
response: 'Résultat'
desktop/views/components/settings.apps.vue:
no-apps: "Aucune application autorisée"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "Aucun utilisateurs mis en sourdine"
in-use: "utilisé"
stats: "Statistiques"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "Changer votre mot de passe"
enter-current-password: "Entrez votre mot de passe actuel"
@@ -938,8 +950,8 @@ desktop/views/pages/admin/admin.vue:
dashboard: "Tableau de bord"
users: "Utilisateur·rice·s"
update: "Mises à jour"
announcements: "お知らせ"
hashtags: "ハッシュタグ"
announcements: "Annonces"
hashtags: "Hashtags"
desktop/views/pages/admin/admin.dashboard.vue:
dashboard: "Tableau de bord"
all-users: "Toutes les utilisateurrices"
@@ -947,9 +959,9 @@ desktop/views/pages/admin/admin.dashboard.vue:
all-notes: "Toutes les publications"
original-notes: "Publications sur cette instance"
invite: "Invitation"
banner-url: "Banner URL"
disableRegistration: "Disable new user registration"
disableLocalTimeline: "Disable the local timeline"
banner-url: "URL de la bannière"
disableRegistration: "Désactiver lenregistrement de nouveaux utilisateurs·rices"
disableLocalTimeline: "Désactiver le fil local"
desktop/views/pages/admin/admin.suspend-user.vue:
suspend-user: "Suspendre un·e utilisateur·rice"
suspend: "Suspendre"
@@ -967,22 +979,22 @@ desktop/views/pages/admin/admin.unverify-user.vue:
unverify: "Ôter la vérification du compte"
unverified: "Ce compte n'est pas vérifié"
desktop/views/pages/admin/admin.announcements.vue:
announcements: "お知らせ"
announcements: "Annonces"
desktop/views/pages/admin/admin.hashtags.vue:
hided-tags: "Hidden Tags"
hided-tags: "Tags cachés"
desktop/views/pages/deck/deck.tl-column.vue:
is-media-only: "Les publications médias uniquement"
is-media-view: "Vue média"
edit: "Options"
desktop/views/pages/deck/deck.user-column.vue:
posts: "投稿"
following: "フォロー"
followers: "フォロワー"
images: "画像"
activity: "アクティビティ"
timeline: "タイムライン"
pinned-notes: "ピン留めされた投稿"
push-to-a-list: "リストに追加"
posts: "Publications"
following: "Suit"
followers: "Abonné·e·s"
images: "Images"
activity: "Activité"
timeline: "Chronologie"
pinned-notes: "Publications épinglées"
push-to-a-list: "Ajouter à la liste"
desktop/views/pages/stats/stats.vue:
all-users: "Toutes les utilisateurrices"
original-users: "Utilisateur·rice·s sur cette instance"
@@ -1035,7 +1047,7 @@ desktop/views/pages/user/user.friends.vue:
no-users: "Pas d'utilisateurs"
desktop/views/pages/user/user.vue:
is-suspended: "Ce compte a été suspendu."
last-used-at: "最終アクセス"
last-used-at: "Actif·ive pour la dernière fois"
desktop/views/pages/user/user.photos.vue:
title: "Photos"
loading: "Chargement en cours"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "Mettre en sourdine"
muted: "Muting"
unmute: "Enlever la sourdine"
block: "Bloquer"
unblock: "Débloquer"
block-confirm: "Bloquer cet utilisateur ?"
push-to-a-list: "Ajouter à la liste"
list-pushed: "Vous avez ajouté {user} à la liste {list}."
desktop/views/pages/user/user.header.vue:
@@ -1055,10 +1070,10 @@ desktop/views/pages/user/user.header.vue:
following: "Suit"
followers: "Abonné·e·s"
is-bot: "Ce compte est un Bot"
years-old: ""
year: ""
month: ""
day: ""
years-old: "ans dâge"
year: "Année"
month: "Mois"
day: "Jour"
desktop/views/pages/user/user.timeline.vue:
default: "Publications"
with-replies: "Publications et réponses"
@@ -1117,8 +1132,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "Hash (md5)"
exif: "EXIF"
nsfw: "CW"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mark-as-sensitive: "Marquer comme sensible"
unmark-as-sensitive: "Ne pas marquer comme sensible"
mobile/views/components/media-image.vue:
sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher"
@@ -1278,8 +1293,8 @@ mobile/views/pages/settings.vue:
timeline: "Fil d'actualité"
show-reply-target: "Afficher les réponses"
show-my-renotes: "Afficher mes republications"
show-renoted-my-notes: "自分の投稿のRenoteを表示する"
show-local-renotes: "ローカルの投稿のRenoteを表示する"
show-renoted-my-notes: "Afficher mes publications partagées"
show-local-renotes: "Afficher les publications partagées localement"
post-style: "Style de la publication"
post-style-standard: "Standard"
post-style-smart: "Intelligent"
@@ -1312,7 +1327,7 @@ mobile/views/pages/settings.vue:
signout: "Déconnexion"
sound: "Sons"
enable-sounds: "Activer les sons"
mark-as-read-all-unread-notes: "すべての投稿を既読にする"
mark-as-read-all-unread-notes: "Marquer toutes les publications comme lues"
mobile/views/pages/user.vue:
follows-you: "Vous suit"
following: "Abonnements"
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "Fil d'actualité"
media: "Media"
is-suspended: "This account has been suspended."
mute: "Mettre en sourdine"
unmute: "Enlever la sourdine"
block: "Bloquer"
unblock: "Débloquer"
mobile/views/pages/user/home.vue:
recent-notes: "Notes récentes"
images: "Images"
@@ -1368,28 +1387,28 @@ docs:
dev/views/index.vue:
manage-apps: "Gestion des applications"
dev/views/apps.vue:
manage-apps: "アプリを管理"
create-app: "アプリ作成"
app-missing: "アプリなし"
manage-apps: "Gestion des applications"
create-app: "Créer une app"
app-missing: "Aucune application"
dev/views/new-app.vue:
create-app: "アプリケーションの作成"
app-name: "アプリケーション名"
app-name-desc: "あなたのアプリの名称。"
app-name-ex: "ex) Misskey for iOS"
app-overview: "アプリの概要"
app-desc: "あなたのアプリの簡単な説明や紹介。"
app-desc-ex: "ex) Misskey iOSクライアント。"
create-app: "Création dune application"
app-name: "Nom de lapplication"
app-name-desc: "Le nom de votre application"
app-name-ex: "p. ex. Misskey pour iOS"
app-overview: "Description courte de lapplication"
app-desc: "Brève description introductive à votre application."
app-desc-ex: "p. ex) Misskey pour iOS"
callback-url: "コールバックURL (オプション)"
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
authority: "権限"
authority: "Autorisations "
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
account-read: "アカウントの情報を見る。"
account-write: "アカウントの情報を操作する。"
note-write: "投稿する。"
reaction-write: "リアクションしたりリアクションをキャンセルする。"
following-write: "フォローしたりフォロー解除する。"
drive-read: "ドライブを見る。"
drive-write: "ドライブを操作する。"
notification-read: "通知を見る。"
notification-write: "通知を操作する。"
account-read: "Afficher les informations du compte"
account-write: "Modifications des informations du compte"
note-write: "Publications."
reaction-write: "Ajout et suppression de réactions."
following-write: "Sabonner et se désabonner."
drive-read: "Lecture du Drive."
drive-write: "Téléversement/suppression des fichiers de votre Lecteur."
notification-read: "Lire vos notifications."
notification-write: "Gestion de vos notifications."

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@@ -832,7 +832,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -950,13 +951,20 @@ desktop/views/components/settings.2fa.vue:
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
@@ -966,8 +974,12 @@ common/views/components/drive-settings.vue:
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
@@ -1196,6 +1208,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
@@ -1524,6 +1539,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "守護神セキュリティ"
signin: "こんな感じでサインインしたらしいで"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了したで!"
failed: "なんか設定に失敗したで。トークンを間違えとらんか確認してや。"
info: "次のサインインからは、パスワードに加えてデバイスに出とるトークンを入力してな。"
desktop/views/components/settings.api.vue:
intro: "APIを利用するには、上記のトークンを「i」っちゅうキーでパラメータに付加してリクエストしてや。"
caution: "アカウントを不正利用されるかも知れんから、このトークンは第三者に教えたらあかんで(アプリなどにも入力しんといてな)。"
regeneration-of-token: "万が一このトークン漏れたとかその可能性があったらトークンを再生成できるで。"
regenerate-token: "トークンを再生成"
token: "トークン:"
enter-password: "パスワードを入力してや"
common/views/components/api-settings.vue:
intro: "API使うんやったらこのトークンを「i」っちゅうパラメータにくっつけてリクエストできるで。"
caution: "アカウント勝手にいじられるかも知れんから、このトークンは教えたらあかんし、アプリにも書いたらあかんで(これはフリちゃうで)"
regeneration-of-token: "トークン漏れてもうたんやったらもっかい生成できるで。"
regenerate-token: "トークンもっかい生成"
token: "Token:"
enter-password: "パスワードを入てや"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送る'
sending: '応答待っとる'
response: 'こんなん返ってきたわ'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはあらへんで"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
in-use: "使うとる"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはおらんで"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "今のパスワードを入れてや"
@@ -938,7 +950,7 @@ desktop/views/pages/admin/admin.vue:
dashboard: "ダッシュボード"
users: "ユーザー"
update: "更新"
announcements: "お知らせ"
announcements: "知っといてや"
hashtags: "ハッシュタグ"
desktop/views/pages/admin/admin.dashboard.vue:
dashboard: "ダッシュボード"
@@ -967,7 +979,7 @@ desktop/views/pages/admin/admin.unverify-user.vue:
unverify: "公式アカウントにはさせへんで"
unverified: "公式アカウントを解除したで"
desktop/views/pages/admin/admin.announcements.vue:
announcements: "お知らせ"
announcements: "知っといてや"
desktop/views/pages/admin/admin.hashtags.vue:
hided-tags: "Hidden Tags"
desktop/views/pages/deck/deck.tl-column.vue:
@@ -979,10 +991,10 @@ desktop/views/pages/deck/deck.user-column.vue:
following: "フォロー"
followers: "フォロワー"
images: "画像"
activity: "アクティビティ"
activity: "やっとること"
timeline: "タイムライン"
pinned-notes: "ピン留めされた投稿"
push-to-a-list: "リストに追加"
pinned-notes: "ピン留めしはった投稿"
push-to-a-list: "リストに入れたる"
desktop/views/pages/stats/stats.vue:
all-users: "全てのユーザー"
original-users: "ここの人らだけ"
@@ -1035,7 +1047,7 @@ desktop/views/pages/user/user.friends.vue:
no-users: "よう話すツレは居らん"
desktop/views/pages/user/user.vue:
is-suspended: "このユーザーはあかんわ。凍結されとる。"
last-used-at: "最終アクセス"
last-used-at: "最後いつ来はった?"
desktop/views/pages/user/user.photos.vue:
title: "写真"
loading: "読み込んどります"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしとるで"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加したで。"
desktop/views/pages/user/user.header.vue:
@@ -1117,8 +1132,8 @@ mobile/views/components/drive.file-detail.vue:
hash: "ハッシュ(md5)"
exif: "EXIF"
nsfw: "ちょっと見せられへんわ"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
mark-as-sensitive: "見たらあかん感じにしとく"
unmark-as-sensitive: "やっぱ見せたるわ"
mobile/views/components/media-image.vue:
sensitive: "見たらあかんで"
click-to-show: "押してみ、見せたるわ"
@@ -1312,7 +1327,7 @@ mobile/views/pages/settings.vue:
signout: "さいなら"
sound: "サウンド"
enable-sounds: "サウンド鳴らす"
mark-as-read-all-unread-notes: "すべての投稿を既読にする"
mark-as-read-all-unread-notes: "全部もう読んだわ"
mobile/views/pages/user.vue:
follows-you: "フォローされとるで"
following: "フォロー"
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーはあかんわ。凍結されとる。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近儲かりまっか?"
images: "画像"
@@ -1369,27 +1388,27 @@ dev/views/index.vue:
manage-apps: "アプリの管理"
dev/views/apps.vue:
manage-apps: "アプリを管理"
create-app: "アプリ作"
app-missing: "アプリなし"
create-app: "アプリ作"
app-missing: "アプリあらへん"
dev/views/new-app.vue:
create-app: "アプリケーションの作成"
app-name: "アプリケーション"
app-name-desc: "あたのアプリの名。"
app-name-ex: "ex) Misskey for iOS"
app-overview: "アプリの概要"
app-desc: "あたのアプリの簡単な説明や紹介。"
app-desc-ex: "ex) Misskey iOSクライアント。"
callback-url: "コールバックURL (オプション)"
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
create-app: "アプリケーション作る"
app-name: "アプリケーションの名前"
app-name-desc: "あたのアプリの名。"
app-name-ex: "ex) 関西ミスキー保安協会"
app-overview: "このアプリどんなん?"
app-desc: "あたのアプリどんなんか教えて"
app-desc-ex: "ex) 関西人なら誰でも口ずさめるこのCMがついにMisskeyへ。"
callback-url: "コールバックURL (無くてもええで)"
callback-url-desc: "ユーザーが認証フォームで認証した後どこに連れてくかを設定できるで"
authority: "権限"
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
account-read: "アカウントの情報を見る。"
account-write: "アカウントの情報を操作する。"
note-write: "投稿する。"
reaction-write: "リアクションしたりリアクションをキャンセルする。"
following-write: "フォローしたりフォロー解除する。"
drive-read: "ドライブを見る。"
drive-write: "ドライブを操作する。"
notification-read: "通知を見る。"
notification-write: "通知を操作する。"
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
account-read: "アカウントの情報見せて"
account-write: "アカウントの情報いじらせて"
note-write: "投稿させて"
reaction-write: "リアクションしたりそれをキャンセルさせて"
following-write: "フォローとかフォロー解除させて"
drive-read: "ドライブ見せて"
drive-write: "ドライブいじらせて"
notification-read: "通知見せて"
notification-write: "通知いじらせて"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "Profiel"
notification: "Melding"
apps: "Apps"
mute: "Dempen"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "Beveiliging"
signin: "Inloggeschiedenis"
password: "Wachtwoord"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "Instellen voltooid!"
failed: "Instellen mislukt. Zorg ervoor dat de sleutel juist is."
info: "Vanaf nu moet je ook de op je apparaat getoonde sleutel tonen bij het inloggen op Misskey."
desktop/views/components/settings.api.vue:
intro: "Als je toegang wilt tot de API, stel deze sleutel dan in als 'i' bij de verzoekparameters."
caution: "Laat deze sleutel niet zien aan derde partijen (en voer hem nergens anders in dan hier), anders kan je account gehackt worden."
regeneration-of-token: "Mocht deze sleutel tóch uitlekken, dan kun je hem opnieuw genereren."
regenerate-token: "Sleutel opnieuw genereren"
token: "Sleutel:"
enter-password: "Voer je wachtwoord in"
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "Geen gedempte gebruikers"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "Wachtwoord wijzigen"
enter-current-password: "Voer je huidige wachtwoord in"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "Dempen"
muted: "Dempend"
unmute: "Ontdempen"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "Tijdlijn"
media: "Media"
is-suspended: "Dit account is geschorst."
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "Recente notities"
images: "Afbeeldingen"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "Notifikasjon"
apps: "Apper"
mute: "Demp"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "Passord"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "Media"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "Nylige innlegg"
images: "Bilder"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "Profil"
notification: "Powiadomienia"
apps: "Aplikacje"
mute: "Wyciszanie"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "Bezpieczeństwo"
signin: "Historia logowań"
password: "Hasło"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "Pomyślnie ukończono konfigurację!"
failed: "Nie udało się skonfigurować uwierzytelniania dwuetapowego, upewnij się że wprowadziłeś prawidłowy token."
info: "Od teraz, wprowadzaj token wyświetlany na urządzeniu przy każdym logowaniu do Misskey."
desktop/views/components/settings.api.vue:
intro: "Aby uzyskać dostęp do API, ustaw ten token jako klucz 'i' parametrów żądań."
caution: "Nie pokazuj tego tokenu osobom trzecim (nie wprowadzaj go nigdzie indziej), aby konto nie trafiło w niepowołane ręce."
regeneration-of-token: "W przypadku wycieku tokenu, możesz wygenerować nowy."
regenerate-token: "Wygeneruj nowy token"
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "Wprowadź hasło"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "Brak zautoryzowanych aplikacji"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "Brak wyciszonych użytkowników"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "Zmień hasło"
enter-current-password: "Wprowadź obecne hasło"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "Wycisz"
muted: "Wyciszyłeś"
unmute: "Cofnij wyciszenie"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "Dodaj do listy"
list-pushed: "Dodałeś(-aś) {user} do {list}."
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "Oś czasu"
media: "Multimedia"
is-suspended: "To konto zostało zablokowane"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "Ostatnie wpisy"
images: "Zdjęcia"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "Linha do tempo"
media: "Mídia"
is-suspended: "Esta conta foi suspensa"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "Notas recentes"
images: "Imagens"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
profile: "プロフィール"
notification: "通知"
apps: "アプリ"
mute: "ミュート"
mute-and-block: "ミュート/ブロック"
blocking: "ブロック"
security: "セキュリティ"
signin: "サインイン履歴"
password: "パスワード"
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
success: "設定が完了しました!"
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
desktop/views/components/settings.api.vue:
common/views/components/api-settings.vue:
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
regenerate-token: "トークンを再生成"
token: "Token:"
enter-password: "パスワードを入力してください"
console:
title: 'APIコンソール'
endpoint: 'エンドポイント'
parameter: 'パラメータ'
send: '送信'
sending: '応答待ち'
response: '結果'
desktop/views/components/settings.apps.vue:
no-apps: "連携しているアプリケーションはありません"
common/views/components/drive-settings.vue:
max: "容量"
in-use: "使用中"
stats: "統計"
desktop/views/components/settings.mute.vue:
no-users: "ミュートしているユーザーはいません"
common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック"
mute: "ミュート"
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
desktop/views/components/settings.password.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
mute: "ミュートする"
muted: "ミュートしています"
unmute: "ミュート解除"
block: "ブロックする"
unblock: "ブロック解除"
block-confirm: "このユーザーをブロックしますか?"
push-to-a-list: "リストに追加"
list-pushed: "{user}を{list}に追加しました。"
desktop/views/pages/user/user.header.vue:
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
timeline: "タイムライン"
media: "メディア"
is-suspended: "このユーザーは凍結されています。"
mute: "ミュート"
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

123
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "10.31.0",
"version": "10.36.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -544,9 +544,9 @@
}
},
"@types/mocha": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.3.tgz",
"integrity": "sha512-C1wVVr7xhKu6c3Mb27dFzNYR05qvHwgtpN+JOYTGc1pKA7dCEDDYpscn7kul+bCUwa3NoGDbzI1pdznSOa397w=="
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz",
"integrity": "sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww=="
},
"@types/mongodb": {
"version": "3.1.12",
@@ -691,9 +691,12 @@
"integrity": "sha512-XWwqRvaSsf4yq/4SYL5/7n5i2RWqf+jtIRlasj65cuZZDZ01wtkDfAIxrEpYcLvzT1HMqBmsnMEcZjK+OMeojQ=="
},
"@types/speakeasy": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.2.tgz",
"integrity": "sha512-h8KW3wSd3/l4oKRGYPxExCaos5VmjcnwDG3RK25tfcoWQR9iLmM9UbwvF1Pd+UT5aY1Z3LdQGt4xU0u9Zk/C2Q=="
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.3.tgz",
"integrity": "sha512-lDc49Aec4WnQPRhW3n90ct14CH0Zyrj48RGMK1SSQP6BOf8HamWg+PG9uj1DVnaa6t+lhQU1j1lhGA7d9baxWw==",
"requires": {
"@types/node": "*"
}
},
"@types/superagent": {
"version": "3.8.4",
@@ -1259,9 +1262,9 @@
}
},
"apexcharts": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-2.1.5.tgz",
"integrity": "sha512-4eKh2HyQVr5ct2t7cWkvDSUyJM9KGw6dRHAlojqo6HJz+XtrnnvL8uP18rzNq4M80P3Ul+yDjpCt5EXlYPfo5Q==",
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-2.1.9.tgz",
"integrity": "sha512-Qs/jLUa03wqPR53yMk8QAwq+qrX/Odc3IIXH2WVVjdWyFXS1lYzGSDbVcVDnOKkxoLdAlzPI3icb2bMjskwfXQ==",
"requires": {
"babel-polyfill": "^6.26.0",
"core-js": "^2.5.7",
@@ -3332,12 +3335,12 @@
}
},
"data-urls": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz",
"integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
"integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
"requires": {
"abab": "^2.0.0",
"whatwg-mimetype": "^2.1.0",
"whatwg-mimetype": "^2.2.0",
"whatwg-url": "^7.0.0"
}
},
@@ -4755,9 +4758,9 @@
}
},
"eslint": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz",
"integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==",
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.8.0.tgz",
"integrity": "sha512-Zok6Bru3y2JprqTNm14mgQ15YQu/SMDkWdnmHfFg770DIUlmMFd/gqqzCHekxzjHZJxXv3tmTpH0C1icaYJsRQ==",
"requires": {
"@babel/code-frame": "^7.0.0",
"ajv": "^6.5.3",
@@ -5311,9 +5314,9 @@
}
},
"file-type": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.1.0.tgz",
"integrity": "sha512-fkjfXnqBRrdUFTS6opakWyMXb+uzDv8zOCqjSOWPbzMLnYnmnUEv/RNY9igkk4nc8TVL44Xd1OCC2fJXH3eb7Q=="
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.2.0.tgz",
"integrity": "sha512-eqX81S1PWdLDPW39yyB214TVVOsUQjSmPcyUjeVH6ksH+94Y2YA/ItiIwa53rJiSofJZLK6lGsuCE3rwt0vp4w=="
},
"filename-regex": {
"version": "2.0.1",
@@ -7224,14 +7227,14 @@
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
},
"html-minifier": {
"version": "3.5.20",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz",
"integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==",
"version": "3.5.21",
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
"integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
"requires": {
"camel-case": "3.0.x",
"clean-css": "4.2.x",
"commander": "2.17.x",
"he": "1.1.x",
"he": "1.2.x",
"param-case": "2.1.x",
"relateurl": "0.2.x",
"uglify-js": "3.4.x"
@@ -7242,14 +7245,10 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
},
"uglify-js": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
"requires": {
"commander": "~2.17.1",
"source-map": "~0.6.1"
}
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
}
}
},
@@ -8355,9 +8354,9 @@
"integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ=="
},
"jsdom": {
"version": "12.2.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-12.2.0.tgz",
"integrity": "sha512-QPOggIJ8fquWPLaYYMoh+zqUmdphDtu1ju0QGTitZT1Yd8I5qenPpXM1etzUegu3MjVp8XPzgZxdn8Yj7e40ig==",
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.0.0.tgz",
"integrity": "sha512-Kmq4ASMNkgpY+YufE322EnIKoiz0UWY2DRkKlU7d5YrIW4xiVRhWFrZV1fr6w/ZNxQ50wGAH5gGRzydgnmkkvw==",
"requires": {
"abab": "^2.0.0",
"acorn": "^6.0.2",
@@ -8378,6 +8377,7 @@
"symbol-tree": "^3.2.2",
"tough-cookie": "^2.4.3",
"w3c-hr-time": "^1.0.1",
"w3c-xmlserializer": "^1.0.0",
"webidl-conversions": "^4.0.2",
"whatwg-encoding": "^1.0.5",
"whatwg-mimetype": "^2.2.0",
@@ -15573,9 +15573,9 @@
}
},
"ts-loader": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.2.2.tgz",
"integrity": "sha512-vM/TrEKXBqRYq5yLatsXyKFnYSpv53klmGtrILGlNqcMsxPVi8+e4yr1Agbu9oMZepx/4szDVn5QpFo83IQdQg==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.0.tgz",
"integrity": "sha512-lGSNs7szRFj/rK9T1EQuayE3QNLg6izDUxt5jpmq0RG1rU2bapAt7E7uLckLCUPeO1jwxCiet2oRaWovc53UAg==",
"requires": {
"chalk": "^2.3.0",
"enhanced-resolve": "^4.0.0",
@@ -15687,16 +15687,17 @@
}
},
"typescript": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz",
"integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA=="
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.4.tgz",
"integrity": "sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q=="
},
"typescript-eslint-parser": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-20.0.0.tgz",
"integrity": "sha512-HZEoGA+LnS3etUlVAPX6I8sZ7872Yb0vPvQv6QDCIE44KD3bFmvPEQ4LbiD+qGkcxh6segjVK0v3rxpb2R6oSA==",
"version": "20.1.1",
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-20.1.1.tgz",
"integrity": "sha512-IJhpqHK60Pz2J5pe8rJUQ10DcMcGwhQnvRFcPV79coEV3bpNfSiHkgpS+sf6zx2ANDWgBhmtZbK9ICOy+v3FKA==",
"requires": {
"eslint": "4.19.1",
"eslint-visitor-keys": "^1.0.0",
"typescript-estree": "2.1.0"
},
"dependencies": {
@@ -15829,7 +15830,7 @@
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"ignore": {
@@ -15911,6 +15912,22 @@
}
}
},
"uglify-js": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
"requires": {
"commander": "~2.17.1",
"source-map": "~0.6.1"
},
"dependencies": {
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
}
}
},
"uglify-to-browserify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
@@ -16763,6 +16780,16 @@
"browser-process-hrtime": "^0.1.2"
}
},
"w3c-xmlserializer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.0.0.tgz",
"integrity": "sha512-0et1+9uXYiIRAecx1D5Z1nk60+vimniGdIKl4XjeqkWi6acoHNlXMv1VR5jV+jF4ooeO08oWbYxeAJOcon1oMA==",
"requires": {
"domexception": "^1.0.1",
"webidl-conversions": "^4.0.2",
"xml-name-validator": "^3.0.0"
}
},
"ware": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz",
@@ -16808,9 +16835,9 @@
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
},
"webpack": {
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.0.tgz",
"integrity": "sha512-Osh/3U9y4swhEKDjy8fF48v2Qx5VC6VvdQ8bEm1HMaVVddiQBw4+mIyDrzVcVRCPT/+4uJFOcklPuoB+I3Zw0w==",
"version": "4.23.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.1.tgz",
"integrity": "sha512-iE5Cu4rGEDk7ONRjisTOjVHv3dDtcFfwitSxT7evtYj/rANJpt1OuC/Kozh1pBa99AUBr1L/LsaNB+D9Xz3CEg==",
"requires": {
"@webassemblyjs/ast": "1.7.10",
"@webassemblyjs/helper-module-context": "1.7.10",

View File

@@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.32.0",
"clientVersion": "1.0.11138",
"version": "10.37.0",
"clientVersion": "1.0.11314",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@@ -58,7 +58,7 @@
"@types/koa__cors": "2.2.3",
"@types/minio": "7.0.0",
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.3",
"@types/mocha": "5.2.5",
"@types/mongodb": "3.1.12",
"@types/ms": "0.7.30",
"@types/node": "10.12.0",
@@ -74,7 +74,7 @@
"@types/sharp": "0.21.0",
"@types/showdown": "1.7.5",
"@types/single-line-log": "1.1.0",
"@types/speakeasy": "2.0.2",
"@types/speakeasy": "2.0.3",
"@types/systeminformation": "3.23.0",
"@types/tinycolor2": "1.4.1",
"@types/tmp": "0.0.33",
@@ -84,7 +84,7 @@
"@types/websocket": "0.0.40",
"@types/ws": "6.0.1",
"animejs": "2.2.0",
"apexcharts": "2.1.5",
"apexcharts": "2.1.9",
"autobind-decorator": "2.1.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
@@ -108,11 +108,11 @@
"elasticsearch": "15.1.1",
"emojilib": "2.3.0",
"escape-regexp": "0.0.1",
"eslint": "5.7.0",
"eslint": "5.8.0",
"eslint-plugin-vue": "4.7.1",
"eventemitter3": "3.1.0",
"file-loader": "2.0.0",
"file-type": "10.1.0",
"file-type": "10.2.0",
"fuckadblock": "3.2.1",
"gulp": "3.9.1",
"gulp-cssnano": "2.1.3",
@@ -129,13 +129,13 @@
"gulp-uglify": "3.0.1",
"gulp-util": "3.0.8",
"hard-source-webpack-plugin": "0.12.0",
"html-minifier": "3.5.20",
"html-minifier": "3.5.21",
"http-signature": "1.2.0",
"insert-text-at-cursor": "0.1.1",
"is-root": "2.0.0",
"is-url": "1.2.4",
"js-yaml": "3.12.0",
"jsdom": "12.2.0",
"jsdom": "13.0.0",
"json5": "2.1.0",
"json5-loader": "1.0.1",
"koa": "2.6.1",
@@ -201,11 +201,11 @@
"textarea-caret": "3.1.0",
"tinycolor2": "1.4.1",
"tmp": "0.0.33",
"ts-loader": "5.2.2",
"ts-loader": "5.3.0",
"ts-node": "7.0.1",
"tslint": "5.10.0",
"typescript": "3.1.3",
"typescript-eslint-parser": "20.0.0",
"typescript": "3.1.4",
"typescript-eslint-parser": "20.1.1",
"uglify-es": "3.3.9",
"url-loader": "1.1.2",
"uuid": "3.3.2",
@@ -229,17 +229,10 @@
"vuex-persistedstate": "2.5.4",
"web-push": "3.3.3",
"webfinger.js": "2.6.6",
"webpack": "4.23.0",
"webpack": "4.23.1",
"webpack-cli": "3.1.2",
"websocket": "1.0.28",
"ws": "6.1.0",
"xev": "2.0.1"
},
"greenkeeper": {
"ignore": [
"deepcopy",
"cafy",
"@types/gulp"
]
}
}

View File

@@ -0,0 +1,72 @@
<template>
<ui-card>
<div slot="title">%fa:key% API</div>
<section class="fit-top">
<ui-input :value="$store.state.i.token" readonly>
<span>%i18n:@token%</span>
</ui-input>
<p>%i18n:@intro%</p>
<ui-info warn>%i18n:@caution%</ui-info>
<p>%i18n:@regeneration-of-token%</p>
<ui-button @click="regenerateToken">%fa:sync-alt% %i18n:@regenerate-token%</ui-button>
</section>
<section>
<header>%fa:terminal% %i18n:@console.title%</header>
<ui-input v-model="endpoint">
<span>%i18n:@console.endpoint%</span>
</ui-input>
<ui-textarea v-model="body">
<span>%i18n:@console.parameter% (JSON or JSON5)</span>
</ui-textarea>
<ui-button @click="send" :disabled="sending">
<template v-if="sending">%i18n:@console.sending%</template>
<template v-else>%fa:paper-plane% %i18n:@console.send%</template>
</ui-button>
<ui-textarea v-if="res" v-model="res" readonly tall>
<span>%i18n:@console.response%</span>
</ui-textarea>
</section>
</ui-card>
</template>
<script lang="ts">
import Vue from 'vue';
import * as JSON5 from 'json5';
export default Vue.extend({
data() {
return {
endpoint: '',
body: '{}',
res: null,
sending: false
};
},
methods: {
regenerateToken() {
(this as any).apis.input({
title: '%i18n:@enter-password%',
type: 'password'
}).then(password => {
(this as any).api('i/regenerate_token', {
password: password
});
});
},
send() {
this.sending = true;
(this as any).api(this.endpoint, JSON5.parse(this.body)).then(res => {
this.sending = false;
this.res = JSON5.stringify(res, null, 2);
}, err => {
this.sending = false;
this.res = JSON5.stringify(err, null, 2);
});
}
}
});
</script>

View File

@@ -14,7 +14,8 @@
</ol>
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
<span class="emoji">{{ emoji.emoji }}</span>
<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span class="emoji" v-else>{{ emoji.emoji }}</span>
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
<span class="alias" v-if="emoji.alias">({{ emoji.alias }})</span>
</li>
@@ -169,22 +170,45 @@ export default Vue.extend({
}
} else if (this.type == 'emoji') {
const matched = [];
const max = 30;
const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis;
customEmojis.some(x => {
if (x.name.startsWith(this.q)) matched.push({
name: x.name,
emoji: `:${x.name}:`,
url: x.url
});
return matched.length == max;
});
customEmojis.some(x => {
const alias = (x.aliases || []).find(a => a.startsWith(this.q));
if (alias) matched.push({
alias: x.name,
name: alias,
emoji: `:${x.name}:`,
url: x.url
});
return matched.length == max;
});
emjdb.some(x => {
if (x.name.indexOf(this.q) == 0 && !x.alias && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
return matched.length == 30;
return matched.length == max;
});
if (matched.length < 30) {
if (matched.length < max) {
emjdb.some(x => {
if (x.name.indexOf(this.q) == 0 && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
return matched.length == 30;
return matched.length == max;
});
}
if (matched.length < 30) {
if (matched.length < max) {
emjdb.some(x => {
if (x.name.indexOf(this.q) > -1 && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
return matched.length == 30;
return matched.length == max;
});
}
this.emojis = matched;
}
},
@@ -340,6 +364,10 @@ export default Vue.extend({
margin 0 4px 0 0
width 24px
> img
width 24px
vertical-align bottom
.name
color var(--autocompleteItemText)

View File

@@ -0,0 +1,19 @@
<template>
<div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj">
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
<ui-button @click="() => $emit('retry')">%i18n:common.error.retry%</ui-button>
</div>
</template>
<style lang="stylus" scoped>
.wjqjnyhzogztorhrdgcpqlkxhkmuetgj
max-width 350px
margin 0 auto
padding 32px
text-align center
color var(--text)
> p
margin 0 0 8px 0
</style>

View File

@@ -1,5 +1,8 @@
import Vue from 'vue';
import muteAndBlock from './mute-and-block.vue';
import error from './error.vue';
import apiSettings from './api-settings.vue';
import driveSettings from './drive-settings.vue';
import profileEditor from './profile-editor.vue';
import noteSkeleton from './note-skeleton.vue';
@@ -44,9 +47,13 @@ import uiTextarea from './ui/textarea.vue';
import uiSwitch from './ui/switch.vue';
import uiRadio from './ui/radio.vue';
import uiSelect from './ui/select.vue';
import uiInfo from './ui/info.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
Vue.component('mk-mute-and-block', muteAndBlock);
Vue.component('mk-error', error);
Vue.component('mk-api-settings', apiSettings);
Vue.component('mk-drive-settings', driveSettings);
Vue.component('mk-profile-editor', profileEditor);
Vue.component('mk-note-skeleton', noteSkeleton);
@@ -91,5 +98,6 @@ Vue.component('ui-textarea', uiTextarea);
Vue.component('ui-switch', uiSwitch);
Vue.component('ui-radio', uiRadio);
Vue.component('ui-select', uiSelect);
Vue.component('ui-info', uiInfo);
Vue.component('form-button', formButton);
Vue.component('form-radio', formRadio);

View File

@@ -3,7 +3,6 @@ import * as emojilib from 'emojilib';
import { length } from 'stringz';
import parse from '../../../../../mfm/parse';
import getAcct from '../../../../../misc/acct/render';
import { url } from '../../../config';
import MkUrl from './url.vue';
import MkGoogle from './google.vue';
import { concat } from '../../../../../prelude/array';
@@ -186,6 +185,21 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'emoji': {
//#region カスタム絵文字
const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis;
const customEmoji = customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
if (customEmoji) {
return [createElement('img', {
attrs: {
src: customEmoji.url,
alt: token.emoji,
title: token.emoji,
style: 'height: 2.5em; vertical-align: middle;'
}
})];
}
//#endregion
const emoji = emojilib.lib[token.emoji];
return [createElement('span', emoji ? emoji.char : token.content)];
}

View File

@@ -0,0 +1,52 @@
<template>
<ui-card>
<div slot="title">%fa:ban% %i18n:@mute-and-block%</div>
<section>
<header>%i18n:@mute%</header>
<ui-info v-if="!muteFetching && mute.length == 0">%i18n:@no-muted-users%</ui-info>
<div class="users" v-if="mute.length != 0">
<div v-for="user in mute" :key="user.id">
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
</div>
</div>
</section>
<section>
<header>%i18n:@block%</header>
<ui-info v-if="!blockFetching && block.length == 0">%i18n:@no-blocked-users%</ui-info>
<div class="users" v-if="block.length != 0">
<div v-for="user in block" :key="user.id">
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
</div>
</div>
</section>
</ui-card>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
data() {
return {
muteFetching: true,
blockFetching: true,
mute: [],
block: []
};
},
mounted() {
(this as any).api('mute/list').then(mute => {
this.mute = mute.map(x => x.mutee);
this.muteFetching = false;
});
(this as any).api('blocking/list').then(blocking => {
this.block = blocking.map(x => x.blockee);
this.blockFetching = false;
});
}
});
</script>

View File

@@ -2,11 +2,11 @@
<header class="bvonvjxbwzaiskogyhbwgyxvcgserpmu">
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
<router-link class="name" :to="note.user | userPage" v-user-preview="note.user.id">{{ note.user | userName }}</router-link>
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
<span class="is-admin" v-if="note.user.isAdmin">admin</span>
<span class="is-bot" v-if="note.user.isBot">bot</span>
<span class="is-cat" v-if="note.user.isCat">cat</span>
<span class="username"><mk-acct :user="note.user"/></span>
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
<div class="info">
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
@@ -68,10 +68,6 @@ export default Vue.extend({
&:hover
text-decoration underline
> .is-verified
margin-right 8px
color #4dabf7
> .is-admin
> .is-bot
> .is-cat
@@ -95,6 +91,10 @@ export default Vue.extend({
color var(--noteHeaderAcct)
flex-shrink 2147483647
> .is-verified
margin 0 .5em 0 0
color #4dabf7
> .info
margin-left auto
font-size 0.9em

View File

@@ -0,0 +1,43 @@
<template>
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
<i v-if="warn">%fa:exclamation-triangle%</i>
<i v-else>%fa:info-circle%</i>
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
warn: {
type: Boolean,
required: false,
default: false
},
},
});
</script>
<style lang="stylus" scoped>
.ymxyweixqwsxauxldgpvecjepnwxbylu
margin 16px 0
padding 16px
font-size 90%
background var(--infoBg)
color var(--infoFg)
&.warn
background var(--infoWarnBg)
color var(--infoWarnFg)
&:first-child
margin-top 0
&:last-child
margin-bottom 0
> i
margin-right 4px
</style>

View File

@@ -1,17 +1,17 @@
<template>
<div class="ui-textarea" :class="{ focused, filled }">
<div class="ui-textarea" :class="{ focused, filled, tall }">
<div class="input">
<span class="label" ref="label"><slot></slot></span>
<textarea ref="input"
:value="value"
:required="required"
:readonly="readonly"
:pattern="pattern"
:autocomplete="autocomplete"
@input="$emit('input', $event.target.value)"
@focus="focused = true"
@blur="focused = false">
</textarea>
:value="value"
:required="required"
:readonly="readonly"
:pattern="pattern"
:autocomplete="autocomplete"
@input="$emit('input', $event.target.value)"
@focus="focused = true"
@blur="focused = false"
></textarea>
</div>
<div class="text"><slot name="text"></slot></div>
</div>
@@ -41,7 +41,12 @@ export default Vue.extend({
autocomplete: {
type: String,
required: false
}
},
tall: {
type: Boolean,
required: false,
default: false
},
},
data() {
return {
@@ -66,6 +71,9 @@ export default Vue.extend({
root(fill)
margin 42px 0 32px 0
&:last-child
margin-bottom 0
> .input
padding 12px
@@ -157,6 +165,11 @@ root(fill)
left 0 !important
transform scale(0.75)
&.tall
> .input
> textarea
min-height 200px
.ui-textarea.fill
root(true)

View File

@@ -222,13 +222,15 @@ class Autocomplete {
const trimmedBefore = before.substring(0, before.lastIndexOf(':'));
const after = source.substr(caret);
if (value.startsWith(':')) value = value + ' ';
// 挿入
this.text = trimmedBefore + value + after;
// キャレットを戻す
this.vm.$nextTick(() => {
this.textarea.focus();
const pos = trimmedBefore.length + 1;
const pos = trimmedBefore.length + (value.startsWith(':') ? value.length : 1);
this.textarea.setSelectionRange(pos, pos);
});
}

View File

@@ -67,12 +67,12 @@ export default Vue.extend({
text: '%i18n:@contextmenu.rename%',
icon: '%fa:i-cursor%',
action: this.rename
}/*, null, {
}, null, {
type: 'item',
text: '%i18n:common.delete%',
icon: '%fa:R trash-alt%',
action: this.deleteFolder
}*/], {
}], {
closed: () => {
this.isContextmenuShowing = false;
}
@@ -207,7 +207,9 @@ export default Vue.extend({
},
deleteFolder() {
alert('not implemented yet');
(this as any).api('drive/folders/delete', {
folderId: this.folder.id
});
}
}
});

View File

@@ -98,7 +98,7 @@ export default Vue.extend({
hierarchyFolders: [],
selectedFiles: [],
uploadings: [],
connection: null
connection: null,
/**
* ドロップされようとしているか
@@ -122,6 +122,7 @@ export default Vue.extend({
this.connection.on('fileDeleted', this.onStreamDriveFileDeleted);
this.connection.on('folderCreated', this.onStreamDriveFolderCreated);
this.connection.on('folderUpdated', this.onStreamDriveFolderUpdated);
this.connection.on('folderDeleted', this.onStreamDriveFolderDeleted);
if (this.initFolder) {
this.move(this.initFolder);
@@ -182,6 +183,10 @@ export default Vue.extend({
}
},
onStreamDriveFolderDeleted(folderId) {
this.removeFolder(folderId);
},
onChangeUploaderUploads(uploads) {
this.uploadings = uploads;
},

View File

@@ -40,8 +40,8 @@ export default Vue.extend({
mounted() {
this.connection = (this as any).os.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollow);
this.connection.on('unfollow', this.onUnfollow);
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
@@ -49,17 +49,11 @@ export default Vue.extend({
},
methods: {
onFollow(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
}
},
onUnfollow(user) {
onFollowChange(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
this.$forceUpdate();
}
},

View File

@@ -4,10 +4,7 @@
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
<div v-if="!fetching && requestInitPromise != null" class="error">
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
<ui-button @click="resolveInitPromise">%i18n:common.error.retry%</ui-button>
</div>
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
@@ -215,16 +212,6 @@ export default Vue.extend({
> *
transition transform .3s ease, opacity .3s ease
> .error
max-width 300px
margin 0 auto
padding 32px
text-align center
color var(--text)
> p
margin 0 0 8px 0
> .placeholder
padding 32px
opacity 0.3

View File

@@ -1,7 +1,7 @@
<template>
<div class="2fa">
<p>%i18n:@intro%<a href="%i18n:@url%" target="_blank">%i18n:@detail%</a></p>
<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:@caution%</p></div>
<p style="margin-top:0;">%i18n:@intro%<a href="%i18n:@url%" target="_blank">%i18n:@detail%</a></p>
<ui-info warn>%i18n:@caution%</ui-info>
<p v-if="!data && !$store.state.i.twoFactorEnabled"><ui-button @click="register">%i18n:@register%</ui-button></p>
<template v-if="$store.state.i.twoFactorEnabled">
<p>%i18n:@already-registered%</p>
@@ -72,9 +72,3 @@ export default Vue.extend({
}
});
</script>
<style lang="stylus" scoped>
.2fa
color #4a535a
</style>

View File

@@ -1,40 +0,0 @@
<template>
<div class="root api">
<ui-input :value="$store.state.i.token" readonly>
<span>%i18n:@token%</span>
</ui-input>
<p>%i18n:@intro%</p>
<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:@caution%</p></div>
<p>%i18n:@regeneration-of-token%</p>
<ui-button @click="regenerateToken">%i18n:@regenerate-token%</ui-button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
methods: {
regenerateToken() {
(this as any).apis.input({
title: '%i18n:@enter-password%',
type: 'password'
}).then(password => {
(this as any).api('i/regenerate_token', {
password: password
});
});
}
}
});
</script>
<style lang="stylus" scoped>
.root.api
code
display inline-block
padding 4px 6px
color #555
background #eee
border-radius 2px
</style>

View File

@@ -1,8 +1,6 @@
<template>
<div class="root">
<div class="none ui info" v-if="!fetching && apps.length == 0">
<p>%fa:info-circle%%i18n:@no-apps%</p>
</div>
<ui-info v-if="!fetching && apps.length == 0">%i18n:@no-apps%</ui-info>
<div class="apps" v-if="apps.length != 0">
<div v-for="app in apps">
<p><b>{{ app.name }}</b></p>

View File

@@ -1,31 +0,0 @@
<template>
<div>
<div class="none ui info" v-if="!fetching && users.length == 0">
<p>%fa:info-circle%%i18n:@no-users%</p>
</div>
<div class="users" v-if="users.length != 0">
<div v-for="user in users" :key="user.id">
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
data() {
return {
fetching: true,
users: []
};
},
mounted() {
(this as any).api('mute/list').then(x => {
this.users = x.users;
this.fetching = false;
});
}
});
</script>

View File

@@ -7,7 +7,7 @@
<p :class="{ active: page == 'notification' }" @mousedown="page = 'notification'">%fa:R bell .fw%%i18n:@notification%</p>
<p :class="{ active: page == 'drive' }" @mousedown="page = 'drive'">%fa:cloud .fw%%i18n:common.drive%</p>
<p :class="{ active: page == 'hashtags' }" @mousedown="page = 'hashtags'">%fa:hashtag .fw%%i18n:@tags%</p>
<p :class="{ active: page == 'mute' }" @mousedown="page = 'mute'">%fa:ban .fw%%i18n:@mute%</p>
<p :class="{ active: page == 'muteAndBlock' }" @mousedown="page = 'muteAndBlock'">%fa:ban .fw%%i18n:@mute-and-block%</p>
<p :class="{ active: page == 'apps' }" @mousedown="page = 'apps'">%fa:puzzle-piece .fw%%i18n:@apps%</p>
<p :class="{ active: page == 'security' }" @mousedown="page = 'security'">%fa:unlock-alt .fw%%i18n:@security%</p>
<p :class="{ active: page == 'api' }" @mousedown="page = 'api'">%fa:key .fw%API</p>
@@ -200,12 +200,9 @@
</section>
</ui-card>
<ui-card class="mute" v-show="page == 'mute'">
<div slot="title">%fa:ban% %i18n:@mute%</div>
<section>
<x-mute/>
</section>
</ui-card>
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
<mk-mute-and-block/>
</div>
<ui-card class="apps" v-show="page == 'apps'">
<div slot="title">%fa:puzzle-piece% %i18n:@apps%</div>
@@ -235,12 +232,9 @@
</section>
</ui-card>
<ui-card class="api" v-show="page == 'api'">
<div slot="title">%fa:key% API</div>
<section class="fit-top">
<x-api/>
</section>
</ui-card>
<div class="api" v-show="page == 'api'">
<mk-api-settings/>
</div>
<ui-card class="other" v-show="page == 'other'">
<div slot="title">%fa:info-circle% %i18n:@about%</div>
@@ -292,10 +286,8 @@
<script lang="ts">
import Vue from 'vue';
import XMute from './settings.mute.vue';
import XPassword from './settings.password.vue';
import X2fa from './settings.2fa.vue';
import XApi from './settings.api.vue';
import XApps from './settings.apps.vue';
import XSignins from './settings.signins.vue';
import XTags from './settings.tags.vue';
@@ -304,10 +296,8 @@ import checkForUpdate from '../../../common/scripts/check-for-update';
export default Vue.extend({
components: {
XMute,
XPassword,
X2fa,
XApi,
XApps,
XSignins,
XTags

View File

@@ -287,7 +287,7 @@ export default Vue.extend({
e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
if (!this.dragging) this.draghover = true;
if (!this.dragging && isDeckColumn) this.draghover = true;
},
onDragleave() {

View File

@@ -8,10 +8,7 @@
</template>
</div>
<div v-if="!fetching && requestInitPromise != null" class="error">
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
<ui-button @click="resolveInitPromise">%i18n:common.error.retry%</ui-button>
</div>
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
<!-- トランジションを有効にするとなぜかメモリリークする -->
<!--<transition-group name="mk-notes" class="transition" ref="notes">-->
@@ -221,13 +218,6 @@ export default Vue.extend({
> *
transition transform .3s ease, opacity .3s ease
> .error
max-width 300px
margin 0 auto
padding 16px
text-align center
color var(--text)
> .placeholder
padding 16px
opacity 0.3

View File

@@ -11,7 +11,11 @@
<div class="action-form">
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
<span v-else>%fa:eye-slash% %i18n:@mute%</span>
</ui-button>
<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
<span v-if="user.isBlocking">%fa:user% %i18n:@unblock%</span>
<span v-else>%fa:user-slash% %i18n:@block%</span>
</ui-button>
<ui-button @click="list">%fa:list% %i18n:@push-to-a-list%</ui-button>
</div>
@@ -66,6 +70,27 @@ export default Vue.extend({
});
},
block() {
if (!window.confirm('%i18n:@block-confirm%')) return;
(this as any).api('blocking/create', {
userId: this.user.id
}).then(() => {
this.user.isBlocking = true;
}, () => {
alert('error');
});
},
unblock() {
(this as any).api('blocking/delete', {
userId: this.user.id
}).then(() => {
this.user.isBlocking = false;
}, () => {
alert('error');
});
},
list() {
const w = (this as any).os.new(MkUserListsWindow);
w.$once('choosen', async list => {
@@ -114,7 +139,6 @@ export default Vue.extend({
> .action-form
padding 16px
text-align center
border-bottom solid 1px var(--faceDivider)
> *
width 100%

View File

@@ -510,6 +510,14 @@ export default class MiOS extends EventEmitter {
return promise;
}
/**
* Misskeyのメタ情報を取得します
*/
@autobind
public getMetaSync() {
return this.meta ? this.meta.data : null;
}
/**
* Misskeyのメタ情報を取得します
* @param force キャッシュを無視するか否か

View File

@@ -17,6 +17,7 @@
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
user: {
@@ -24,6 +25,7 @@ export default Vue.extend({
required: true
}
},
data() {
return {
u: this.user,
@@ -31,28 +33,24 @@ export default Vue.extend({
connection: null
};
},
mounted() {
this.connection = (this as any).os.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollow);
this.connection.on('unfollow', this.onUnfollow);
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onFollow(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
}
},
onUnfollow(user) {
onFollowChange(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
this.$forceUpdate();
}
},
@@ -90,8 +88,6 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
.mk-follow-button
display block
user-select none

View File

@@ -12,7 +12,6 @@ import noteCard from './note-card.vue';
import userCard from './user-card.vue';
import noteDetail from './note-detail.vue';
import followButton from './follow-button.vue';
import muteButton from './mute-button.vue';
import friendsMaker from './friends-maker.vue';
import notification from './notification.vue';
import notifications from './notifications.vue';
@@ -37,7 +36,6 @@ Vue.component('mk-note-card', noteCard);
Vue.component('mk-user-card', userCard);
Vue.component('mk-note-detail', noteDetail);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-mute-button', muteButton);
Vue.component('mk-friends-maker', friendsMaker);
Vue.component('mk-notification', notification);
Vue.component('mk-notifications', notifications);

View File

@@ -1,79 +0,0 @@
<template>
<button
class="mk-mute-button"
:class="{ active: user.isMuted }"
@click="onClick">
<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
<span v-else>%fa:eye% %i18n:@unmute%</span>
</button>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
props: {
user: {
type: Object,
required: true
}
},
methods: {
onClick() {
if (!this.user.isMuted) {
this.mute();
} else {
this.unmute();
}
},
mute() {
(this as any).api('mute/create', { userId: this.user.id})
.then(() => { this.user.isMuted = true })
.catch(() => { alert('error')})
},
unmute() {
(this as any).api('mute/delete', { userId: this.user.id })
.then(() => { this.user.isMuted = false })
.catch(() => { alert('error') })
}
},
})
</script>
<style lang="stylus" scoped>
.mk-mute-button
display block
user-select none
cursor pointer
padding 0 16px
margin 0
min-width 100px
line-height 36px
font-size 14px
font-weight bold
color var(--primary)
background transparent
outline none
border solid 1px var(--primary)
border-radius 36px
&:hover
background var(--primaryAlpha01)
&:active
background var(--primaryAlpha02)
&.active
color var(--primaryForeground)
background var(--primary)
&:hover
background var(--primaryLighten10)
border-color var(--primaryLighten10)
&:active
background var(--primaryDarken10)
border-color var(--primaryDarken10)
</style>

View File

@@ -26,6 +26,9 @@
<ui-switch v-model="iLikeSushi">%i18n:common.i-like-sushi%</ui-switch>
<ui-switch v-model="disableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
<ui-switch v-model="alwaysShowNsfw">%i18n:common.always-show-nsfw% (%i18n:common.this-setting-is-this-device-only%)</ui-switch>
</section>
<section>
<ui-switch v-model="games_reversi_showBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
<ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
</section>
@@ -82,6 +85,8 @@
<mk-drive-settings/>
<mk-mute-and-block/>
<ui-card>
<div slot="title">%fa:volume-up% %i18n:@sound%</div>
@@ -120,6 +125,8 @@
</section>
</ui-card>
<mk-api-settings />
<ui-card>
<div slot="title">%fa:sync-alt% %i18n:@update%</div>

View File

@@ -11,7 +11,7 @@
<a class="avatar">
<img :src="user.avatarUrl" alt="avatar"/>
</a>
<mk-mute-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
<button class="menu" ref="menu" @click="menu">%fa:ellipsis-h%</button>
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
</div>
<div class="title">
@@ -67,6 +67,7 @@ import Vue from 'vue';
import * as age from 's-age';
import parseAcct from '../../../../../misc/acct/parse';
import Progress from '../../../common/scripts/loading';
import Menu from '../../../common/views/components/menu.vue';
import XHome from './user/home.vue';
export default Vue.extend({
@@ -109,7 +110,61 @@ export default Vue.extend({
Progress.done();
document.title = `${Vue.filter('userName')(this.user)} | ${(this as any).os.instanceName}`;
});
}
},
menu() {
let menu = [{
icon: this.user.isMuted ? '%fa:eye%' : '%fa:eye-slash%',
text: this.user.isMuted ? '%i18n:@unmute%' : '%i18n:@mute%',
action: () => {
if (this.user.isMuted) {
(this as any).api('mute/delete', {
userId: this.user.id
}).then(() => {
this.user.isMuted = false;
}, () => {
alert('error');
});
} else {
(this as any).api('mute/create', {
userId: this.user.id
}).then(() => {
this.user.isMuted = true;
}, () => {
alert('error');
});
}
}
}, {
icon: this.user.isBlocking ? '%fa:user%' : '%fa:user-slash%',
text: this.user.isBlocking ? '%i18n:@unblock%' : '%i18n:@block%',
action: () => {
if (this.user.isBlocking) {
(this as any).api('blocking/delete', {
userId: this.user.id
}).then(() => {
this.user.isBlocking = false;
}, () => {
alert('error');
});
} else {
(this as any).api('blocking/create', {
userId: this.user.id
}).then(() => {
this.user.isBlocking = true;
}, () => {
alert('error');
});
}
}
}];
this.os.new(Menu, {
source: this.$refs.menu,
compact: true,
items: menu
});
},
}
});
</script>
@@ -156,14 +211,10 @@ main
max-width 600px
> .top
&:after
content ''
display block
clear both
display flex
> .avatar
display block
float left
width 25%
height 40px
@@ -183,11 +234,15 @@ main
border 4px solid $bg
border-radius 12px
> .mk-mute-button
float right
> .menu
margin 0 0 0 auto
padding 8px
margin-right 8px
font-size 18px
color var(--text)
> .mk-follow-button
float right
margin 0
> .title
margin 8px 0

View File

@@ -131,6 +131,11 @@
remoteInfoBg: '#42321c',
remoteInfoFg: '#ffbd3e',
infoBg: '#253142',
infoFg: '#fff',
infoWarnBg: '#42321c',
infoWarnFg: '#ffbd3e',
messagingRoomBg: '@bg',
messagingRoomInfo: '#fff',
messagingRoomDateDividerLine: 'rgba(255, 255, 255, 0.1)',

View File

@@ -131,6 +131,11 @@
remoteInfoBg: '#fff0db',
remoteInfoFg: '#573c08',
infoBg: '#e5f5ff',
infoFg: '#72818a',
infoWarnBg: '#fff0db',
infoWarnFg: '#573c08',
messagingRoomBg: '#fff',
messagingRoomInfo: '#000',
messagingRoomDateDividerLine: 'rgba(0, 0, 0, 0.1)',

View File

@@ -14,11 +14,13 @@ export type Source = {
* メンテナの連絡先(URLかmailto形式のURL)
*/
url: string;
email?: string;
repository_url?: string;
feedback_url?: string;
};
name?: string;
description?: string;
languages?: string[];
welcome_bg_url?: string;
url: string;
port: number;

View File

@@ -8,7 +8,7 @@
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
```
%URL%/streaming?i=xxxxxxxxxxxxxxx
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx
```
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
@@ -22,7 +22,7 @@
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
```
%URL%/streaming
%WS_URL%/streaming
```
---

View File

@@ -9,9 +9,9 @@ export type TextElementHashtag = {
};
export default function(text: string, i: number) {
if (!(/^\s#[^\s\.,!\?]+/.test(text) || (i == 0 && /^#[^\s\.,!\?]+/.test(text)))) return null;
if (!(/^\s#[^\s\.,!\?#]+/.test(text) || (i == 0 && /^#[^\s\.,!\?#]+/.test(text)))) return null;
const isHead = text.startsWith('#');
const hashtag = text.match(/^\s?#[^\s\.,!\?]+/)[0];
const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
const res: any[] = !isHead ? [{
type: 'text',
content: text[0]

View File

@@ -17,7 +17,8 @@ export default function(text: string, index: number) {
const quote = match[1]
.split('\n')
.map(line => line.replace(/^>+/g, '').trim())
.join('\n');
.join('\n')
.trim();
return {
type: 'quote',

View File

@@ -0,0 +1,20 @@
import { IDriveFile } from '../models/drive-file';
import config from '../config';
export default function(file: IDriveFile, thumbnail = false): string {
if (file == null) return null;
if (file.metadata.withoutChunks) {
if (thumbnail) {
return file.metadata.thumbnailUrl || file.metadata.url;
} else {
return file.metadata.url;
}
} else {
if (thumbnail) {
return `${config.drive_url}/${file._id}?thumbnail`;
} else {
return `${config.drive_url}/${file._id}`;
}
}
}

View File

@@ -1,6 +1,5 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const AccessToken = db.get<IAccessToken>('accessTokens');
AccessToken.createIndex('token');
@@ -15,30 +14,3 @@ export type IAccessToken = {
token: string;
hash: string;
};
/**
* AccessTokenを物理削除します
*/
export async function deleteAccessToken(accessToken: string | mongo.ObjectID | IAccessToken) {
let a: IAccessToken;
// Populate
if (isObjectId(accessToken)) {
a = await AccessToken.findOne({
_id: accessToken
});
} else if (typeof accessToken === 'string') {
a = await AccessToken.findOne({
_id: new mongo.ObjectID(accessToken)
});
} else {
a = accessToken as IAccessToken;
}
if (a == null) return;
// このAccessTokenを削除
await AccessToken.remove({
_id: a._id
});
}

View File

@@ -22,27 +22,28 @@ export type IApp = {
/**
* Pack an app for API response
*
* @param {any} app
* @param {any} me?
* @param {any} options?
* @return {Promise<any>}
*/
export const pack = (
app: any,
me?: any,
options?: {
detail?: boolean,
includeSecret?: boolean,
includeProfileImageIds?: boolean
}
) => new Promise<any>(async (resolve, reject) => {
const opts = options || {
const opts = Object.assign({
detail: false,
includeSecret: false,
includeProfileImageIds: false
};
}, options);
let _app: any;
const fields = opts.detail ? {} : {
name: true
};
// Populate the app if 'app' is ID
if (isObjectId(app)) {
_app = await App.findOne({
@@ -51,7 +52,7 @@ export const pack = (
} else if (typeof app === 'string') {
_app = await App.findOne({
_id: new mongo.ObjectID(app)
});
}, { fields });
} else {
_app = deepcopy(app);
}

56
src/models/blocking.ts Normal file
View File

@@ -0,0 +1,56 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const deepcopy = require('deepcopy');
import { pack as packUser, IUser } from './user';
const Blocking = db.get<IBlocking>('blocking');
Blocking.createIndex('blockerId');
Blocking.createIndex('blockeeId');
Blocking.createIndex(['blockerId', 'blockeeId'], { unique: true });
export default Blocking;
export type IBlocking = {
_id: mongo.ObjectID;
createdAt: Date;
blockeeId: mongo.ObjectID;
blockerId: mongo.ObjectID;
};
export const packMany = (
blockings: (string | mongo.ObjectID | IBlocking)[],
me?: string | mongo.ObjectID | IUser
) => {
return Promise.all(blockings.map(x => pack(x, me)));
};
export const pack = (
blocking: any,
me?: any
) => new Promise<any>(async (resolve, reject) => {
let _blocking: any;
// Populate the blocking if 'blocking' is ID
if (isObjectId(blocking)) {
_blocking = await Blocking.findOne({
_id: blocking
});
} else if (typeof blocking === 'string') {
_blocking = await Blocking.findOne({
_id: new mongo.ObjectID(blocking)
});
} else {
_blocking = deepcopy(blocking);
}
// Rename _id to id
_blocking.id = _blocking._id;
delete _blocking._id;
// Populate blockee
_blocking.blockee = await packUser(_blocking.blockeeId, me, {
detail: true
});
resolve(_blocking);
});

View File

@@ -1,6 +1,5 @@
import * as mongo from 'mongodb';
import monkDb, { nativeDbConn } from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const DriveFileThumbnail = monkDb.get<IDriveFileThumbnail>('driveFileThumbnails.files');
DriveFileThumbnail.createIndex('metadata.originalId', { sparse: true, unique: true });
@@ -28,35 +27,3 @@ export type IDriveFileThumbnail = {
contentType: string;
metadata: IMetadata;
};
/**
* DriveFileThumbnailを物理削除します
*/
export async function deleteDriveFileThumbnail(driveFile: string | mongo.ObjectID | IDriveFileThumbnail) {
let d: IDriveFileThumbnail;
// Populate
if (isObjectId(driveFile)) {
d = await DriveFileThumbnail.findOne({
_id: driveFile
});
} else if (typeof driveFile === 'string') {
d = await DriveFileThumbnail.findOne({
_id: new mongo.ObjectID(driveFile)
});
} else {
d = driveFile as IDriveFileThumbnail;
}
if (d == null) return;
// このDriveFileThumbnailのチャンクをすべて削除
await DriveFileThumbnailChunk.remove({
files_id: d._id
});
// このDriveFileThumbnailを削除
await DriveFileThumbnail.remove({
_id: d._id
});
}

View File

@@ -1,13 +1,9 @@
import * as mongo from 'mongodb';
const deepcopy = require('deepcopy');
import { pack as packFolder } from './drive-folder';
import config from '../config';
import monkDb, { nativeDbConn } from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
import Note, { deleteNote } from './note';
import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
import User from './user';
import DriveFileThumbnail, { deleteDriveFileThumbnail } from './drive-file-thumbnail';
import getDriveFileUrl from '../misc/get-drive-file-url';
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
DriveFile.createIndex('md5');
@@ -37,7 +33,14 @@ export type IMetadata = {
thumbnailUrl?: string;
src?: string;
deletedAt?: Date;
/**
* このファイルの中身データがMongoDB内に保存されているのか否か
* オブジェクトストレージを利用している or リモートサーバーへの直リンクである
* な場合は false になります
*/
withoutChunks?: boolean;
storage?: string;
storageProps?: any;
isSensitive?: boolean;
@@ -77,71 +80,13 @@ export function validateFileName(name: string): boolean {
);
}
/**
* DriveFileを物理削除します
*/
export async function deleteDriveFile(driveFile: string | mongo.ObjectID | IDriveFile) {
let d: IDriveFile;
// Populate
if (isObjectId(driveFile)) {
d = await DriveFile.findOne({
_id: driveFile
});
} else if (typeof driveFile === 'string') {
d = await DriveFile.findOne({
_id: new mongo.ObjectID(driveFile)
});
} else {
d = driveFile as IDriveFile;
}
if (d == null) return;
// このDriveFileを添付しているNoteをすべて削除
await Promise.all((
await Note.find({ fileIds: d._id })
).map(x => deleteNote(x)));
// このDriveFileを添付しているMessagingMessageをすべて削除
await Promise.all((
await MessagingMessage.find({ fileId: d._id })
).map(x => deleteMessagingMessage(x)));
// このDriveFileがアバターやバナーに使われていたらそれらのプロパティをnullにする
const u = await User.findOne({ _id: d.metadata.userId });
if (u) {
if (u.avatarId && u.avatarId.equals(d._id)) {
await User.update({ _id: u._id }, { $set: { avatarId: null } });
}
if (u.bannerId && u.bannerId.equals(d._id)) {
await User.update({ _id: u._id }, { $set: { bannerId: null } });
}
}
// このDriveFileのDriveFileThumbnailをすべて削除
await Promise.all((
await DriveFileThumbnail.find({ 'metadata.originalId': d._id })
).map(x => deleteDriveFileThumbnail(x)));
// このDriveFileのチャンクをすべて削除
await DriveFileChunk.remove({
files_id: d._id
});
// このDriveFileを削除
await DriveFile.remove({
_id: d._id
});
}
export const packMany = async (
export const packMany = (
files: any[],
options?: {
detail: boolean
}
) => {
return (await Promise.all(files.map(f => pack(f, options)))).filter(x => x != null);
return Promise.all(files.map(f => pack(f, options)));
};
/**
@@ -190,8 +135,8 @@ export const pack = (
_target = Object.assign(_target, _file.metadata);
_target.url = _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
_target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
_target.url = getDriveFileUrl(_file);
_target.thumbnailUrl = getDriveFileUrl(_file, true);
_target.isRemote = _file.metadata.isRemote;
if (_target.properties == null) _target.properties = {};
@@ -218,6 +163,7 @@ export const pack = (
delete _target.storage;
delete _target.storageProps;
delete _target.isRemote;
delete _target._user;
resolve(_target);
});

View File

@@ -23,51 +23,6 @@ export function isValidFolderName(name: string): boolean {
);
}
/**
* DriveFolderを物理削除します
*/
export async function deleteDriveFolder(driveFolder: string | mongo.ObjectID | IDriveFolder) {
let d: IDriveFolder;
// Populate
if (isObjectId(driveFolder)) {
d = await DriveFolder.findOne({
_id: driveFolder
});
} else if (typeof driveFolder === 'string') {
d = await DriveFolder.findOne({
_id: new mongo.ObjectID(driveFolder)
});
} else {
d = driveFolder as IDriveFolder;
}
if (d == null) return;
// このDriveFolderに格納されているDriveFileがあればすべてルートに移動
await DriveFile.update({
'metadata.folderId': d._id
}, {
$set: {
'metadata.folderId': null
}
});
// このDriveFolderに格納されているDriveFolderがあればすべてルートに移動
await DriveFolder.update({
parentId: d._id
}, {
$set: {
parentId: null
}
});
// このDriveFolderを削除
await DriveFolder.remove({
_id: d._id
});
}
/**
* Pack a drive folder for API response
*/

View File

@@ -5,6 +5,7 @@ import isObjectId from '../misc/is-objectid';
import { pack as packNote } from './note';
const Favorite = db.get<IFavorite>('favorites');
Favorite.createIndex('userId');
Favorite.createIndex(['userId', 'noteId'], { unique: true });
export default Favorite;
@@ -15,38 +16,11 @@ export type IFavorite = {
noteId: mongo.ObjectID;
};
/**
* Favoriteを物理削除します
*/
export async function deleteFavorite(favorite: string | mongo.ObjectID | IFavorite) {
let f: IFavorite;
// Populate
if (isObjectId(favorite)) {
f = await Favorite.findOne({
_id: favorite
});
} else if (typeof favorite === 'string') {
f = await Favorite.findOne({
_id: new mongo.ObjectID(favorite)
});
} else {
f = favorite as IFavorite;
}
if (f == null) return;
// このFavoriteを削除
await Favorite.remove({
_id: f._id
});
}
export const packMany = async (
export const packMany = (
favorites: any[],
me: any
) => {
return (await Promise.all(favorites.map(f => pack(f, me)))).filter(x => x != null);
return Promise.all(favorites.map(f => pack(f, me)));
};
/**

View File

@@ -5,6 +5,8 @@ import isObjectId from '../misc/is-objectid';
import { pack as packUser } from './user';
const FollowRequest = db.get<IFollowRequest>('followRequests');
FollowRequest.createIndex('followerId');
FollowRequest.createIndex('followeeId');
FollowRequest.createIndex(['followerId', 'followeeId'], { unique: true });
export default FollowRequest;
@@ -28,33 +30,6 @@ export type IFollowRequest = {
}
};
/**
* FollowRequestを物理削除します
*/
export async function deleteFollowRequest(followRequest: string | mongo.ObjectID | IFollowRequest) {
let f: IFollowRequest;
// Populate
if (isObjectId(followRequest)) {
f = await FollowRequest.findOne({
_id: followRequest
});
} else if (typeof followRequest === 'string') {
f = await FollowRequest.findOne({
_id: new mongo.ObjectID(followRequest)
});
} else {
f = followRequest as IFollowRequest;
}
if (f == null) return;
// このFollowingを削除
await FollowRequest.remove({
_id: f._id
});
}
/**
* Pack a request for API response
*/

View File

@@ -1,8 +1,9 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const Following = db.get<IFollowing>('following');
Following.createIndex('followerId');
Following.createIndex('followeeId');
Following.createIndex(['followerId', 'followeeId'], { unique: true });
export default Following;
@@ -25,30 +26,3 @@ export type IFollowing = {
sharedInbox?: string;
}
};
/**
* Followingを物理削除します
*/
export async function deleteFollowing(following: string | mongo.ObjectID | IFollowing) {
let f: IFollowing;
// Populate
if (isObjectId(following)) {
f = await Following.findOne({
_id: following
});
} else if (typeof following === 'string') {
f = await Following.findOne({
_id: new mongo.ObjectID(following)
});
} else {
f = following as IFollowing;
}
if (f == null) return;
// このFollowingを削除
await Following.remove({
_id: f._id
});
}

View File

@@ -1,6 +1,5 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const MessagingHistory = db.get<IMessagingHistory>('messagingHistories');
export default MessagingHistory;
@@ -12,30 +11,3 @@ export type IMessagingHistory = {
partnerId: mongo.ObjectID;
messageId: mongo.ObjectID;
};
/**
* MessagingHistoryを物理削除します
*/
export async function deleteMessagingHistory(messagingHistory: string | mongo.ObjectID | IMessagingHistory) {
let m: IMessagingHistory;
// Populate
if (isObjectId(messagingHistory)) {
m = await MessagingHistory.findOne({
_id: messagingHistory
});
} else if (typeof messagingHistory === 'string') {
m = await MessagingHistory.findOne({
_id: new mongo.ObjectID(messagingHistory)
});
} else {
m = messagingHistory as IMessagingHistory;
}
if (m == null) return;
// このMessagingHistoryを削除
await MessagingHistory.remove({
_id: m._id
});
}

View File

@@ -4,7 +4,6 @@ import { pack as packUser } from './user';
import { pack as packFile } from './drive-file';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
import MessagingHistory, { deleteMessagingHistory } from './messaging-history';
import { length } from 'stringz';
const MessagingMessage = db.get<IMessagingMessage>('messagingMessages');
@@ -24,38 +23,6 @@ export function isValidText(text: string): boolean {
return length(text.trim()) <= 1000 && text.trim() != '';
}
/**
* MessagingMessageを物理削除します
*/
export async function deleteMessagingMessage(messagingMessage: string | mongo.ObjectID | IMessagingMessage) {
let m: IMessagingMessage;
// Populate
if (isObjectId(messagingMessage)) {
m = await MessagingMessage.findOne({
_id: messagingMessage
});
} else if (typeof messagingMessage === 'string') {
m = await MessagingMessage.findOne({
_id: new mongo.ObjectID(messagingMessage)
});
} else {
m = messagingMessage as IMessagingMessage;
}
if (m == null) return;
// このMessagingMessageを指すMessagingHistoryをすべて削除
await Promise.all((
await MessagingHistory.find({ messageId: m._id })
).map(x => deleteMessagingHistory(x)));
// このMessagingMessageを削除
await MessagingMessage.remove({
_id: m._id
});
}
/**
* Pack a messaging message for API response
*/

View File

@@ -15,4 +15,24 @@ export type IMeta = {
disableLocalTimeline?: boolean;
hidedTags?: string[];
bannerUrl?: string;
/**
* カスタム絵文字定義
*/
emojis?: {
/**
* 絵文字名 (例: thinking_ai)
*/
name: string;
/**
* エイリアス
*/
aliases?: string[];
/**
* 絵文字画像のURL
*/
url: string;
}[];
};

View File

@@ -1,8 +1,12 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const deepcopy = require('deepcopy');
import { pack as packUser, IUser } from './user';
const Mute = db.get<IMute>('mute');
Mute.createIndex('muterId');
Mute.createIndex('muteeId');
Mute.createIndex(['muterId', 'muteeId'], { unique: true });
export default Mute;
@@ -13,29 +17,40 @@ export interface IMute {
muteeId: mongo.ObjectID;
}
/**
* Muteを物理削除します
*/
export async function deleteMute(mute: string | mongo.ObjectID | IMute) {
let m: IMute;
export const packMany = (
mutes: (string | mongo.ObjectID | IMute)[],
me?: string | mongo.ObjectID | IUser
) => {
return Promise.all(mutes.map(x => pack(x, me)));
};
// Populate
export const pack = (
mute: any,
me?: any
) => new Promise<any>(async (resolve, reject) => {
let _mute: any;
// Populate the mute if 'mute' is ID
if (isObjectId(mute)) {
m = await Mute.findOne({
_mute = await Mute.findOne({
_id: mute
});
} else if (typeof mute === 'string') {
m = await Mute.findOne({
_mute = await Mute.findOne({
_id: new mongo.ObjectID(mute)
});
} else {
m = mute as IMute;
_mute = deepcopy(mute);
}
if (m == null) return;
// Rename _id to id
_mute.id = _mute._id;
delete _mute._id;
// このMuteを削除
await Mute.remove({
_id: m._id
// Populate mutee
_mute.mutee = await packUser(_mute.muteeId, me, {
detail: true
});
}
resolve(_mute);
});

View File

@@ -7,6 +7,8 @@ import Reaction from './note-reaction';
import { pack as packUser } from './user';
const NoteReaction = db.get<INoteReaction>('noteReactions');
NoteReaction.createIndex('noteId');
NoteReaction.createIndex('userId');
NoteReaction.createIndex(['userId', 'noteId'], { unique: true });
export default NoteReaction;
@@ -31,33 +33,6 @@ export const validateReaction = $.str.or([
'pudding'
]);
/**
* NoteReactionを物理削除します
*/
export async function deleteNoteReaction(noteReaction: string | mongo.ObjectID | INoteReaction) {
let n: INoteReaction;
// Populate
if (isObjectId(noteReaction)) {
n = await NoteReaction.findOne({
_id: noteReaction
});
} else if (typeof noteReaction === 'string') {
n = await NoteReaction.findOne({
_id: new mongo.ObjectID(noteReaction)
});
} else {
n = noteReaction as INoteReaction;
}
if (n == null) return;
// このNoteReactionを削除
await NoteReaction.remove({
_id: n._id
});
}
/**
* Pack a reaction for API response
*/

View File

@@ -2,6 +2,8 @@ import * as mongo from 'mongodb';
import db from '../db/mongodb';
const NoteUnread = db.get<INoteUnread>('noteUnreads');
NoteUnread.createIndex('userId');
NoteUnread.createIndex('noteId');
NoteUnread.createIndex(['userId', 'noteId'], { unique: true });
export default NoteUnread;

View File

@@ -1,8 +1,9 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const NoteWatching = db.get<INoteWatching>('noteWatching');
NoteWatching.createIndex('userId');
NoteWatching.createIndex('noteId');
NoteWatching.createIndex(['userId', 'noteId'], { unique: true });
export default NoteWatching;
@@ -12,30 +13,3 @@ export interface INoteWatching {
userId: mongo.ObjectID;
noteId: mongo.ObjectID;
}
/**
* NoteWatchingを物理削除します
*/
export async function deleteNoteWatching(noteWatching: string | mongo.ObjectID | INoteWatching) {
let n: INoteWatching;
// Populate
if (isObjectId(noteWatching)) {
n = await NoteWatching.findOne({
_id: noteWatching
});
} else if (typeof noteWatching === 'string') {
n = await NoteWatching.findOne({
_id: new mongo.ObjectID(noteWatching)
});
} else {
n = noteWatching as INoteWatching;
}
if (n == null) return;
// このNoteWatchingを削除
await NoteWatching.remove({
_id: n._id
});
}

View File

@@ -6,13 +6,10 @@ import isObjectId from '../misc/is-objectid';
import { length } from 'stringz';
import { IUser, pack as packUser } from './user';
import { pack as packApp } from './app';
import PollVote, { deletePollVote } from './poll-vote';
import Reaction, { deleteNoteReaction } from './note-reaction';
import PollVote from './poll-vote';
import Reaction from './note-reaction';
import { packMany as packFileMany, IDriveFile } from './drive-file';
import NoteWatching, { deleteNoteWatching } from './note-watching';
import NoteReaction from './note-reaction';
import Favorite, { deleteFavorite } from './favorite';
import Notification, { deleteNotification } from './notification';
import Favorite from './favorite';
import Following from './following';
import config from '../config';
@@ -108,72 +105,6 @@ export type INote = {
_files?: IDriveFile[];
};
/**
* Noteを物理削除します
*/
export async function deleteNote(note: string | mongo.ObjectID | INote) {
let n: INote;
// Populate
if (isObjectId(note)) {
n = await Note.findOne({
_id: note
});
} else if (typeof note === 'string') {
n = await Note.findOne({
_id: new mongo.ObjectID(note)
});
} else {
n = note as INote;
}
console.log(n == null ? `Note: delete skipped ${note}` : `Note: deleting ${n._id}`);
if (n == null) return;
// このNoteへの返信をすべて削除
await Promise.all((
await Note.find({ replyId: n._id })
).map(x => deleteNote(x)));
// このNoteのRenoteをすべて削除
await Promise.all((
await Note.find({ renoteId: n._id })
).map(x => deleteNote(x)));
// この投稿に対するNoteWatchingをすべて削除
await Promise.all((
await NoteWatching.find({ noteId: n._id })
).map(x => deleteNoteWatching(x)));
// この投稿に対するNoteReactionをすべて削除
await Promise.all((
await NoteReaction.find({ noteId: n._id })
).map(x => deleteNoteReaction(x)));
// この投稿に対するPollVoteをすべて削除
await Promise.all((
await PollVote.find({ noteId: n._id })
).map(x => deletePollVote(x)));
// この投稿に対するFavoriteをすべて削除
await Promise.all((
await Favorite.find({ noteId: n._id })
).map(x => deleteFavorite(x)));
// この投稿に対するNotificationをすべて削除
await Promise.all((
await Notification.find({ noteId: n._id })
).map(x => deleteNotification(x)));
// このNoteを削除
await Note.remove({
_id: n._id
});
console.log(`Note: deleted ${n._id}`);
}
export const hideNote = async (packedNote: any, meId: mongo.ObjectID) => {
let hide = false;
@@ -233,7 +164,7 @@ export const hideNote = async (packedNote: any, meId: mongo.ObjectID) => {
}
};
export const packMany = async (
export const packMany = (
notes: (string | mongo.ObjectID | INote)[],
me?: string | mongo.ObjectID | IUser,
options?: {
@@ -241,7 +172,7 @@ export const packMany = async (
skipHide?: boolean;
}
) => {
return (await Promise.all(notes.map(n => pack(n, me, options)))).filter(x => x != null);
return Promise.all(notes.map(n => pack(n, me, options)));
};
/**
@@ -328,11 +259,6 @@ export const pack = async (
// When requested a detailed note data
if (opts.detail) {
//#region 重いので廃止
_note.prev = null;
_note.next = null;
//#endregion
if (_note.replyId) {
// Populate reply to note
_note.reply = pack(_note.replyId, meId, {

View File

@@ -51,37 +51,10 @@ export interface INotification {
isRead: Boolean;
}
/**
* Notificationを物理削除します
*/
export async function deleteNotification(notification: string | mongo.ObjectID | INotification) {
let n: INotification;
// Populate
if (isObjectId(notification)) {
n = await Notification.findOne({
_id: notification
});
} else if (typeof notification === 'string') {
n = await Notification.findOne({
_id: new mongo.ObjectID(notification)
});
} else {
n = notification as INotification;
}
if (n == null) return;
// このNotificationを削除
await Notification.remove({
_id: n._id
});
}
export const packMany = async (
export const packMany = (
notifications: any[]
) => {
return (await Promise.all(notifications.map(n => pack(n)))).filter(x => x != null);
return Promise.all(notifications.map(n => pack(n)));
};
/**

View File

@@ -1,6 +1,5 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const PollVote = db.get<IPollVote>('pollVotes');
export default PollVote;
@@ -12,30 +11,3 @@ export interface IPollVote {
noteId: mongo.ObjectID;
choice: number;
}
/**
* PollVoteを物理削除します
*/
export async function deletePollVote(pollVote: string | mongo.ObjectID | IPollVote) {
let p: IPollVote;
// Populate
if (isObjectId(pollVote)) {
p = await PollVote.findOne({
_id: pollVote
});
} else if (typeof pollVote === 'string') {
p = await PollVote.findOne({
_id: new mongo.ObjectID(pollVote)
});
} else {
p = pollVote as IPollVote;
}
if (p == null) return;
// このPollVoteを削除
await PollVote.remove({
_id: p._id
});
}

View File

@@ -1,6 +1,5 @@
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
const SwSubscription = db.get<ISwSubscription>('swSubscriptions');
export default SwSubscription;
@@ -12,30 +11,3 @@ export interface ISwSubscription {
auth: string;
publickey: string;
}
/**
* SwSubscriptionを物理削除します
*/
export async function deleteSwSubscription(swSubscription: string | mongo.ObjectID | ISwSubscription) {
let s: ISwSubscription;
// Populate
if (isObjectId(swSubscription)) {
s = await SwSubscription.findOne({
_id: swSubscription
});
} else if (typeof swSubscription === 'string') {
s = await SwSubscription.findOne({
_id: new mongo.ObjectID(swSubscription)
});
} else {
s = swSubscription as ISwSubscription;
}
if (s == null) return;
// このSwSubscriptionを削除
await SwSubscription.remove({
_id: s._id
});
}

View File

@@ -14,33 +14,6 @@ export interface IUserList {
userIds: mongo.ObjectID[];
}
/**
* UserListを物理削除します
*/
export async function deleteUserList(userList: string | mongo.ObjectID | IUserList) {
let u: IUserList;
// Populate
if (isObjectId(userList)) {
u = await UserList.findOne({
_id: userList
});
} else if (typeof userList === 'string') {
u = await UserList.findOne({
_id: new mongo.ObjectID(userList)
});
} else {
u = userList as IUserList;
}
if (u == null) return;
// このUserListを削除
await UserList.remove({
_id: u._id
});
}
export const pack = (
userList: string | mongo.ObjectID | IUserList
) => new Promise<any>(async (resolve, reject) => {

View File

@@ -1,27 +1,15 @@
import * as mongo from 'mongodb';
const deepcopy = require('deepcopy');
const sequential = require('promise-sequential');
import rap from '@prezzemolo/rap';
import db from '../db/mongodb';
import isObjectId from '../misc/is-objectid';
import Note, { packMany as packNoteMany, deleteNote } from './note';
import Following, { deleteFollowing } from './following';
import Mute, { deleteMute } from './mute';
import { packMany as packNoteMany } from './note';
import Following from './following';
import Blocking from './blocking';
import Mute from './mute';
import { getFriendIds } from '../server/api/common/get-friends';
import config from '../config';
import AccessToken, { deleteAccessToken } from './access-token';
import NoteWatching, { deleteNoteWatching } from './note-watching';
import Favorite, { deleteFavorite } from './favorite';
import NoteReaction, { deleteNoteReaction } from './note-reaction';
import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
import MessagingHistory, { deleteMessagingHistory } from './messaging-history';
import DriveFile, { deleteDriveFile } from './drive-file';
import DriveFolder, { deleteDriveFolder } from './drive-folder';
import PollVote, { deletePollVote } from './poll-vote';
import SwSubscription, { deleteSwSubscription } from './sw-subscription';
import Notification, { deleteNotification } from './notification';
import UserList, { deleteUserList } from './user-list';
import FollowRequest, { deleteFollowRequest } from './follow-request';
import FollowRequest from './follow-request';
const User = db.get<IUser>('users');
@@ -167,149 +155,48 @@ export function isValidBirthday(birthday: string): boolean {
}
//#endregion
/**
* Userを物理削除します
*/
export async function deleteUser(user: string | mongo.ObjectID | IUser) {
let u: IUser;
// Populate
if (isObjectId(user)) {
u = await User.findOne({
_id: user
});
} else if (typeof user === 'string') {
u = await User.findOne({
_id: new mongo.ObjectID(user)
});
} else {
u = user as IUser;
}
console.log(u == null ? `User: delete skipped ${user}` : `User: deleting ${u._id}`);
if (u == null) return;
// このユーザーのAccessTokenをすべて削除
await Promise.all((
await AccessToken.find({ userId: u._id })
).map(x => deleteAccessToken(x)));
// このユーザーのNoteをすべて削除
await sequential((
await Note.find({ userId: u._id })
).map(x => () => deleteNote(x)));
// このユーザーのNoteReactionをすべて削除
await Promise.all((
await NoteReaction.find({ userId: u._id })
).map(x => deleteNoteReaction(x)));
// このユーザーのNoteWatchingをすべて削除
await Promise.all((
await NoteWatching.find({ userId: u._id })
).map(x => deleteNoteWatching(x)));
// このユーザーのPollVoteをすべて削除
await Promise.all((
await PollVote.find({ userId: u._id })
).map(x => deletePollVote(x)));
// このユーザーのFavoriteをすべて削除
await Promise.all((
await Favorite.find({ userId: u._id })
).map(x => deleteFavorite(x)));
// このユーザーのMessageをすべて削除
await Promise.all((
await MessagingMessage.find({ userId: u._id })
).map(x => deleteMessagingMessage(x)));
// このユーザーへのMessageをすべて削除
await Promise.all((
await MessagingMessage.find({ recipientId: u._id })
).map(x => deleteMessagingMessage(x)));
// このユーザーの関わるMessagingHistoryをすべて削除
await Promise.all((
await MessagingHistory.find({ $or: [{ partnerId: u._id }, { userId: u._id }] })
).map(x => deleteMessagingHistory(x)));
// このユーザーのDriveFileをすべて削除
await Promise.all((
await DriveFile.find({ 'metadata.userId': u._id })
).map(x => deleteDriveFile(x)));
// このユーザーのDriveFolderをすべて削除
await Promise.all((
await DriveFolder.find({ userId: u._id })
).map(x => deleteDriveFolder(x)));
// このユーザーのMuteをすべて削除
await Promise.all((
await Mute.find({ muterId: u._id })
).map(x => deleteMute(x)));
// このユーザーへのMuteをすべて削除
await Promise.all((
await Mute.find({ muteeId: u._id })
).map(x => deleteMute(x)));
// このユーザーのFollowingをすべて削除
await Promise.all((
await Following.find({ followerId: u._id })
).map(x => deleteFollowing(x)));
// このユーザーへのFollowingをすべて削除
await Promise.all((
await Following.find({ followeeId: u._id })
).map(x => deleteFollowing(x)));
// このユーザーのFollowRequestをすべて削除
await Promise.all((
await FollowRequest.find({ followerId: u._id })
).map(x => deleteFollowRequest(x)));
// このユーザーへのFollowRequestをすべて削除
await Promise.all((
await FollowRequest.find({ followeeId: u._id })
).map(x => deleteFollowRequest(x)));
// このユーザーのSwSubscriptionをすべて削除
await Promise.all((
await SwSubscription.find({ userId: u._id })
).map(x => deleteSwSubscription(x)));
// このユーザーのNotificationをすべて削除
await Promise.all((
await Notification.find({ notifieeId: u._id })
).map(x => deleteNotification(x)));
// このユーザーが原因となったNotificationをすべて削除
await Promise.all((
await Notification.find({ notifierId: u._id })
).map(x => deleteNotification(x)));
// このユーザーのUserListをすべて削除
await Promise.all((
await UserList.find({ userId: u._id })
).map(x => deleteUserList(x)));
// このユーザーが入っているすべてのUserListからこのユーザーを削除
await Promise.all((
await UserList.find({ userIds: u._id })
).map(x =>
UserList.update({ _id: x._id }, {
$pull: { userIds: u._id }
export async function getRelation(me: mongo.ObjectId, target: mongo.ObjectId) {
const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
Following.findOne({
followerId: me,
followeeId: target
}),
Following.findOne({
followerId: target,
followeeId: me
}),
FollowRequest.findOne({
followerId: me,
followeeId: target
}),
FollowRequest.findOne({
followerId: target,
followeeId: me
}),
Blocking.findOne({
blockerId: me,
blockeeId: target
}),
Blocking.findOne({
blockerId: target,
blockeeId: me
}),
Mute.findOne({
muterId: me,
muteeId: target
})
));
]);
// このユーザーを削除
await User.remove({
_id: u._id
});
console.log(`User: deleted ${u._id}`);
return {
isFollowing: following1 !== null,
isStalking: following1 && following1.stalk,
hasPendingFollowRequestFromYou: followReq1 !== null,
hasPendingFollowRequestToYou: followReq2 !== null,
isFollowed: following2 !== null,
isBlocking: toBlocking !== null,
isBlocked: fromBlocked !== null,
isMuted: mute !== null
};
}
/**
@@ -336,13 +223,16 @@ export const pack = (
let _user: any;
const fields = opts.detail ? {
} : {
settings: false,
clientSettings: false,
profile: false,
keywords: false,
domains: false
const fields = opts.detail ? {} : {
name: true,
username: true,
host: true,
avatarColor: true,
avatarUrl: true,
isCat: true,
isBot: true,
isAdmin: true,
isVerified: true
};
// Populate the user if 'user' is ID
@@ -377,6 +267,8 @@ export const pack = (
_user.id = _user._id;
delete _user._id;
delete _user.usernameLower;
if (_user.host == null) {
// Remove private properties
delete _user.keypair;
@@ -384,7 +276,6 @@ export const pack = (
delete _user.token;
delete _user.twoFactorTempSecret;
delete _user.twoFactorSecret;
delete _user.usernameLower;
if (_user.twitter) {
delete _user.twitter.accessToken;
delete _user.twitter.accessTokenSecret;
@@ -407,16 +298,6 @@ export const pack = (
if (_user.avatarUrl == null) {
_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
// 互換性のため
if (_user.avatarId) {
_user.avatarUrl = `${config.drive_url}/${_user.avatarId}`;
}
}
// 互換性のため
if (_user.bannerId && _user.bannerUrl == null) {
_user.bannerUrl = `${config.drive_url}/${_user.bannerId}`;
}
if (!meId || !meId.equals(_user.id) || !opts.detail) {
@@ -426,42 +307,17 @@ export const pack = (
delete _user.hasUnreadNotification;
}
if (meId && !meId.equals(_user.id)) {
const [following1, following2, followReq1, followReq2, mute] = await Promise.all([
Following.findOne({
followerId: meId,
followeeId: _user.id
}),
Following.findOne({
followerId: _user.id,
followeeId: meId
}),
FollowRequest.findOne({
followerId: meId,
followeeId: _user.id
}),
FollowRequest.findOne({
followerId: _user.id,
followeeId: meId
}),
Mute.findOne({
muterId: meId,
muteeId: _user.id
})
]);
if (meId && !meId.equals(_user.id) && opts.detail) {
const relation = await getRelation(meId, _user.id);
// Whether the user is following
_user.isFollowing = following1 !== null;
_user.isStalking = following1 && following1.stalk;
_user.hasPendingFollowRequestFromYou = followReq1 !== null;
_user.hasPendingFollowRequestToYou = followReq2 !== null;
// Whether the user is followed
_user.isFollowed = following2 !== null;
// Whether the user is muted
_user.isMuted = mute !== null;
_user.isFollowing = relation.isFollowing;
_user.isFollowed = relation.isFollowed;
_user.isStalking = relation.isStalking;
_user.hasPendingFollowRequestFromYou = relation.hasPendingFollowRequestFromYou;
_user.hasPendingFollowRequestToYou = relation.hasPendingFollowRequestToYou;
_user.isBlocking = relation.isBlocking;
_user.isBlocked = relation.isBlocked;
_user.isMuted = relation.isMuted;
}
if (opts.detail) {

View File

@@ -0,0 +1,34 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../../models/user';
import config from '../../../../config';
import * as debug from 'debug';
import { IBlock } from '../../type';
import block from '../../../../services/blocking/create';
const log = debug('misskey:activitypub');
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
const uri = activity.id || activity;
log(`Block: ${uri}`);
if (!id.startsWith(config.url + '/')) {
return null;
}
const blockee = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});
if (blockee === null) {
throw new Error('blockee not found');
}
if (blockee.host != null) {
throw new Error('ブロックしようとしているユーザーはローカルユーザーではありません');
}
block(actor, blockee);
};

View File

@@ -10,6 +10,7 @@ import accept from './accept';
import reject from './reject';
import add from './add';
import remove from './remove';
import block from './block';
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
switch (activity.type) {
@@ -53,6 +54,10 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
await undo(actor, activity);
break;
case 'Block':
await block(actor, activity);
break;
case 'Collection':
case 'OrderedCollection':
// TODO

View File

@@ -0,0 +1,34 @@
import * as mongo from 'mongodb';
import User, { IRemoteUser } from '../../../../models/user';
import config from '../../../../config';
import * as debug from 'debug';
import { IBlock } from '../../type';
import unblock from '../../../../services/blocking/delete';
const log = debug('misskey:activitypub');
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
const uri = activity.id || activity;
log(`UnBlock: ${uri}`);
if (!id.startsWith(config.url + '/')) {
return null;
}
const blockee = await User.findOne({
_id: new mongo.ObjectID(id.split('/').pop())
});
if (blockee === null) {
throw new Error('blockee not found');
}
if (blockee.host != null) {
throw new Error('ブロック解除しようとしているユーザーはローカルユーザーではありません');
}
unblock(actor, blockee);
};

View File

@@ -1,8 +1,9 @@
import * as debug from 'debug';
import { IRemoteUser } from '../../../../models/user';
import { IUndo, IFollow } from '../../type';
import { IUndo, IFollow, IBlock } from '../../type';
import unfollow from './follow';
import unblock from './block';
import Resolver from '../../resolver';
const log = debug('misskey:activitypub');
@@ -31,6 +32,9 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
case 'Follow':
unfollow(actor, object as IFollow);
break;
case 'Block':
unblock(actor, object as IBlock);
break;
}
return null;

View File

@@ -15,6 +15,7 @@ import { URL } from 'url';
import { resolveNote } from './note';
import registerInstance from '../../../services/register-instance';
import Instance from '../../../models/instance';
import getDriveFileUrl from '../../../misc/get-drive-file-url';
const log = debug('misskey:activitypub');
@@ -209,8 +210,8 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
const avatarId = avatar ? avatar._id : null;
const bannerId = banner ? banner._id : null;
const avatarUrl = (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null;
const bannerUrl = (banner && banner.metadata.url) ? banner.metadata.url : null;
const avatarUrl = getDriveFileUrl(avatar, true);
const bannerUrl = getDriveFileUrl(banner, false);
await User.update({ _id: user._id }, {
$set: {
@@ -303,8 +304,8 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
featured: person.featured,
avatarId: avatar ? avatar._id : null,
bannerId: banner ? banner._id : null,
avatarUrl: (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null,
bannerUrl: banner && banner.metadata.url ? banner.metadata.url : null,
avatarUrl: getDriveFileUrl(avatar, true),
bannerUrl: getDriveFileUrl(banner, false),
description: htmlToMFM(person.summary),
followersCount,
followingCount,

View File

@@ -0,0 +1,8 @@
import config from '../../../config';
import { ILocalUser, IRemoteUser } from "../../../models/user";
export default (blocker?: ILocalUser, blockee?: IRemoteUser) => ({
type: 'Block',
actor: `${config.url}/users/${blocker._id}`,
object: blockee.uri
});

View File

@@ -1,8 +1,8 @@
import config from '../../../config';
import { IDriveFile } from '../../../models/drive-file';
import getDriveFileUrl from '../../../misc/get-drive-file-url';
export default (file: IDriveFile) => ({
type: 'Document',
mediaType: file.contentType,
url: file.metadata.url || `${config.drive_url}/${file._id}`
url: getDriveFileUrl(file)
});

View File

@@ -1,8 +1,8 @@
import config from '../../../config';
import { IDriveFile } from '../../../models/drive-file';
import getDriveFileUrl from '../../../misc/get-drive-file-url';
export default (file: IDriveFile) => ({
type: 'Image',
url: file.metadata.url || `${config.drive_url}/${file._id}`,
url: getDriveFileUrl(file),
sensitive: file.metadata.isSensitive
});

View File

@@ -108,6 +108,10 @@ export interface IAnnounce extends IActivity {
type: 'Announce';
}
export interface IBlock extends IActivity {
type: 'Block';
}
export type Object =
ICollection |
IOrderedCollection |
@@ -120,4 +124,5 @@ export type Object =
IAdd |
IRemove |
ILike |
IAnnounce;
IAnnounce |
IBlock;

View File

@@ -1,36 +1,83 @@
import { toUnicode, toASCII } from 'punycode';
import User, { IUser } from '../models/user';
import User, { IUser, IRemoteUser } from '../models/user';
import webFinger from './webfinger';
import config from '../config';
import { createPerson } from './activitypub/models/person';
import { createPerson, updatePerson } from './activitypub/models/person';
import { URL } from 'url';
import * as debug from 'debug';
export default async (username: string, _host: string, option?: any): Promise<IUser> => {
const log = debug('misskey:remote:resolve-user');
export default async (username: string, _host: string, option?: any, resync?: boolean): Promise<IUser> => {
const usernameLower = username.toLowerCase();
if (_host == null) {
return await User.findOne({ usernameLower });
log(`return local user: ${usernameLower}`);
return await User.findOne({ usernameLower, host: null });
}
const hostAscii = toASCII(_host).toLowerCase();
const host = toUnicode(hostAscii);
if (config.host == host) {
log(`return local user: ${usernameLower}`);
return await User.findOne({ usernameLower, host: null });
}
let user = await User.findOne({ usernameLower, host }, option);
const user = await User.findOne({ usernameLower, host }, option);
const acctLower = `${usernameLower}@${hostAscii}`;
if (user === null) {
const acctLower = `${usernameLower}@${hostAscii}`;
const self = await resolveSelf(acctLower);
const finger = await webFinger(acctLower);
const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
if (!self) {
throw new Error('self link not found');
}
user = await createPerson(self.href);
log(`return new remote user: ${acctLower}`);
return await createPerson(self.href);
}
if (resync) {
log(`try resync: ${acctLower}`);
const self = await resolveSelf(acctLower);
if ((user as IRemoteUser).uri !== self.href) {
// if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping.
log(`uri missmatch: ${acctLower}`);
console.log(`recovery missmatch uri for (username=${username}, host=${host}) from ${(user as IRemoteUser).uri} to ${self.href}`);
// validate uri
const uri = new URL(self.href);
if (uri.hostname !== hostAscii) {
throw new Error(`Invalied uri`);
}
await User.update({
usernameLower,
host: host
}, {
$set: {
uri: self.href
}
});
} else {
log(`uri is fine: ${acctLower}`);
}
await updatePerson(self.href);
log(`return resynced remote user: ${acctLower}`);
return await User.findOne({ uri: self.href });
}
log(`return existing remote user: ${acctLower}`);
return user;
};
async function resolveSelf(acctLower: string) {
log(`WebFinger for ${acctLower}`);
const finger = await webFinger(acctLower);
const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
if (!self) {
throw new Error('self link not found');
}
return self;
}

View File

@@ -62,7 +62,15 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
console.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`);
}
} catch (e) {
rej(e);
if (e.name == 'INVALID_PARAM') {
rej({
code: e.name,
param: e.param,
reason: e.message
});
} else {
rej(e);
}
return;
}

View File

@@ -17,6 +17,12 @@ export const meta = {
}
}),
emojis: $.arr($.obj()).optional.note({
desc: {
'ja-JP': 'カスタム絵文字定義'
}
}),
disableRegistration: $.bool.optional.nullable.note({
desc: {
'ja-JP': '招待制か否か'
@@ -53,6 +59,10 @@ export default (params: any) => new Promise(async (res, rej) => {
set.broadcasts = ps.broadcasts;
}
if (ps.emojis) {
set.emojis = ps.emojis;
}
if (typeof ps.disableRegistration === 'boolean') {
set.disableRegistration = ps.disableRegistration;
}

View File

@@ -41,7 +41,7 @@ async function fetchAny(uri: string) {
// URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ
if (uri.startsWith(config.url + '/')) {
const id = new mongo.ObjectID(uri.split('/').pop());
const [ user, note ] = await Promise.all([
const [user, note] = await Promise.all([
User.findOne({ _id: id }),
Note.findOne({ _id: id })
]);
@@ -52,7 +52,7 @@ async function fetchAny(uri: string) {
// URI(AP Object id)としてDB検索
{
const [ user, note ] = await Promise.all([
const [user, note] = await Promise.all([
User.findOne({ uri: uri }),
Note.findOne({ uri: uri })
]);
@@ -68,7 +68,7 @@ async function fetchAny(uri: string) {
// /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する
// これはDBに存在する可能性があるため再度DB検索
if (uri !== object.id) {
const [ user, note ] = await Promise.all([
const [user, note] = await Promise.all([
User.findOne({ uri: object.id }),
Note.findOne({ uri: object.id })
]);

View File

@@ -44,6 +44,7 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
// Response
res(await pack(app, null, {
detail: true,
includeSecret: true
}));
});

View File

@@ -21,6 +21,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
// Send response
res(await pack(ap, user, {
detail: true,
includeSecret: isSecure && ap.userId.equals(user._id)
}));
});

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