Compare commits

...

83 Commits
7.0.2 ... 7.3.0

Author SHA1 Message Date
a5023271ef 7.3.0 2018-08-22 09:40:54 +09:00
c3747db670 Fix doc 2018-08-22 09:36:42 +09:00
fe1e60a28c Fix #2334 2018-08-22 09:33:59 +09:00
f91d2e8c8d Merge branch 'master' of https://github.com/syuilo/misskey 2018-08-22 09:31:51 +09:00
dccc2c60e3 Fix bugs
Closes #2367
2018-08-22 09:31:35 +09:00
933e25804c Merge pull request #2389 from Tosuke/fix-2384
Fix login bug(#2384)
2018-08-22 09:27:09 +09:00
0b503661af fix login bug(#2384) 2018-08-22 09:26:06 +09:00
58082431ff Merge branch 'master' of https://github.com/syuilo/misskey 2018-08-22 09:16:55 +09:00
2536bfb5f5 #2378 2018-08-22 09:16:52 +09:00
6428066552 Merge pull request #2388 from mei23/mei-0822-dbcheck
起動時のDB接続チェックでエラーを検出できないのを修正
2018-08-22 09:12:59 +09:00
4bf3827b73 Revert "#2387"
This reverts commit 3cad494404.
2018-08-22 09:12:37 +09:00
3cad494404 #2387 2018-08-22 09:11:59 +09:00
ef0793311f リバーシのアイコンのコントラストのオプションを追加するなど 2018-08-22 09:10:39 +09:00
6f3e341e89 Fix DB connectivity check 2018-08-22 08:46:31 +09:00
2fea3be7c0 Merge pull request #2364 from acid-chicken/patch-2
Resolve #2363
2018-08-22 08:41:47 +09:00
82059d4fd9 7.2.0 2018-08-22 05:24:16 +09:00
07ddeae2f1 Clean up 2018-08-22 04:53:02 +09:00
f2279758b2 Merge pull request #2366 from acid-chicken/patch-autogen
[AUTOMATED] Update README.md
2018-08-22 03:30:12 +09:00
1ed189a518 Merge pull request #2386 from acid-chicken/patch-3
Update boot.js
2018-08-22 02:25:41 +09:00
137741d307 Update index.js 2018-08-22 01:52:13 +09:00
d702f6e090 Rename ja-ks.yml to ja-KS.yml 2018-08-22 01:51:42 +09:00
f33701233c Update boot.js 2018-08-22 01:50:13 +09:00
70003269e5 Update boot.js 2018-08-22 01:48:08 +09:00
61896d2386 Merge branch 'master' of https://github.com/syuilo/misskey 2018-08-22 01:46:13 +09:00
52d640c5a7 Fix 2018-08-22 01:46:10 +09:00
c65f5761e1 Merge pull request #2385 from mei23/mei-0821-aptype
ActivityPub で Content-Type を正しく扱う
2018-08-22 01:23:43 +09:00
3016ac4805 Fix bug 2018-08-22 01:02:56 +09:00
28a47cd331 Fix 2018-08-22 00:59:07 +09:00
6ecb88b0d1 #2338 2018-08-22 00:52:00 +09:00
8df1278c8e Merge branch 'master' of https://github.com/syuilo/misskey 2018-08-22 00:44:26 +09:00
52bec430d4 use ja-JP 2018-08-22 00:44:07 +09:00
da4cec8767 Merge pull request #2362 from syuilo/greenkeeper/vue-loader-15.4.0
Update vue-loader to the latest version 🚀
2018-08-22 00:11:24 +09:00
ad0087d7dd Merge pull request #2382 from syuilo/greenkeeper/webpack-4.17.0
Update webpack to the latest version 🚀
2018-08-22 00:11:12 +09:00
9630860035 Merge pull request #2383 from syuilo/greenkeeper/sharp-0.20.7
fix(package): update sharp to version 0.20.7
2018-08-22 00:11:01 +09:00
75e4c8d74d Better reponse 2018-08-21 23:56:15 +09:00
1a5ee81e7e fix(package): update sharp to version 0.20.7
Closes #2368
2018-08-21 11:18:21 +00:00
3476be16ab Merge pull request #2381 from mei23/mei-0821-apnote
ActivityPub Note/Outbox の公開範囲の修正
2018-08-21 20:07:33 +09:00
c42f61a0f4 fix(package): update webpack to version 4.17.0 2018-08-21 08:48:10 +00:00
b42a9e1c4e Set ActivityPub Content-Type 2018-08-21 13:48:03 +09:00
4495525705 Respect visibility in ActivityPub Note/Outbox 2018-08-21 13:22:30 +09:00
a603602f32 Clean up 2018-08-21 05:42:31 +09:00
fd947407af Revert "Fix bug?"
This reverts commit 2c9bacfcea.
2018-08-21 01:23:39 +09:00
30444e5f1a #2359 など 2018-08-21 01:03:58 +09:00
f0d818de24 Fix bug 2018-08-21 00:12:45 +09:00
3fb98e808f 7.1.2 2018-08-20 23:49:27 +09:00
2c9bacfcea Fix bug? 2018-08-20 23:49:00 +09:00
ae0284b1b1 Update README.md [AUTOGEN] 2018-08-20 19:03:01 +09:00
166c4ebda0 Update reversi.game.vue 2018-08-20 18:12:03 +09:00
319eed029b Update reversi.game.vue 2018-08-20 18:10:51 +09:00
ec5aa10167 fix(package): update vue-loader to version 15.4.0 2018-08-20 00:44:56 +00:00
a542765cf8 Merge pull request #2352 from acid-chicken/patch-2
Update README.md
2018-08-20 07:45:35 +09:00
fd3f8d43db Merge pull request #2356 from acid-chicken/patch-3
Fix z-index
2018-08-20 07:44:57 +09:00
4f4496078a Update user.vue 2018-08-20 02:05:57 +09:00
8f4f5b4ce0 7.1.1 2018-08-20 01:42:56 +09:00
bdc6718ae5 Fix bug 2018-08-20 01:41:53 +09:00
09d1f1c20d Update README.md 2018-08-19 23:14:22 +09:00
0efb7af17b 7.1.0 2018-08-19 23:11:55 +09:00
a45a78b94f Merge pull request #2336 from syuilo/l10n_master
New Crowdin translations
2018-08-19 23:06:47 +09:00
6337c26cf0 Merge pull request #2347 from syuilo/greenkeeper/html-minifier-3.5.20
Update html-minifier to the latest version 🚀
2018-08-19 23:06:34 +09:00
208dec25d9 Merge pull request #2349 from syuilo/configress-build
設定ファイルなしでビルドできるように
2018-08-19 22:32:58 +09:00
7d65a0c3d5 wip 2018-08-19 22:32:25 +09:00
bb925e5de3 wip 2018-08-19 21:07:18 +09:00
c9de5b65d4 wip 2018-08-19 19:15:29 +09:00
9bc3fcf74f Merge pull request #2348 from mei23/mei-0819-nsfw3
リモートにNSFWが効かないのを修正
2018-08-19 18:46:47 +09:00
19d979c330 Merge branch 'master' of https://github.com/syuilo/misskey 2018-08-19 18:38:15 +09:00
bb7b335491 nameId廃止 & アプリ作成時にシークレットを返すように 2018-08-19 18:38:02 +09:00
be5a0b4794 Fix リモートにNSFWが効かない 2018-08-19 18:08:29 +09:00
48eea03386 fix(package): update html-minifier to version 3.5.20 2018-08-19 09:03:23 +00:00
566317dc83 Fix bug 2018-08-19 13:32:02 +09:00
d4334645c2 New translations ja.yml (Japanese (Kansai-ben)) 2018-08-19 12:41:16 +09:00
8cc017354a New translations ja.yml (English) 2018-08-19 04:11:04 +09:00
7caa083612 New translations ja.yml (Japanese (Kansai-ben)) 2018-08-19 04:01:58 +09:00
68e1b00eb1 New translations ja.yml (Catalan) 2018-08-19 04:01:55 +09:00
1d904c756a New translations ja.yml (Portuguese) 2018-08-19 04:01:53 +09:00
0a1db1f595 New translations ja.yml (Korean) 2018-08-19 04:01:50 +09:00
e30e8267dd New translations ja.yml (Polish) 2018-08-19 04:01:48 +09:00
288a881817 New translations ja.yml (Chinese Simplified) 2018-08-19 04:01:46 +09:00
f593790872 New translations ja.yml (Italian) 2018-08-19 04:01:43 +09:00
1044ad8589 New translations ja.yml (Russian) 2018-08-19 04:01:41 +09:00
6e24015e68 New translations ja.yml (English) 2018-08-19 04:01:39 +09:00
b9a2c449ff New translations ja.yml (Spanish) 2018-08-19 04:01:37 +09:00
7c390cbf7b New translations ja.yml (German) 2018-08-19 04:01:35 +09:00
a657d1c774 New translations ja.yml (French) 2018-08-19 04:01:33 +09:00
79 changed files with 548 additions and 377 deletions

View File

@ -39,9 +39,15 @@ please see [Setup and installation guide](./docs/setup.en.md).
---------------------------------------------------------------- ----------------------------------------------------------------
**[PR](https://github.com/syuilo/misskey/pulls)s welcome!** **[PR](https://github.com/syuilo/misskey/pulls)s welcome!**
If you want to... ### i18n
* i18n ... please see [Translation guide](./docs/translate.en.md).
* l10n ... please visit https://crowdin.com/project/misskey Please see [Translation guide](./docs/translate.en.md).
### l10n
Misskey is using Crowdin for l10n.
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)](https://crowdin.com/project/misskey)
:heart: Backers & Sponsors :heart: Backers & Sponsors
---------------------------------------------------------------- ----------------------------------------------------------------
@ -53,8 +59,8 @@ If you want to...
<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/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/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D" alt="gutfuckllc"></td>
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td> <td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/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/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/12931605/ead494101f364dffa90efe49e36fb494/1?token-time=2145916800&token-hash=NzSFPjIlodXyv41rwK61aZWVZWfI4surJaNj8vWKvqM%3D" alt="Reiju"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/user?u=12378075">39ff</a></td> <td><a href="https://www.patreon.com/user?u=12378075">39ff</a></td>
<td><a href="https://www.patreon.com/user?u=12731202">negao</a></td> <td><a href="https://www.patreon.com/user?u=12731202">negao</a></td>
@ -62,22 +68,26 @@ If you want to...
<td><a href="https://www.patreon.com/user?u=3384329">べすれい</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/gutfuckllc">gutfuckllc</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=12718187">Peter G.</a></td>
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
<td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td> <td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td>
<td><a href="https://www.patreon.com/user?u=12931605">Reiju</a></td>
</tr></table> </tr></table>
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12931605/ead494101f364dffa90efe49e36fb494/1?token-time=2145916800&token-hash=NzSFPjIlodXyv41rwK61aZWVZWfI4surJaNj8vWKvqM%3D" alt="Reiju"></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/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/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/4950409/28e7d016209243759d9316be2e21381d/2?token-time=2145916800&token-hash=LuEaDkchH3GQWUcTOhBQ8xfKQYF0s5FjlZRd7Yduia8%3D" alt="mikan54951"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4950409/28e7d016209243759d9316be2e21381d/2?token-time=2145916800&token-hash=LuEaDkchH3GQWUcTOhBQ8xfKQYF0s5FjlZRd7Yduia8%3D" alt="mikan54951"></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> <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>
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12959468/c249e15aebec4424b5c0f427173671b6/1?token-time=2145916800&token-hash=lubpCEdxAkxPlpR2O6bvZ7BIh8Q4nGf-U_mE1qpjVAQ%3D" alt="fujishan"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12959468/c249e15aebec4424b5c0f427173671b6/1?token-time=2145916800&token-hash=lubpCEdxAkxPlpR2O6bvZ7BIh8Q4nGf-U_mE1qpjVAQ%3D" alt="fujishan"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/user?u=12931605">Reiju</a></td>
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
<td><a href="https://www.patreon.com/dansup">dansup</a></td> <td><a href="https://www.patreon.com/dansup">dansup</a></td>
<td><a href="https://www.patreon.com/user?u=4950409">mikan54951</a></td> <td><a href="https://www.patreon.com/user?u=4950409">mikan54951</a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td> <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
<td><a href="https://www.patreon.com/fujishan">fujishan</a></td> <td><a href="https://www.patreon.com/fujishan">fujishan</a></td>
</tr></table> </tr></table>
**Last updated:** Sat, 18 Aug 2018 02:02:58 UTC **Last updated:** Mon, 20 Aug 2018 10:02:31 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

View File

@ -11,12 +11,12 @@ If you find an untranslated part on Misskey:
- In fact, `foo` should be a word that is appropriate for the situation and is easy to understand in English. - In fact, `foo` should be a word that is appropriate for the situation and is easy to understand in English.
- For example, if the untranslated portion is the following "タイムライン" you must write: `%i18n:@timeline%`. - For example, if the untranslated portion is the following "タイムライン" you must write: `%i18n:@timeline%`.
3. Open the `locales/ja.yml`, check whether the <strong>file name (path)</strong> found in step 1 exists, if not, create it. 3. Open the `locales/ja-JP.yml`, check whether the <strong>file name (path)</strong> found in step 1 exists, if not, create it.
- Do not put the beginning of the path `src/client/app/` in the locale file. - Do not put the beginning of the path `src/client/app/` in the locale file.
- For example, in this case we want to modify untranslated parts of `src/client/app/mobile/views/pages/home.vue`, so the key is `mobile/views/pages/home.vue`. - For example, in this case we want to modify untranslated parts of `src/client/app/mobile/views/pages/home.vue`, so the key is `mobile/views/pages/home.vue`.
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. 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.yml`. - For example, in this case we add timeline: `timeline: "タイムライン"` to `locales/ja-JP.yml`.
5. And done 5. And done

View File

@ -16,7 +16,7 @@ Si vous trouvez un segment non-traduit sur Misskey :
- Par exemple, dans ce cas de figure, nous voulons modifier le segment non-traduit de : `src/client/app/mobile/views/pages/home.vue`donc il faut juste écrire : `mobile/views/pages/home.vue` dans les fichiers linguistiques. - Par exemple, dans ce cas de figure, nous voulons modifier le segment non-traduit de : `src/client/app/mobile/views/pages/home.vue`donc il faut juste écrire : `mobile/views/pages/home.vue` dans les fichiers linguistiques.
4. Ajoutez la propriété du texte traduit grâce à la clef `foo`, en-dessous du chemin correspondant à votre modification que vous avez trouvé ou créé dans l'étape 2. À côté, veuillez indiquer entre "guillemets" la valeur de votre traduction. 4. Ajoutez la propriété du texte traduit grâce à la clef `foo`, en-dessous du chemin correspondant à votre modification que vous avez trouvé ou créé dans l'étape 2. À côté, veuillez indiquer entre "guillemets" la valeur de votre traduction.
- Par exemple, dans ce cas de figure, nous ajoutons la propriété et la traduction `timeline: "Timeline"` à `locales/fr.yml`, mais aussi la propriété et la version originale `timeline: "タイムライン"` à `locales/ja.yml`. - Par exemple, dans ce cas de figure, nous ajoutons la propriété et la traduction `timeline: "Timeline"` à `locales/fr.yml`, mais aussi la propriété et la version originale `timeline: "タイムライン"` à `locales/ja-JP.yml`.
5. Vous avez réussi à traduire une portion de misskey 5. Vous avez réussi à traduire une portion de misskey

View File

@ -11,12 +11,12 @@ Misskey内の未翻訳箇所を見つけたら
- `foo`は実際にはその場に適したわかりやすい(英語の)名前にしてください。 - `foo`は実際にはその場に適したわかりやすい(英語の)名前にしてください。
- 例えば未翻訳箇所が「タイムライン」というテキストだった場合、`%i18n:@timeline%`のようにします。 - 例えば未翻訳箇所が「タイムライン」というテキストだった場合、`%i18n:@timeline%`のようにします。
3. `locales/ja.yml`を開き、1.で見つけた<strong>ファイル名(パス)</strong>のキーが存在するか確認し、無ければ作成してください。 3. `locales/ja-JP.yml`を開き、1.で見つけた<strong>ファイル名(パス)</strong>のキーが存在するか確認し、無ければ作成してください。
- パスの`src/client/app/`は省略してください。 - パスの`src/client/app/`は省略してください。
- 例えば、今回の例では`src/client/app/mobile/views/pages/home.vue`の未翻訳箇所を修正したいので、キーは`mobile/views/pages/home.vue`になります。 - 例えば、今回の例では`src/client/app/mobile/views/pages/home.vue`の未翻訳箇所を修正したいので、キーは`mobile/views/pages/home.vue`になります。
4. そのキーの直下に2.で置換した`foo`の部分をキーとし、テキストを値とするプロパティを追加します。 4. そのキーの直下に2.で置換した`foo`の部分をキーとし、テキストを値とするプロパティを追加します。
- 例えば、今回の例で言うと`locales/ja.yml``timeline: "タイムライン"`を追加します。 - 例えば、今回の例で言うと`locales/ja-JP.yml``timeline: "タイムライン"`を追加します。
5. 完了です! 5. 完了です!

View File

@ -23,7 +23,6 @@ const uglifyes = require('uglify-es');
const locales = require('./locales'); const locales = require('./locales');
import { fa } from './src/misc/fa'; import { fa } from './src/misc/fa';
import config from './src/config';
const uglify = uglifyComposer(uglifyes, console); const uglify = uglifyComposer(uglifyes, console);
@ -118,7 +117,6 @@ gulp.task('build:client:script', () => {
const client = require('./built/client/meta.json'); const client = require('./built/client/meta.json');
return gulp.src(['./src/client/app/boot.js', './src/client/app/safe.js']) return gulp.src(['./src/client/app/boot.js', './src/client/app/safe.js'])
.pipe(replace('VERSION', JSON.stringify(client.version))) .pipe(replace('VERSION', JSON.stringify(client.version)))
.pipe(replace('API', JSON.stringify(config.api_url)))
.pipe(replace('ENV', JSON.stringify(env))) .pipe(replace('ENV', JSON.stringify(env)))
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales)))) .pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
.pipe(isProduction ? uglify({ .pipe(isProduction ? uglify({

View File

@ -1,4 +1,4 @@
# **Please DO NOT edit these files** except `ja.yml`. # **Please DO NOT edit these files** except `ja-JP.yml`.
If you want to... If you want to...
* i18n ... please see [Translation guide](../docs/translate.en.md). * i18n ... please see [Translation guide](../docs/translate.en.md).

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "名前を変更" rename: "名前を変更"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "Umbenennen" rename: "Umbenennen"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
nsfw: "NSFW"
contextmenu: contextmenu:
rename: "Rename" rename: "Rename"
mark-as-sensitive: "Mark as 'sensitive'" mark-as-sensitive: "Mark as 'sensitive'"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "Choose files" select-file: "Choose files"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "Choose a folder" select-folder: "Choose a folder"
mobile/views/components/drive.file.vue:
nsfw: "NSFW"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "Download" download: "Download"
rename: "Rename" rename: "Rename"
move: "Move" move: "Move"
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "NSFW"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "NSFW" sensitive: "NSFW"
click-to-show: "Click to show" click-to-show: "Click to show"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "Renombrar" rename: "Renombrar"
mark-as-sensitive: "Marcar como 'sensible'" mark-as-sensitive: "Marcar como 'sensible'"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "Avatar" avatar: "Avatar"
banner: "Bannière" banner: "Bannière"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "Renommer" rename: "Renommer"
mark-as-sensitive: "Marquer comme sensible" mark-as-sensitive: "Marquer comme sensible"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "Choisissez un fichier" select-file: "Choisissez un fichier"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "Choisissez un dossier" select-folder: "Choisissez un dossier"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "Télécharger" download: "Télécharger"
rename: "Renommer" rename: "Renommer"
move: "Déplacer" move: "Déplacer"
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "Le contenu est NSFW" sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher" click-to-show: "Cliquer pour afficher"

View File

@ -8,14 +8,14 @@ const yaml = require('js-yaml');
const loadLang = lang => yaml.safeLoad( const loadLang = lang => yaml.safeLoad(
fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8')); fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8'));
const native = loadLang('ja'); const native = loadLang('ja-JP');
const langs = { const langs = {
'de': loadLang('de'), 'de': loadLang('de'),
'en': loadLang('en'), 'en': loadLang('en'),
'fr': loadLang('fr'), 'fr': loadLang('fr'),
'ja': native, 'ja': native,
'ja-ks': loadLang('ja-ks'), 'ja-KS': loadLang('ja-KS'),
'pl': loadLang('pl'), 'pl': loadLang('pl'),
'es': loadLang('es') 'es': loadLang('es')
}; };

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "名前を変更" rename: "名前を変更"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

File diff suppressed because it is too large Load Diff

View File

@ -691,7 +691,6 @@ desktop/views/components/settings.vue:
password: "パスワード" password: "パスワード"
2fa: "二段階認証" 2fa: "二段階認証"
other: "その他" other: "その他"
license: "ライセンス"
behaviour: "動作" behaviour: "動作"
fetch-on-scroll: "スクロールで自動読み込み" fetch-on-scroll: "スクロールで自動読み込み"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "名前を変更" rename: "名前を変更"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "Awatar" avatar: "Awatar"
banner: "Baner" banner: "Baner"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "Zmień nazwę" rename: "Zmień nazwę"
mark-as-sensitive: "Oznacz jako zawartość wrażliwą" mark-as-sensitive: "Oznacz jako zawartość wrażliwą"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "Wybierz plik" select-file: "Wybierz plik"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "Wybierz katalog" select-folder: "Wybierz katalog"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "Pobierz" download: "Pobierz"
rename: "Zmień nazwę" rename: "Zmień nazwę"
move: "Przenieś" move: "Przenieś"
hash: "Hash (md5)" hash: "Hash (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "To jest zawartość NSFW" sensitive: "To jest zawartość NSFW"
click-to-show: "Naciśnij aby wyświetlić" click-to-show: "Naciśnij aby wyświetlić"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "名前を変更" rename: "名前を変更"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "名前を変更" rename: "名前を変更"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -443,6 +443,7 @@ desktop/views/components/drive-window.vue:
desktop/views/components/drive.file.vue: desktop/views/components/drive.file.vue:
avatar: "アイコン" avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意"
contextmenu: contextmenu:
rename: "名前を変更" rename: "名前を変更"
mark-as-sensitive: "閲覧注意に設定" mark-as-sensitive: "閲覧注意に設定"
@ -954,12 +955,15 @@ mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
mobile/views/components/drive-folder-chooser.vue: mobile/views/components/drive-folder-chooser.vue:
select-folder: "フォルダーを選択" select-folder: "フォルダーを選択"
mobile/views/components/drive.file.vue:
nsfw: "閲覧注意"
mobile/views/components/drive.file-detail.vue: mobile/views/components/drive.file-detail.vue:
download: "ダウンロード" download: "ダウンロード"
rename: "名前を変更" rename: "名前を変更"
move: "移動" move: "移動"
hash: "ハッシュ (md5)" hash: "ハッシュ (md5)"
exif: "EXIF" exif: "EXIF"
nsfw: "閲覧注意"
mobile/views/components/media-image.vue: mobile/views/components/media-image.vue:
sensitive: "閲覧注意" sensitive: "閲覧注意"
click-to-show: "クリックして表示" click-to-show: "クリックして表示"

View File

@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "7.0.2", "version": "7.3.0",
"clientVersion": "1.0.8654", "clientVersion": "1.0.8741",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "main": "./built/index.js",
"private": true, "private": true,
@ -126,7 +126,7 @@
"gulp-util": "3.0.8", "gulp-util": "3.0.8",
"hard-source-webpack-plugin": "0.12.0", "hard-source-webpack-plugin": "0.12.0",
"highlight.js": "9.12.0", "highlight.js": "9.12.0",
"html-minifier": "3.5.19", "html-minifier": "3.5.20",
"http-signature": "1.2.0", "http-signature": "1.2.0",
"insert-text-at-cursor": "0.1.1", "insert-text-at-cursor": "0.1.1",
"is-root": "2.0.0", "is-root": "2.0.0",
@ -157,6 +157,7 @@
"monk": "6.0.6", "monk": "6.0.6",
"ms": "2.1.1", "ms": "2.1.1",
"nan": "2.10.0", "nan": "2.10.0",
"nested-property": "0.0.7",
"node-sass": "4.9.3", "node-sass": "4.9.3",
"node-sass-json-importer": "3.3.1", "node-sass-json-importer": "3.3.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
@ -181,7 +182,7 @@
"s-age": "1.1.2", "s-age": "1.1.2",
"sass-loader": "7.1.0", "sass-loader": "7.1.0",
"seedrandom": "2.4.4", "seedrandom": "2.4.4",
"sharp": "0.20.5", "sharp": "0.20.7",
"showdown": "1.8.6", "showdown": "1.8.6",
"showdown-highlightjs-extension": "0.1.2", "showdown-highlightjs-extension": "0.1.2",
"single-line-log": "1.1.2", "single-line-log": "1.1.2",
@ -208,7 +209,7 @@
"vue-cropperjs": "2.2.1", "vue-cropperjs": "2.2.1",
"vue-js-modal": "1.3.17", "vue-js-modal": "1.3.17",
"vue-json-tree-view": "2.1.4", "vue-json-tree-view": "2.1.4",
"vue-loader": "15.3.0", "vue-loader": "15.4.0",
"vue-router": "3.0.1", "vue-router": "3.0.1",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-template-compiler": "2.5.17", "vue-template-compiler": "2.5.17",
@ -217,7 +218,7 @@
"vuex-persistedstate": "2.5.4", "vuex-persistedstate": "2.5.4",
"web-push": "3.3.2", "web-push": "3.3.2",
"webfinger.js": "2.6.6", "webfinger.js": "2.6.6",
"webpack": "4.16.5", "webpack": "4.17.0",
"webpack-cli": "3.1.0", "webpack-cli": "3.1.0",
"websocket": "1.0.26", "websocket": "1.0.26",
"ws": "6.0.0", "ws": "6.0.0",

View File

@ -7,7 +7,7 @@
<div class="app"> <div class="app">
<section> <section>
<h2>{{ app.name }}</h2> <h2>{{ app.name }}</h2>
<p class="nid">{{ app.nameId }}</p> <p class="id">{{ app.id }}</p>
<p class="description">{{ app.description }}</p> <p class="description">{{ app.description }}</p>
</section> </section>
<section> <section>

View File

@ -32,13 +32,15 @@
//#region Detect app name //#region Detect app name
let app = null; let app = null;
if (url.pathname == '/docs' || url.pathname.startsWith('/docs/')) app = 'docs'; if (`${url.pathname}/`.startsWith('/docs/')) app = 'docs';
if (url.pathname == '/dev' || url.pathname.startsWith('/dev/')) app = 'dev'; if (`${url.pathname}/`.startsWith('/dev/')) app = 'dev';
if (url.pathname == '/auth' || url.pathname.startsWith('/auth/')) app = 'auth'; if (`${url.pathname}/`.startsWith('/auth/')) app = 'auth';
//#endregion //#endregion
//#region Detect the user language //#region Detect the user language
let lang = navigator.language.split('-')[0]; let lang = navigator.language;
if (!LANGS.includes(lang)) lang = lang.split('-')[0];
// The default language is English // The default language is English
if (!LANGS.includes(lang)) lang = 'en'; if (!LANGS.includes(lang)) lang = 'en';
@ -104,7 +106,7 @@
// グローバルにタイマーIDを代入しておく // グローバルにタイマーIDを代入しておく
window.mkBootTimer = window.setTimeout(async () => { window.mkBootTimer = window.setTimeout(async () => {
// Fetch meta // Fetch meta
const res = await fetch(API + '/meta', { const res = await fetch('/api/meta', {
method: 'POST', method: 'POST',
cache: 'no-cache' cache: 'no-cache'
}); });

View File

@ -18,11 +18,11 @@
</div> </div>
<div class="board"> <div class="board">
<div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels"> <div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span> <span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div> </div>
<div class="flex"> <div class="flex">
<div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels"> <div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<div v-for="i in game.settings.map.length">{{ i }}</div> <div v-for="i in game.settings.map.length">{{ i }}</div>
</div> </div>
<div class="cells" :style="cellsStyle"> <div class="cells" :style="cellsStyle">
@ -30,15 +30,15 @@
:class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }" :class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }"
@click="set(i)" @click="set(i)"
:title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`"> :title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`">
<img v-if="stone === true" :src="blackUser.avatarUrl" alt=""> <img v-if="stone === true" :src="blackUser.avatarUrl" alt="black" :class="{ contrast: $store.state.settings.games.reversi.useContrastStones }">
<img v-if="stone === false" :src="whiteUser.avatarUrl" alt=""> <img v-if="stone === false" :src="whiteUser.avatarUrl" alt="white" :class="{ contrast: $store.state.settings.games.reversi.useContrastStones }">
</div> </div>
</div> </div>
<div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels"> <div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<div v-for="i in game.settings.map.length">{{ i }}</div> <div v-for="i in game.settings.map.length">{{ i }}</div>
</div> </div>
</div> </div>
<div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels"> <div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span> <span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div> </div>
</div> </div>
@ -421,6 +421,13 @@ root(isDark)
width 100% width 100%
height 100% height 100%
&.contrast
&[alt="black"]
filter brightness(.5)
&[alt="white"]
filter brightness(2)
> .graph > .graph
display grid display grid
grid-template-columns repeat(61, 1fr) grid-template-columns repeat(61, 1fr)

View File

@ -6,7 +6,7 @@
<i></i> <i></i>
<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a> <a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a>
<i></i> <i></i>
<a :href="devUrl">%i18n:@develop%</a> <a href="/dev">%i18n:@develop%</a>
<i></i> <i></i>
<a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a> <a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a>
</span> </span>
@ -14,18 +14,21 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { docsUrl, statsUrl, statusUrl, devUrl, repositoryUrl, feedbackUrl, lang } from '../../../config'; import { lang } from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
return { return {
aboutUrl: `${docsUrl}/${lang}/about`, aboutUrl: `/docs/${lang}/about`,
statsUrl, repositoryUrl: 'https://github.com/syuilo/misskey',
statusUrl, feedbackUrl: 'https://github.com/syuilo/misskey/issues/new'
devUrl,
repositoryUrl: repositoryUrl || `https://github.com/syuilo/misskey`,
feedbackUrl: feedbackUrl || `https://github.com/syuilo/misskey/issues/new`
} }
},
created() {
(this as any).os.getMeta().then(meta => {
if (meta.repositoryUrl) this.repositoryUrl = meta.repositoryUrl;
if (meta.feedbackUrl) this.feedbackUrl = meta.feedbackUrl;
});
} }
}); });
</script> </script>

View File

@ -12,13 +12,13 @@
</ui-input> </ui-input>
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/> <ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button> <ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
<p style="margin: 8px 0;" v-if="twitterIntegration">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p> <p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
</form> </form>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { apiUrl, host, twitterIntegration } from '../../../config'; import { apiUrl, host } from '../../../config';
export default Vue.extend({ export default Vue.extend({
props: { props: {
@ -36,8 +36,7 @@ export default Vue.extend({
password: '', password: '',
token: '', token: '',
apiUrl, apiUrl,
host, host
twitterIntegration
}; };
}, },
methods: { methods: {

View File

@ -1,48 +1,50 @@
<template> <template>
<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()"> <form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required> <template v-if="meta">
<span>%i18n:@invitation-code%</span> <ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
<span slot="prefix">%fa:id-card-alt%</span> <span>%i18n:@invitation-code%</span>
<p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p> <span slot="prefix">%fa:id-card-alt%</span>
</ui-input> <p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p>
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername"> </ui-input>
<span>%i18n:@username%</span> <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername">
<span slot="prefix">@</span> <span>%i18n:@username%</span>
<span slot="suffix">@{{ host }}</span> <span slot="prefix">@</span>
<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p> <span slot="suffix">@{{ host }}</span>
<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p> <p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p> <p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p> <p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p> <p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p> <p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p> <p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
</ui-input> <p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true"> </ui-input>
<span>%i18n:@password%</span> <ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true">
<span slot="prefix">%fa:lock%</span> <span>%i18n:@password%</span>
<div slot="text"> <span slot="prefix">%fa:lock%</span>
<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p> <div slot="text">
<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p> <p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p> <p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
</div> <p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
</ui-input> </div>
<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype"> </ui-input>
<span>%i18n:@password% (%i18n:@retype%)</span> <ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
<span slot="prefix">%fa:lock%</span> <span>%i18n:@password% (%i18n:@retype%)</span>
<div slot="text"> <span slot="prefix">%fa:lock%</span>
<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p> <div slot="text">
<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p> <p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
</div> <p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
</ui-input> </div>
<div v-if="recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="recaptchaSitekey" style="margin: 16px 0;"></div> </ui-input>
<ui-button type="submit">%i18n:@create%</ui-button> <div v-if="meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div>
<ui-button type="submit">%i18n:@create%</ui-button>
</template>
</form> </form>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
const getPasswordStrength = require('syuilo-password-strength'); const getPasswordStrength = require('syuilo-password-strength');
import { host, url, recaptchaSitekey } from '../../../config'; import { host, url } from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -53,7 +55,6 @@ export default Vue.extend({
retypedPassword: '', retypedPassword: '',
invitationCode: '', invitationCode: '',
url, url,
recaptchaSitekey,
usernameState: null, usernameState: null,
passwordStrength: '', passwordStrength: '',
passwordRetypeState: null, passwordRetypeState: null,
@ -73,6 +74,12 @@ export default Vue.extend({
this.meta = meta; this.meta = meta;
}); });
}, },
mounted() {
const head = document.getElementsByTagName('head')[0];
const script = document.createElement('script');
script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
head.appendChild(script);
},
methods: { methods: {
onChangeUsername() { onChangeUsername() {
if (this.username == '') { if (this.username == '') {
@ -123,7 +130,7 @@ export default Vue.extend({
username: this.username, username: this.username,
password: this.password, password: this.password,
invitationCode: this.invitationCode, invitationCode: this.invitationCode,
'g-recaptcha-response': recaptchaSitekey != null ? (window as any).grecaptcha.getResponse() : null 'g-recaptcha-response': this.meta.recaptchaSitekey != null ? (window as any).grecaptcha.getResponse() : null
}).then(() => { }).then(() => {
(this as any).api('signin', { (this as any).api('signin', {
username: this.username, username: this.username,
@ -134,19 +141,11 @@ export default Vue.extend({
}).catch(() => { }).catch(() => {
alert('%i18n:@some-error%'); alert('%i18n:@some-error%');
if (recaptchaSitekey != null) { if (this.meta.recaptchaSitekey != null) {
(window as any).grecaptcha.reset(); (window as any).grecaptcha.reset();
} }
}); });
} }
},
mounted() {
if (recaptchaSitekey != null) {
const head = document.getElementsByTagName('head')[0];
const script = document.createElement('script');
script.setAttribute('src', 'https://www.google.com/recaptcha/api.js');
head.appendChild(script);
}
} }
}); });
</script> </script>

View File

@ -1,51 +1,20 @@
declare const _HOST_: string;
declare const _HOSTNAME_: string;
declare const _URL_: string;
declare const _NAME_: string;
declare const _DESCRIPTION_: string;
declare const _API_URL_: string;
declare const _WS_URL_: string;
declare const _DOCS_URL_: string;
declare const _STATS_URL_: string;
declare const _STATUS_URL_: string;
declare const _DEV_URL_: string;
declare const _REPOSITORY_URL_: string;
declare const _FEEDBACK_URL_: string;
declare const _LANG_: string; declare const _LANG_: string;
declare const _LANGS_: string; declare const _LANGS_: string;
declare const _RECAPTCHA_SITEKEY_: string;
declare const _SW_PUBLICKEY_: string;
declare const _THEME_COLOR_: string; declare const _THEME_COLOR_: string;
declare const _COPYRIGHT_: string; declare const _COPYRIGHT_: string;
declare const _VERSION_: string; declare const _VERSION_: string;
declare const _CODENAME_: string; declare const _CODENAME_: string;
declare const _LICENSE_: string;
declare const _GOOGLE_MAPS_API_KEY_: string;
declare const _WELCOME_BG_URL_: string;
declare const _TWITTER_INTEGRATION_: boolean;
export const host = _HOST_; const address = new URL(location.href);
export const hostname = _HOSTNAME_;
export const url = _URL_; export const host = address.host;
export const name = _NAME_; export const hostname = address.hostname;
export const description = _DESCRIPTION_; export const url = address.origin;
export const apiUrl = _API_URL_; export const apiUrl = url + '/api';
export const wsUrl = _WS_URL_; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://');
export const docsUrl = _DOCS_URL_;
export const statsUrl = _STATS_URL_;
export const statusUrl = _STATUS_URL_;
export const devUrl = _DEV_URL_;
export const repositoryUrl = _REPOSITORY_URL_;
export const feedbackUrl = _FEEDBACK_URL_;
export const lang = _LANG_; export const lang = _LANG_;
export const langs = _LANGS_; export const langs = _LANGS_;
export const recaptchaSitekey = _RECAPTCHA_SITEKEY_;
export const swPublickey = _SW_PUBLICKEY_;
export const themeColor = _THEME_COLOR_; export const themeColor = _THEME_COLOR_;
export const copyright = _COPYRIGHT_; export const copyright = _COPYRIGHT_;
export const version = _VERSION_; export const version = _VERSION_;
export const codename = _CODENAME_; export const codename = _CODENAME_;
export const license = _LICENSE_;
export const googleMapsApiKey = _GOOGLE_MAPS_API_KEY_;
export const welcomeBgUrl = _WELCOME_BG_URL_;
export const twitterIntegration = _TWITTER_INTEGRATION_;

View File

@ -193,7 +193,7 @@ export default Vue.extend({
clearNotification() { clearNotification() {
this.unreadCount = 0; this.unreadCount = 0;
document.title = config.name; document.title = (this as any).os.instanceName;
}, },
onVisibilitychange() { onVisibilitychange() {

View File

@ -56,8 +56,9 @@
<mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%"> <mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
<span>%i18n:@show-maps-desc%</span> <span>%i18n:@show-maps-desc%</span>
</mk-switch> </mk-switch>
<mk-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
<mk-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/> <mk-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
<mk-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
<mk-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones" text="%i18n:common.use-contrast-reversi-stones%"/>
</section> </section>
<section class="web" v-show="page == 'web'"> <section class="web" v-show="page == 'web'">
@ -191,12 +192,6 @@
<button class="ui button block" @click="taskmngr">%i18n:@task-manager%</button> <button class="ui button block" @click="taskmngr">%i18n:@task-manager%</button>
</details> </details>
</section> </section>
<section class="other" v-show="page == 'other'">
<h1>%i18n:@license%</h1>
<div v-html="license"></div>
<a :href="licenseUrl" target="_blank">%i18n:@third-parties%</a>
</section>
</div> </div>
</div> </div>
</template> </template>
@ -211,7 +206,7 @@ import XApi from './settings.api.vue';
import XApps from './settings.apps.vue'; import XApps from './settings.apps.vue';
import XSignins from './settings.signins.vue'; import XSignins from './settings.signins.vue';
import XDrive from './settings.drive.vue'; import XDrive from './settings.drive.vue';
import { url, docsUrl, license, lang, langs, version } from '../../../config'; import { url, langs, version } from '../../../config';
import checkForUpdate from '../../../common/scripts/check-for-update'; import checkForUpdate from '../../../common/scripts/check-for-update';
import MkTaskManager from './taskmanager.vue'; import MkTaskManager from './taskmanager.vue';
@ -230,7 +225,6 @@ export default Vue.extend({
return { return {
page: 'profile', page: 'profile',
meta: null, meta: null,
license,
version, version,
langs, langs,
latestVersion: undefined, latestVersion: undefined,
@ -238,10 +232,6 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
licenseUrl(): string {
return `${docsUrl}/${lang}/license`;
},
apiViaStream: { apiViaStream: {
get() { return this.$store.state.device.apiViaStream; }, get() { return this.$store.state.device.apiViaStream; },
set(value) { this.$store.commit('device/set', { key: 'apiViaStream', value }); } set(value) { this.$store.commit('device/set', { key: 'apiViaStream', value }); }
@ -387,7 +377,13 @@ export default Vue.extend({
}, },
onChangeReversiBoardLabels(v) { onChangeReversiBoardLabels(v) {
this.$store.dispatch('settings/set', { this.$store.dispatch('settings/set', {
key: 'reversiBoardLabels', key: 'games.reversi.showBoardLabels',
value: v
});
},
onChangeUseContrastReversiStones(v) {
this.$store.dispatch('settings/set', {
key: 'games.reversi.useContrastStones',
value: v value: v
}); });
}, },

View File

@ -4,11 +4,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
mounted() { mounted() {
document.title = `${config.name} - %i18n:@title%`; document.title = `${(this as any).os.instanceName} - %i18n:@title%`;
} }
}); });
</script> </script>

View File

@ -7,7 +7,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
props: { props: {
@ -17,7 +16,7 @@ export default Vue.extend({
} }
}, },
mounted() { mounted() {
document.title = config.name; document.title = (this as any).os.instanceName;
Progress.start(); Progress.start();
}, },

View File

@ -12,12 +12,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
return { return {
name: config.name, name: (this as any).os.instanceName,
posted: false, posted: false,
text: new URLSearchParams(location.search).get('text') text: new URLSearchParams(location.search).get('text')
}; };

View File

@ -5,7 +5,7 @@
<template v-if="$store.state.device.darkmode">%fa:moon%</template> <template v-if="$store.state.device.darkmode">%fa:moon%</template>
<template v-else>%fa:R moon%</template> <template v-else>%fa:R moon%</template>
</button> </button>
<div class="body" :style="{ backgroundImage: `url('${ welcomeBgUrl }')` }"> <div class="body">
<div class="container"> <div class="container">
<div class="info"> <div class="info">
<span><b>{{ host }}</b></span> <span><b>{{ host }}</b></span>
@ -46,22 +46,26 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { host, name, description, copyright, welcomeBgUrl } from '../../../config'; import { host, copyright } from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
return { return {
stats: null, stats: null,
copyright, copyright,
welcomeBgUrl,
host, host,
name, name: 'Misskey',
description, description: '',
pointerInterval: null, pointerInterval: null,
tags: [] tags: []
}; };
}, },
created() { created() {
(this as any).os.getMeta().then(meta => {
this.name = meta.name;
this.description = meta.description;
});
(this as any).api('stats').then(stats => { (this as any).api('stats').then(stats => {
this.stats = stats; this.stats = stats;
}); });

View File

@ -5,16 +5,6 @@
<b-form-group label="アプリケーション名" description="あなたのアプリの名称。"> <b-form-group label="アプリケーション名" description="あなたのアプリの名称。">
<b-form-input v-model="name" type="text" placeholder="ex) Misskey for iOS" autocomplete="off" required/> <b-form-input v-model="name" type="text" placeholder="ex) Misskey for iOS" autocomplete="off" required/>
</b-form-group> </b-form-group>
<b-form-group label="ID" description="あなたのアプリのID。">
<b-input v-model="nid" type="text" pattern="^[a-zA-Z0-9_]{1,30}$" placeholder="ex) misskey-for-ios" autocomplete="off" required/>
<p class="info" v-if="nidState == 'wait'" style="color:#999">%fa:spinner .pulse .fw%確認しています...</p>
<p class="info" v-if="nidState == 'ok'" style="color:#3CB7B5">%fa:fw check%利用できます</p>
<p class="info" v-if="nidState == 'unavailable'" style="color:#FF1161">%fa:fw exclamation-triangle%既に利用されています</p>
<p class="info" v-if="nidState == 'error'" style="color:#FF1161">%fa:fw exclamation-triangle%通信エラー</p>
<p class="info" v-if="nidState == 'invalid-format'" style="color:#FF1161">%fa:fw exclamation-triangle%a~zA~Z0~9_が使えます</p>
<p class="info" v-if="nidState == 'min-range'" style="color:#FF1161">%fa:fw exclamation-triangle%1文字以上でお願いします</p>
<p class="info" v-if="nidState == 'max-range'" style="color:#FF1161">%fa:fw exclamation-triangle%30文字以内でお願いします</p>
</b-form-group>
<b-form-group label="アプリの概要" description="あなたのアプリの簡単な説明や紹介。"> <b-form-group label="アプリの概要" description="あなたのアプリの簡単な説明や紹介。">
<b-textarea v-model="description" placeholder="ex) Misskey iOSクライアント。" autocomplete="off" required></b-textarea> <b-textarea v-model="description" placeholder="ex) Misskey iOSクライアント。" autocomplete="off" required></b-textarea>
</b-form-group> </b-form-group>
@ -50,47 +40,16 @@ export default Vue.extend({
data() { data() {
return { return {
name: '', name: '',
nid: '',
description: '', description: '',
cb: '', cb: '',
nidState: null, nidState: null,
permission: [] permission: []
}; };
}, },
watch: {
nid() {
if (this.nid == null || this.nid == '') {
this.nidState = null;
return;
}
const err =
!this.nid.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
this.nid.length < 1 ? 'min-range' :
this.nid.length > 30 ? 'max-range' :
null;
if (err) {
this.nidState = err;
return;
}
this.nidState = 'wait';
(this as any).api('app/name_id/available', {
nameId: this.nid
}).then(result => {
this.nidState = result.available ? 'ok' : 'unavailable';
}).catch(err => {
this.nidState = 'error';
});
}
},
methods: { methods: {
onSubmit() { onSubmit() {
(this as any).api('app/create', { (this as any).api('app/create', {
name: this.name, name: this.name,
nameId: this.nid,
description: this.description, description: this.description,
callbackUrl: this.cb, callbackUrl: this.cb,
permission: this.permission permission: this.permission

View File

@ -70,6 +70,10 @@ export default class MiOS extends EventEmitter {
chachedAt: Date; chachedAt: Date;
}; };
public get instanceName() {
return this.meta ? this.meta.data.name : 'Misskey';
}
private isMetaFetching = false; private isMetaFetching = false;
public app: Vue; public app: Vue;

View File

@ -38,7 +38,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import getNoteSummary from '../../../../../misc/get-note-summary'; import getNoteSummary from '../../../../../misc/get-note-summary';
import * as config from '../../../config';
const displayLimit = 30; const displayLimit = 30;
@ -190,7 +189,7 @@ export default Vue.extend({
clearNotification() { clearNotification() {
this.unreadCount = 0; this.unreadCount = 0;
document.title = config.name; document.title = (this as any).os.instanceName;
}, },
onVisibilitychange() { onVisibilitychange() {

View File

@ -8,7 +8,7 @@
<button class="nav" @click="$parent.isDrawerOpening = true">%fa:bars%</button> <button class="nav" @click="$parent.isDrawerOpening = true">%fa:bars%</button>
<template v-if="hasUnreadNotification || hasUnreadMessagingMessage || hasGameInvitation">%fa:circle%</template> <template v-if="hasUnreadNotification || hasUnreadMessagingMessage || hasGameInvitation">%fa:circle%</template>
<h1> <h1>
<slot>config.name</slot> <slot>{{ os.instanceName }}</slot>
</h1> </h1>
<slot name="func"></slot> <slot name="func"></slot>
</div> </div>
@ -20,13 +20,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as anime from 'animejs'; import * as anime from 'animejs';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
props: ['func'], props: ['func'],
data() { data() {
return { return {
config,
hasGameInvitation: false, hasGameInvitation: false,
connection: null, connection: null,
connectionId: null connectionId: null

View File

@ -41,7 +41,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { docsUrl, lang } from '../../../config'; import { lang } from '../../../config';
export default Vue.extend({ export default Vue.extend({
props: ['isOpen'], props: ['isOpen'],
@ -50,7 +50,7 @@ export default Vue.extend({
hasGameInvitation: false, hasGameInvitation: false,
connection: null, connection: null,
connectionId: null, connectionId: null,
aboutUrl: `${docsUrl}/${lang}/about` aboutUrl: `/docs/${lang}/about`
}; };
}, },
computed: { computed: {

View File

@ -25,7 +25,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -44,7 +43,7 @@ export default Vue.extend({
window.addEventListener('popstate', this.onPopState); window.addEventListener('popstate', this.onPopState);
}, },
mounted() { mounted() {
document.title = `${config.name} Drive`; document.title = `${(this as any).os.instanceName} Drive`;
document.documentElement.style.background = '#fff'; document.documentElement.style.background = '#fff';
}, },
beforeDestroy() { beforeDestroy() {
@ -64,7 +63,7 @@ export default Vue.extend({
(this.$refs as any).browser.openContextMenu(); (this.$refs as any).browser.openContextMenu();
}, },
onMoveRoot(silent) { onMoveRoot(silent) {
const title = `${config.name} Drive`; const title = `${(this as any).os.instanceName} Drive`;
if (!silent) { if (!silent) {
// Rewrite URL // Rewrite URL
@ -77,7 +76,7 @@ export default Vue.extend({
this.folder = null; this.folder = null;
}, },
onOpenFolder(folder, silent) { onOpenFolder(folder, silent) {
const title = `${folder.name} | ${config.name} Drive`; const title = `${folder.name} | ${(this as any).os.instanceName} Drive`;
if (!silent) { if (!silent) {
// Rewrite URL // Rewrite URL
@ -90,7 +89,7 @@ export default Vue.extend({
this.folder = folder; this.folder = folder;
}, },
onOpenFile(file, silent) { onOpenFile(file, silent) {
const title = `${file.name} | ${config.name} Drive`; const title = `${file.name} | ${(this as any).os.instanceName} Drive`;
if (!silent) { if (!silent) {
// Rewrite URL // Rewrite URL

View File

@ -14,7 +14,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -29,7 +28,7 @@ export default Vue.extend({
this.fetch(); this.fetch();
}, },
mounted() { mounted() {
document.title = `${config.name} | %i18n:@notifications%`; document.title = `${(this as any).os.instanceName} | %i18n:@notifications%`;
}, },
methods: { methods: {
fetch() { fetch() {

View File

@ -21,7 +21,6 @@ import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import getUserName from '../../../../../misc/get-user-name'; import getUserName from '../../../../../misc/get-user-name';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -50,7 +49,7 @@ export default Vue.extend({
this.user = user; this.user = user;
this.fetching = false; this.fetching = false;
document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + config.name; document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + (this as any).os.instanceName;
}); });
}, },
onLoaded() { onLoaded() {

View File

@ -20,7 +20,6 @@
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -49,7 +48,7 @@ export default Vue.extend({
this.user = user; this.user = user;
this.fetching = false; this.fetching = false;
document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + config.name; document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | ' + (this as any).os.instanceName;
}); });
}, },
onLoaded() { onLoaded() {

View File

@ -7,11 +7,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as config from '../../../../config';
export default Vue.extend({ export default Vue.extend({
mounted() { mounted() {
document.title = `${config.name} %i18n:@reversi%`; document.title = `${(this as any).os.instanceName} %i18n:@reversi%`;
document.documentElement.style.background = '#fff'; document.documentElement.style.background = '#fff';
}, },
methods: { methods: {

View File

@ -49,7 +49,6 @@
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import XTl from './home.timeline.vue'; import XTl from './home.timeline.vue';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
components: { components: {
@ -97,7 +96,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
document.title = config.name; document.title = (this as any).os.instanceName;
Progress.start(); Progress.start();

View File

@ -11,7 +11,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -48,7 +47,7 @@ export default Vue.extend({
this.user = user; this.user = user;
this.fetching = false; this.fetching = false;
document.title = `%i18n:@messaging%: ${Vue.filter('userName')(this.user)} | ${config.name}`; document.title = `%i18n:@messaging%: ${Vue.filter('userName')(this.user)} | ${(this as any).os.instanceName}`;
}); });
} }
} }

View File

@ -8,11 +8,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import getAcct from '../../../../../misc/acct/render'; import getAcct from '../../../../../misc/acct/render';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
mounted() { mounted() {
document.title = `${config.name} %i18n:@messaging%`; document.title = `${(this as any).os.instanceName} %i18n:@messaging%`;
}, },
methods: { methods: {
navigate(user) { navigate(user) {

View File

@ -16,7 +16,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -32,7 +31,7 @@ export default Vue.extend({
this.fetch(); this.fetch();
}, },
mounted() { mounted() {
document.title = config.name; document.title = (this as any).os.instanceName;
}, },
methods: { methods: {
fetch() { fetch() {

View File

@ -12,7 +12,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import * as config from '../../../config';
const limit = 20; const limit = 20;
@ -35,7 +34,7 @@ export default Vue.extend({
} }
}, },
mounted() { mounted() {
document.title = `%i18n:@search%: ${this.q} | ${config.name}`; document.title = `%i18n:@search%: ${this.q} | ${(this as any).os.instanceName}`;
this.fetch(); this.fetch();
}, },

View File

@ -13,8 +13,9 @@
<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch> <ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch> <ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
<ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch> <ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch>
<ui-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
<ui-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch> <ui-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
<ui-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
<ui-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
<div> <div>
<div>%i18n:@timeline%</div> <div>%i18n:@timeline%</div>
@ -189,7 +190,14 @@ export default Vue.extend({
onChangeReversiBoardLabels(v) { onChangeReversiBoardLabels(v) {
this.$store.dispatch('settings/set', { this.$store.dispatch('settings/set', {
key: 'reversiBoardLabels', key: 'games.reversi.showBoardLabels',
value: v
});
},
onChangeUseContrastReversiStones(v) {
this.$store.dispatch('settings/set', {
key: 'games.reversi.useContrastStones',
value: v value: v
}); });
}, },

View File

@ -91,15 +91,15 @@ export default Vue.extend({
method: 'POST', method: 'POST',
body: data body: data
}) })
.then(response => response.json()) .then(response => response.json())
.then(f => { .then(f => {
this.avatarId = f.id; this.avatarId = f.id;
this.avatarUploading = false; this.avatarUploading = false;
}) })
.catch(e => { .catch(e => {
this.avatarUploading = false; this.avatarUploading = false;
alert('%18n:!@upload-failed%'); alert('%18n:@upload-failed%');
}); });
}, },
onBannerChange([file]) { onBannerChange([file]) {
@ -113,15 +113,15 @@ export default Vue.extend({
method: 'POST', method: 'POST',
body: data body: data
}) })
.then(response => response.json()) .then(response => response.json())
.then(f => { .then(f => {
this.bannerId = f.id; this.bannerId = f.id;
this.bannerUploading = false; this.bannerUploading = false;
}) })
.catch(e => { .catch(e => {
this.bannerUploading = false; this.bannerUploading = false;
alert('%18n:!@upload-failed%'); alert('%18n:@upload-failed%');
}); });
}, },
save() { save() {

View File

@ -12,12 +12,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
return { return {
name: config.name, name: (this as any).os.instanceName,
posted: false, posted: false,
text: new URLSearchParams(location.search).get('text') text: new URLSearchParams(location.search).get('text')
}; };

View File

@ -67,7 +67,6 @@ import * as age from 's-age';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
import XHome from './user/home.vue'; import XHome from './user/home.vue';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
components: { components: {
@ -107,7 +106,7 @@ export default Vue.extend({
this.fetching = false; this.fetching = false;
Progress.done(); Progress.done();
document.title = Vue.filter('userName')(this.user) + ' | ' + config.name; document.title = Vue.filter('userName')(this.user) + ' | ' + (this as any).os.instanceName;
}); });
} }
} }
@ -248,7 +247,7 @@ root(isDark)
top 47px top 47px
box-shadow 0 4px 4px isDark ? rgba(#000, 0.3) : rgba(#000, 0.07) box-shadow 0 4px 4px isDark ? rgba(#000, 0.3) : rgba(#000, 0.07)
background-color $bg background-color $bg
z-index 1 z-index 2
> .nav-container > .nav-container
display flex display flex

View File

@ -30,7 +30,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { apiUrl, copyright, host, name, description } from '../../../config'; import { apiUrl, copyright, host } from '../../../config';
export default Vue.extend({ export default Vue.extend({
data() { data() {
@ -39,12 +39,17 @@ export default Vue.extend({
copyright, copyright,
stats: null, stats: null,
host, host,
name, name: 'Misskey',
description, description: '',
tags: [] tags: []
}; };
}, },
created() { created() {
(this as any).os.getMeta().then(meta => {
this.name = meta.name;
this.description = meta.description;
});
(this as any).api('stats').then(stats => { (this as any).api('stats').then(stats => {
this.stats = stats; this.stats = stats;
}); });

View File

@ -53,7 +53,6 @@
import Vue from 'vue'; import Vue from 'vue';
import * as XDraggable from 'vuedraggable'; import * as XDraggable from 'vuedraggable';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import * as config from '../../../config';
export default Vue.extend({ export default Vue.extend({
components: { components: {
@ -103,7 +102,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
document.title = config.name; document.title = (this as any).os.instanceName;
}, },
methods: { methods: {

View File

@ -1,5 +1,6 @@
import Vuex from 'vuex'; import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate'; import createPersistedState from 'vuex-persistedstate';
import * as nestedProperty from 'nested-property';
import MiOS from './mios'; import MiOS from './mios';
import { hostname } from './config'; import { hostname } from './config';
@ -22,7 +23,12 @@ const defaultSettings = {
disableViaMobile: false, disableViaMobile: false,
memo: null, memo: null,
iLikeSushi: false, iLikeSushi: false,
reversiBoardLabels: false games: {
reversi: {
showBoardLabels: false,
useContrastStones: false
}
}
}; };
const defaultDeviceSettings = { const defaultDeviceSettings = {
@ -125,7 +131,7 @@ export default (os: MiOS) => new Vuex.Store({
mutations: { mutations: {
set(state, x: { key: string; value: any }) { set(state, x: { key: string; value: any }) {
state[x.key] = x.value; nestedProperty.set(state, x.key, x.value);
}, },
setHome(state, data) { setHome(state, data) {

View File

@ -82,7 +82,7 @@ props:
ja: "フォルダ" ja: "フォルダ"
en: "The folder of this file" en: "The folder of this file"
sensitive: isSensitive:
type: "boolean" type: "boolean"
optional: true optional: true
desc: desc:

View File

@ -14,6 +14,7 @@ import * as portscanner from 'portscanner';
import isRoot = require('is-root'); import isRoot = require('is-root');
import Xev from 'xev'; import Xev from 'xev';
import * as program from 'commander'; import * as program from 'commander';
import mongo from './db/mongodb';
import Logger from './misc/logger'; import Logger from './misc/logger';
import ProgressBar from './misc/cli/progressbar'; import ProgressBar from './misc/cli/progressbar';
@ -158,8 +159,13 @@ function checkMongoDb(config: Config) {
const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null; const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null;
const uri = `mongodb://${u && p ? `${u}:****@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`; const uri = `mongodb://${u && p ? `${u}:****@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
mongoDBLogger.info(`Connecting to ${uri}`); mongoDBLogger.info(`Connecting to ${uri}`);
require('./db/mongodb');
mongoDBLogger.succ('Connectivity confirmed'); mongo.then(() => {
mongoDBLogger.succ('Connectivity confirmed');
})
.catch(err => {
mongoDBLogger.error(err.message);
});
} }
function spawnWorkers(limit: number) { function spawnWorkers(limit: number) {

View File

@ -5,8 +5,6 @@ import db from '../db/mongodb';
import config from '../config'; import config from '../config';
const App = db.get<IApp>('apps'); const App = db.get<IApp>('apps');
App.createIndex('nameId');
App.createIndex('nameIdLower');
App.createIndex('secret'); App.createIndex('secret');
export default App; export default App;
@ -16,17 +14,11 @@ export type IApp = {
userId: mongo.ObjectID | null; userId: mongo.ObjectID | null;
secret: string; secret: string;
name: string; name: string;
nameId: string;
nameIdLower: string;
description: string; description: string;
permission: string[]; permission: string[];
callbackUrl: string; callbackUrl: string;
}; };
export function isValidNameId(nameId: string): boolean {
return typeof nameId == 'string' && /^[a-zA-Z0-9_]{1,30}$/.test(nameId);
}
/** /**
* Pack an app for API response * Pack an app for API response
* *
@ -76,8 +68,6 @@ export const pack = (
_app.id = _app._id; _app.id = _app._id;
delete _app._id; delete _app._id;
delete _app.nameIdLower;
// Visible by only owner // Visible by only owner
if (!opts.includeSecret) { if (!opts.includeSecret) {
delete _app.secret; delete _app.secret;

View File

@ -118,6 +118,7 @@ export interface IRemoteUser extends IUserBase {
publicKeyPem: string; publicKeyPem: string;
}; };
updatedAt: Date; updatedAt: Date;
isAdmin: false;
} }
export type IUser = ILocalUser | IRemoteUser; export type IUser = ILocalUser | IRemoteUser;

View File

@ -81,7 +81,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
// 添付メディア // 添付メディア
// TODO: attachmentは必ずしもImageではない // TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない // TODO: attachmentは必ずしも配列ではない
// Noteがsensitiveなら添付もsensitiveにする
const media = note.attachment const media = note.attachment
.map(attach => attach.sensitive = note.sensitive)
? await Promise.all(note.attachment.map(x => resolveImage(actor, x))) ? await Promise.all(note.attachment.map(x => resolveImage(actor, x)))
: []; : [];

View File

@ -131,7 +131,8 @@ export async function createPerson(value: any, resolver?: Resolver): Promise<IUs
endpoints: person.endpoints, endpoints: person.endpoints,
uri: person.id, uri: person.id,
url: person.url, url: person.url,
isBot isBot: isBot,
isCat: (person as any).isCat === true ? true : false
}) as IRemoteUser; }) as IRemoteUser;
} catch (e) { } catch (e) {
// duplicate key error // duplicate key error
@ -262,7 +263,8 @@ export async function updatePerson(value: string | IObject, resolver?: Resolver)
notesCount, notesCount,
name: person.name, name: person.name,
url: person.url, url: person.url,
endpoints: person.endpoints endpoints: person.endpoints,
isCat: (person as any).isCat === true ? true : false
} }
}); });
} }

View File

@ -79,6 +79,8 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
...mentionTags, ...mentionTags,
]; ];
const files = await promisedFiles;
return { return {
id: `${config.url}/notes/${note._id}`, id: `${config.url}/notes/${note._id}`,
type: 'Note', type: 'Note',
@ -89,7 +91,8 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
to, to,
cc, cc,
inReplyTo, inReplyTo,
attachment: (await promisedFiles).map(renderDocument), attachment: files.map(renderDocument),
sensitive: files.some(file => file.metadata.isSensitive),
tag tag
}; };
} }

View File

@ -29,6 +29,7 @@ export default async (user: ILocalUser) => {
icon: user.avatarId && renderImage(avatar), icon: user.avatarId && renderImage(avatar),
image: user.bannerId && renderImage(banner), image: user.bannerId && renderImage(banner),
manuallyApprovesFollowers: user.isLocked, manuallyApprovesFollowers: user.isLocked,
publicKey: renderKey(user) publicKey: renderKey(user),
isCat: user.isCat
}; };
}; };

View File

@ -16,6 +16,7 @@ export interface IObject {
image?: any; image?: any;
url?: string; url?: string;
tag?: any[]; tag?: any[];
sensitive?: boolean;
} }
export interface IActivity extends IObject { export interface IActivity extends IObject {

View File

@ -41,10 +41,20 @@ function inbox(ctx: Router.IRouterContext) {
} }
function isActivityPubReq(ctx: Router.IRouterContext) { function isActivityPubReq(ctx: Router.IRouterContext) {
ctx.response.vary('Accept');
const accepted = ctx.accepts('html', 'application/activity+json', 'application/ld+json'); const accepted = ctx.accepts('html', 'application/activity+json', 'application/ld+json');
return ['application/activity+json', 'application/ld+json'].includes(accepted as string); return ['application/activity+json', 'application/ld+json'].includes(accepted as string);
} }
export function setResponseType(ctx: Router.IRouterContext) {
const accpet = ctx.accepts('application/activity+json', 'application/ld+json');
if (accpet === 'application/ld+json') {
ctx.response.type = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
} else {
ctx.response.type = 'application/activity+json; charset=utf-8';
}
}
// inbox // inbox
router.post('/inbox', json(), inbox); router.post('/inbox', json(), inbox);
router.post('/users/:user/inbox', json(), inbox); router.post('/users/:user/inbox', json(), inbox);
@ -54,7 +64,8 @@ router.get('/notes/:note', async (ctx, next) => {
if (!isActivityPubReq(ctx)) return await next(); if (!isActivityPubReq(ctx)) return await next();
const note = await Note.findOne({ const note = await Note.findOne({
_id: new mongo.ObjectID(ctx.params.note) _id: new mongo.ObjectID(ctx.params.note),
$or: [ { visibility: 'public' }, { visibility: 'home' } ]
}); });
if (note === null) { if (note === null) {
@ -62,7 +73,8 @@ router.get('/notes/:note', async (ctx, next) => {
return; return;
} }
ctx.body = pack(await renderNote(note)); ctx.body = pack(await renderNote(note, false));
setResponseType(ctx);
}); });
// outbox // outbox
@ -90,6 +102,7 @@ router.get('/users/:user/publickey', async ctx => {
if (isLocalUser(user)) { if (isLocalUser(user)) {
ctx.body = pack(renderKey(user)); ctx.body = pack(renderKey(user));
setResponseType(ctx);
} else { } else {
ctx.status = 400; ctx.status = 400;
} }
@ -103,6 +116,7 @@ async function userInfo(ctx: Router.IRouterContext, user: IUser) {
} }
ctx.body = pack(await renderPerson(user as ILocalUser)); ctx.body = pack(await renderPerson(user as ILocalUser));
setResponseType(ctx);
} }
router.get('/users/:user', async ctx => { router.get('/users/:user', async ctx => {

View File

@ -1,5 +1,5 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import * as Koa from 'koa'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID from '../../misc/cafy-id'; import $ from 'cafy'; import ID from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
@ -8,8 +8,9 @@ import pack from '../../remote/activitypub/renderer';
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection'; import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page'; import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
import renderFollowUser from '../../remote/activitypub/renderer/follow-user'; import renderFollowUser from '../../remote/activitypub/renderer/follow-user';
import { setResponseType } from '../activitypub';
export default async (ctx: Koa.Context) => { export default async (ctx: Router.IRouterContext) => {
const userId = new mongo.ObjectID(ctx.params.user); const userId = new mongo.ObjectID(ctx.params.user);
// Get 'cursor' parameter // Get 'cursor' parameter
@ -72,9 +73,11 @@ export default async (ctx: Koa.Context) => {
); );
ctx.body = pack(rendered); ctx.body = pack(rendered);
setResponseType(ctx);
} else { } else {
// index page // index page
const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`, null); const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`, null);
ctx.body = pack(rendered); ctx.body = pack(rendered);
setResponseType(ctx);
} }
}; };

View File

@ -1,5 +1,5 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import * as Koa from 'koa'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID from '../../misc/cafy-id'; import $ from 'cafy'; import ID from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
@ -8,8 +8,9 @@ import pack from '../../remote/activitypub/renderer';
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection'; import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page'; import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
import renderFollowUser from '../../remote/activitypub/renderer/follow-user'; import renderFollowUser from '../../remote/activitypub/renderer/follow-user';
import { setResponseType } from '../activitypub';
export default async (ctx: Koa.Context) => { export default async (ctx: Router.IRouterContext) => {
const userId = new mongo.ObjectID(ctx.params.user); const userId = new mongo.ObjectID(ctx.params.user);
// Get 'cursor' parameter // Get 'cursor' parameter
@ -72,9 +73,11 @@ export default async (ctx: Koa.Context) => {
); );
ctx.body = pack(rendered); ctx.body = pack(rendered);
setResponseType(ctx);
} else { } else {
// index page // index page
const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`, null); const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`, null);
ctx.body = pack(rendered); ctx.body = pack(rendered);
setResponseType(ctx);
} }
}; };

View File

@ -1,16 +1,17 @@
import * as mongo from 'mongodb'; import * as mongo from 'mongodb';
import * as Koa from 'koa'; import * as Router from 'koa-router';
import config from '../../config'; import config from '../../config';
import $ from 'cafy'; import ID from '../../misc/cafy-id'; import $ from 'cafy'; import ID from '../../misc/cafy-id';
import User from '../../models/user'; import User from '../../models/user';
import pack from '../../remote/activitypub/renderer'; import pack from '../../remote/activitypub/renderer';
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection'; import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page'; import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
import { setResponseType } from '../activitypub';
import Note from '../../models/note'; import Note from '../../models/note';
import renderNote from '../../remote/activitypub/renderer/note'; import renderNote from '../../remote/activitypub/renderer/note';
export default async (ctx: Koa.Context) => { export default async (ctx: Router.IRouterContext) => {
const userId = new mongo.ObjectID(ctx.params.user); const userId = new mongo.ObjectID(ctx.params.user);
// Get 'sinceId' parameter // Get 'sinceId' parameter
@ -83,7 +84,7 @@ export default async (ctx: Koa.Context) => {
if (sinceId) notes.reverse(); if (sinceId) notes.reverse();
const renderedNotes = await Promise.all(notes.map(note => renderNote(note))); const renderedNotes = await Promise.all(notes.map(note => renderNote(note, false)));
const rendered = renderOrderedCollectionPage( const rendered = renderOrderedCollectionPage(
`${partOf}?page=true${sinceId ? `&since_id=${sinceId}` : ''}${untilId ? `&until_id=${untilId}` : ''}`, `${partOf}?page=true${sinceId ? `&since_id=${sinceId}` : ''}${untilId ? `&until_id=${untilId}` : ''}`,
user.notesCount, renderedNotes, partOf, user.notesCount, renderedNotes, partOf,
@ -92,6 +93,7 @@ export default async (ctx: Koa.Context) => {
); );
ctx.body = pack(rendered); ctx.body = pack(rendered);
setResponseType(ctx);
} else { } else {
// index page // index page
const rendered = renderOrderedCollection(partOf, user.notesCount, const rendered = renderOrderedCollection(partOf, user.notesCount,
@ -99,5 +101,6 @@ export default async (ctx: Koa.Context) => {
`${partOf}?page=true&since_id=000000000000000000000000` `${partOf}?page=true&since_id=000000000000000000000000`
); );
ctx.body = pack(rendered); ctx.body = pack(rendered);
setResponseType(ctx);
} }
}; };

View File

@ -1,6 +1,6 @@
import { performance } from 'perf_hooks'; import { performance } from 'perf_hooks';
import limitter from './limitter'; import limitter from './limitter';
import { IUser, isLocalUser } from '../../models/user'; import { IUser } from '../../models/user';
import { IApp } from '../../models/app'; import { IApp } from '../../models/app';
import endpoints from './endpoints'; import endpoints from './endpoints';
@ -21,7 +21,7 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
return rej('YOUR_ACCOUNT_HAS_BEEN_SUSPENDED'); return rej('YOUR_ACCOUNT_HAS_BEEN_SUSPENDED');
} }
if (ep.meta.requireAdmin && !(isLocalUser(user) && user.isAdmin)) { if (ep.meta.requireAdmin && !user.isAdmin) {
return rej('YOU_ARE_NOT_ADMIN'); return rej('YOU_ARE_NOT_ADMIN');
} }

View File

@ -34,6 +34,10 @@ export default (params: any) => new Promise(async (res, rej) => {
return rej('user not found'); return rej('user not found');
} }
if (user.isAdmin) {
return rej('cannot suspend admin');
}
await User.findOneAndUpdate({ await User.findOneAndUpdate({
_id: user._id _id: user._id
}, { }, {

View File

@ -1,6 +1,6 @@
import rndstr from 'rndstr'; import rndstr from 'rndstr';
import $ from 'cafy'; import $ from 'cafy';
import App, { isValidNameId, pack } from '../../../../models/app'; import App, { pack } from '../../../../models/app';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
export const meta = { export const meta = {
@ -11,10 +11,6 @@ export const meta = {
* Create an app * Create an app
*/ */
export default async (params: any, user: ILocalUser) => new Promise(async (res, rej) => { export default async (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
// Get 'nameId' parameter
const [nameId, nameIdErr] = $.str.pipe(isValidNameId).get(params.nameId);
if (nameIdErr) return rej('invalid nameId param');
// Get 'name' parameter // Get 'name' parameter
const [name, nameErr] = $.str.get(params.name); const [name, nameErr] = $.str.get(params.name);
if (nameErr) return rej('invalid name param'); if (nameErr) return rej('invalid name param');
@ -40,8 +36,6 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
createdAt: new Date(), createdAt: new Date(),
userId: user && user._id, userId: user && user._id,
name: name, name: name,
nameId: nameId,
nameIdLower: nameId.toLowerCase(),
description: description, description: description,
permission: permission, permission: permission,
callbackUrl: callbackUrl, callbackUrl: callbackUrl,
@ -49,5 +43,7 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
}); });
// Response // Response
res(await pack(app)); res(await pack(app, null, {
includeSecret: true
}));
}); });

View File

@ -1,31 +0,0 @@
/**
* Module dependencies
*/
import $ from 'cafy';
import App from '../../../../../models/app';
import { isValidNameId } from '../../../../../models/app';
/**
* Check available nameId of app
*
* @param {any} params
* @return {Promise<any>}
*/
export default async (params: any) => new Promise(async (res, rej) => {
// Get 'nameId' parameter
const [nameId, nameIdErr] = $.str.pipe(isValidNameId).get(params.nameId);
if (nameIdErr) return rej('invalid nameId param');
// Get exist
const exist = await App
.count({
nameIdLower: nameId.toLowerCase()
}, {
limit: 1
});
// Reply
res({
available: exist === 0
});
});

View File

@ -9,21 +9,11 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
const isSecure = user != null && app == null; const isSecure = user != null && app == null;
// Get 'appId' parameter // Get 'appId' parameter
const [appId, appIdErr] = $.type(ID).optional.get(params.appId); const [appId, appIdErr] = $.type(ID).get(params.appId);
if (appIdErr) return rej('invalid appId param'); if (appIdErr) return rej('invalid appId param');
// Get 'nameId' parameter
const [nameId, nameIdErr] = $.str.optional.get(params.nameId);
if (nameIdErr) return rej('invalid nameId param');
if (appId === undefined && nameId === undefined) {
return rej('appId or nameId is required');
}
// Lookup app // Lookup app
const ap = appId !== undefined const ap = await App.findOne({ _id: appId });
? await App.findOne({ _id: appId })
: await App.findOne({ nameIdLower: nameId.toLowerCase() });
if (ap === null) { if (ap === null) {
return rej('app not found'); return rej('app not found');

View File

@ -34,9 +34,12 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
const sort = { const sort = {
_id: -1 _id: -1
}; };
const query = { const query = {
'metadata.userId': user._id 'metadata.userId': user._id,
'metadata.deletedAt': { $exists: false }
} as any; } as any;
if (sinceId) { if (sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
@ -47,6 +50,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
$lt: untilId $lt: untilId
}; };
} }
if (type) { if (type) {
query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`); query.contentType = new RegExp(`^${type.replace(/\*/g, '.+?')}$`);
} }
@ -59,6 +63,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
}); });
// Serialize // Serialize
res(await Promise.all(files.map(async file => res(await Promise.all(files.map(file => pack(file))));
await pack(file))));
}); });

View File

@ -20,6 +20,9 @@ export default () => new Promise(async (res, rej) => {
version: pkg.version, version: pkg.version,
clientVersion: client.version, clientVersion: client.version,
name: config.name || 'Misskey',
description: config.description,
secure: config.https != null, secure: config.https != null,
machine: os.hostname(), machine: os.hostname(),
os: os.platform(), os: os.platform(),
@ -29,6 +32,8 @@ export default () => new Promise(async (res, rej) => {
cores: os.cpus().length cores: os.cpus().length
}, },
broadcasts: meta.broadcasts, broadcasts: meta.broadcasts,
disableRegistration: meta.disableRegistration disableRegistration: meta.disableRegistration,
recaptchaSitekey: config.recaptcha ? config.recaptcha.site_key : null,
swPublickey: config.sw ? config.sw.public_key : null
}); });
}); });

View File

@ -12,8 +12,9 @@ export default async (ctx: Koa.Context) => {
ctx.set('Access-Control-Allow-Credentials', 'true'); ctx.set('Access-Control-Allow-Credentials', 'true');
const body = ctx.request.body as any; const body = ctx.request.body as any;
const username = body['username']; // See: https://github.com/syuilo/misskey/issues/2384
const password = body['password']; const username = body['username'] || body['x'];
const password = body['password'] || body['y'];
const token = body['token']; const token = body['token'];
if (typeof username != 'string') { if (typeof username != 'string') {
@ -63,7 +64,7 @@ export default async (ctx: Koa.Context) => {
if (verified) { if (verified) {
signin(ctx, user); signin(ctx, user);
} else { } else {
ctx.throw(400, { ctx.throw(403, {
error: 'invalid token' error: 'invalid token'
}); });
} }
@ -71,7 +72,7 @@ export default async (ctx: Koa.Context) => {
signin(ctx, user); signin(ctx, user);
} }
} else { } else {
ctx.throw(400, { ctx.throw(403, {
error: 'incorrect password' error: 'incorrect password'
}); });
} }

View File

@ -11,13 +11,13 @@ export default async function(
subscriber: Xev, subscriber: Xev,
user: IUser user: IUser
) { ) {
const mute = await Mute.find({ muterId: user._id });
const mutedUserIds = mute.map(m => m.muteeId.toString());
// Subscribe stream // Subscribe stream
subscriber.on('hybrid-timeline', onEvent); subscriber.on('hybrid-timeline', onEvent);
subscriber.on(`hybrid-timeline:${user._id}`, onEvent); subscriber.on(`hybrid-timeline:${user._id}`, onEvent);
const mute = await Mute.find({ muterId: user._id });
const mutedUserIds = mute.map(m => m.muteeId.toString());
async function onEvent(note: any) { async function onEvent(note: any) {
//#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する //#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (mutedUserIds.indexOf(note.userId) != -1) { if (mutedUserIds.indexOf(note.userId) != -1) {

View File

@ -16,8 +16,6 @@ import I18nReplacer from './src/misc/i18n';
import { pattern as i18nPattern, replacement as i18nReplacement } from './webpack/i18n'; import { pattern as i18nPattern, replacement as i18nReplacement } from './webpack/i18n';
import { pattern as faPattern, replacement as faReplacement } from './src/misc/fa'; import { pattern as faPattern, replacement as faReplacement } from './src/misc/fa';
const constants = require('./src/const.json'); const constants = require('./src/const.json');
import config from './src/config';
import { licenseHtml } from './src/misc/license';
const locales = require('./locales'); const locales = require('./locales');
const meta = require('./package.json'); const meta = require('./package.json');
@ -58,8 +56,6 @@ const isProduction = process.env.NODE_ENV == 'production';
const entry = { const entry = {
desktop: './src/client/app/desktop/script.ts', desktop: './src/client/app/desktop/script.ts',
mobile: './src/client/app/mobile/script.ts', mobile: './src/client/app/mobile/script.ts',
//stats: './src/client/app/stats/script.ts',
//status: './src/client/app/status/script.ts',
dev: './src/client/app/dev/script.ts', dev: './src/client/app/dev/script.ts',
auth: './src/client/app/auth/script.ts', auth: './src/client/app/auth/script.ts',
sw: './src/client/app/sw.js' sw: './src/client/app/sw.js'
@ -72,31 +68,12 @@ const output = {
//#region Define consts //#region Define consts
const consts = { const consts = {
_RECAPTCHA_SITEKEY_: config.recaptcha ? config.recaptcha.site_key : null,
_SW_PUBLICKEY_: config.sw ? config.sw.public_key : null,
_THEME_COLOR_: constants.themeColor, _THEME_COLOR_: constants.themeColor,
_COPYRIGHT_: constants.copyright, _COPYRIGHT_: constants.copyright,
_VERSION_: version, _VERSION_: version,
_CODENAME_: codename, _CODENAME_: codename,
_STATUS_URL_: config.status_url,
_STATS_URL_: config.stats_url,
_DOCS_URL_: config.docs_url,
_API_URL_: config.api_url,
_WS_URL_: config.ws_url,
_DEV_URL_: config.dev_url,
_REPOSITORY_URL_: config.maintainer.repository_url,
_FEEDBACK_URL_: config.maintainer.feedback_url,
_LANG_: '%lang%', _LANG_: '%lang%',
_LANGS_: Object.keys(locales).map(l => [l, locales[l].meta.lang]), _LANGS_: Object.keys(locales).map(l => [l, locales[l].meta.lang])
_NAME_: config.name,
_DESCRIPTION_: config.description,
_HOST_: config.host,
_HOSTNAME_: config.hostname,
_URL_: config.url,
_LICENSE_: licenseHtml,
_GOOGLE_MAPS_API_KEY_: config.google_maps_api_key,
_WELCOME_BG_URL_: config.welcome_bg_url,
_TWITTER_INTEGRATION_: config.twitter != null
}; };
const _consts: { [ key: string ]: any } = {}; const _consts: { [ key: string ]: any } = {};