Compare commits

...

53 Commits

Author SHA1 Message Date
syuilo
baf94f86c4 11.31.3 2019-09-03 07:28:45 +09:00
syuilo
d36b129369 Improve reaction tooltip 2019-09-03 07:25:02 +09:00
syuilo
f36d88246a Fetch more reactins 2019-09-03 07:15:53 +09:00
syuilo
03f87140b3 11.31.2 2019-09-03 06:58:19 +09:00
syuilo
1dc07f6b72 🎨 2019-09-03 06:58:01 +09:00
syuilo
0aa0a9d24b 11.31.1 2019-09-03 06:22:05 +09:00
syuilo
a9a93db2b4 Update reactions-viewer.reaction.vue 2019-09-03 06:20:52 +09:00
syuilo
f187df3933 Update reactions-viewer.reaction.vue 2019-09-03 06:20:04 +09:00
syuilo
8abe8042d7 Fix bug 2019-09-03 06:19:17 +09:00
syuilo
58fd46ff6f 🎨 2019-09-03 06:00:45 +09:00
syuilo
fef8b662c1 🎨 2019-09-03 05:50:01 +09:00
syuilo
8de2f4ce76 Update node to 12.9.1 2019-09-03 05:43:44 +09:00
Acid Chicken (硫酸鶏)
e5e344e1cd Update README.md [AUTOGEN] (#5382) 2019-09-03 01:02:57 +09:00
Aya Morisawa
e70d7edf41 Fix #5380 (#5381) 2019-09-02 07:01:33 +09:00
syuilo
71d4d51fb2 11.31.0 2019-09-02 06:33:07 +09:00
syuilo
aaf38f1cbe Update CHANGELOG.md 2019-09-02 06:23:10 +09:00
syuilo
0e0d6692c0 Fix bug 2019-09-02 06:21:43 +09:00
syuilo
29f927fe72 Update CHANGELOG.md 2019-09-02 06:21:06 +09:00
syuilo
ee39d9594e Improve readability 2019-09-02 06:04:12 +09:00
Oni-Men
cefd2a4c54 ページURLが空の時currentNameを使うように (#5368)
* ページURLが空の時currentNameを使う

* 空の時はページURLにcurrentNameを代入するように

* Update src/client/app/common/views/pages/page-editor/page-editor.vue

Co-Authored-By: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* update validator

* Update src/client/app/common/views/pages/page-editor/page-editor.vue

Co-Authored-By: Acid Chicken (硫酸鶏) <root@acid-chicken.com>

* やっぱりuuidは統一

* エラー処理を追加

* some fix

* ちょっとだけ翻訳の追加と改善

* リファクタリング

* Revert "やっぱりuuidは統一"

This reverts commit 965a860504.

* やっぱりuuidをわける

* エラー判定をidからcodeに+リファクタリング
2019-09-02 06:02:35 +09:00
syuilo
a08c20d9af Fix #5353 2019-09-02 05:59:24 +09:00
syuilo
dc11f1afbf Improve readavility 2019-09-02 05:52:38 +09:00
syuilo
b0f2b209a2 Fix error 2019-09-02 05:42:30 +09:00
syuilo
a25fdfd519 Fix #5373 2019-09-02 05:34:25 +09:00
syuilo
c1aa58596d Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-09-02 05:20:17 +09:00
syuilo
b6a3eb2445 Fix #5379 2019-09-02 05:20:06 +09:00
syuilo
310f4d2edb New Crowdin translations (#5337)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Spanish)

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

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

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (Chinese Simplified)
2019-09-02 05:04:24 +09:00
Aya Morisawa
701fee3139 Prevent users from opening note menu when already opened (#5367) 2019-09-02 04:45:01 +09:00
MeiMei
593c2b9517 Proxy for SMTP (#5371) 2019-09-02 04:42:52 +09:00
syuilo
96b2267cb8 Chart resyncing (#5372)
* wip

* Add test

* Fix test

* Insert moderation log

* Add todo
2019-09-02 04:41:26 +09:00
Acid Chicken (硫酸鶏)
84730a071a Update README.md [AUTOGEN] (#5377) 2019-09-02 04:06:54 +09:00
syuilo
d0b0cf8dfb Use npx 2019-09-01 04:31:59 +09:00
Aya Morisawa
749200d22b Show users who sent reaction on hover (#5362)
* Show users who sent reaction on hover

* Support i18n

* detail -> details

* Extract methods

* Update on change
2019-08-31 03:04:36 +09:00
syuilo
a47baad943 Update dependencies 🚀 2019-08-31 03:01:50 +09:00
syuilo
50abb51ece Syslog support
Resolve #5355
2019-08-30 08:29:46 +09:00
Aya Morisawa
1f890c5bed Assign URL to each page of admin (#5366)
* Assign URL to each page of admin

* Remove cursor pointer
2019-08-30 04:19:49 +09:00
Aya Morisawa
97f23af86d Assign URL to each page of settings (#5349)
* Assign URL to each page of settings

* Use router-link

* comma

* Use active-class

* Clean up

* space

* comma

* Redirect if mobile

* Redirect to /i/settings/profile

* Clean up
2019-08-29 14:57:28 +09:00
syuilo
d77aa1f26a 不要なプロパティをレスポンスから削除 2019-08-29 07:34:54 +09:00
Acid Chicken (硫酸鶏)
0b075ad4e9 Update README.md [AUTOGEN] (#5356) 2019-08-29 05:33:52 +09:00
Aya Morisawa
423f776ed0 Perform animation only when reaction is added (#5359) 2019-08-29 05:20:27 +09:00
Aya Morisawa
084fd8152b Refactor reactions-viewer.vue (#5358) 2019-08-29 05:15:14 +09:00
syuilo
89d35c2e63 Fix bug 2019-08-29 05:13:03 +09:00
Aya Morisawa
be33581642 Fix animation not being performed on new reaction (#5345)
* Fix animation not being performed on new reaction

* Clean up
2019-08-29 05:11:26 +09:00
Oni-Men
2d6d9f30e1 ページURLが他と重複してたらエラーを投げるように (#5354)
* [Page]nameが重複したときの処理を追加

* page-editor側のerr.idにuuidを適用

* refactor

* uuidをわけた
2019-08-28 08:00:05 +09:00
Aya Morisawa
85721065fd Save memo automatically (#5351)
* Save memo automatically

* Use clearTimeout

* Clean up

* Clean up
2019-08-28 02:26:19 +09:00
Aya Morisawa
9d65768d4d [MFM] Fallback to js if specified lang is not available (#5347) 2019-08-27 19:44:49 +09:00
MeiMei
13f69e4291 excludeNsfwやCWのNSFW扱いなど (#5341)
* NoteにisSensitive

* Revert "NoteにisSensitive"

This reverts commit 3d5bcfbaf0.

* query excludeNsfw

* AP deliverでCW付きはsensitiveにするように

* excludeNsfwでCW付きも除くように
2019-08-27 17:33:07 +09:00
MeiMei
6a0affcec1 Tune worker/job counts (#5346)
* デフォルトのワーカー数を1に

* Tune default job count
2019-08-27 05:33:24 +09:00
MeiMei
ab6a84cd45 未実装のTLのRenoteクエリを実装 (#5343)
* users/notes includeMyRenotes

* other renotes

* fix target user in users/notes

* users/notesからv10から未実装でありえないオプションを削除

* users/notesのincludeMyRenotesの説明を修正

* remove needless anonymous checks
2019-08-27 03:24:35 +09:00
Aya Morisawa
ba93bf7478 Prevent users from changing the disabled option (#5344) 2019-08-27 02:44:01 +09:00
Satsuki Yanagi
1c4e1af7c3 Improve post form (#5326)
* Improve post form

* Remove local icon from button
2019-08-25 16:12:01 +09:00
Satsuki Yanagi
a85f4c4fc4 Resolve #2716 (#5340)
* Resolve #2716

* Update ja-JP.yml
2019-08-25 16:11:20 +09:00
MeiMei
9d6c8806af CWの中のサムネイルのサイズが変なのを修正 (#5339)
* Fix #5338

* Revert "Fix #5338"

This reverts commit 72b32df2b7.

* Fix media-list height

* fix
2019-08-24 19:20:53 +09:00
72 changed files with 1198 additions and 768 deletions

View File

@@ -116,8 +116,25 @@ autoAdmin: true
# Whether disable HSTS
#disableHsts: true
# Clustering
# Number of worker processes
#clusterLimit: 1
# Job concurrency per worker
# deliverJobConcurrency: 128;
# inboxJobConcurrency: 16;
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
# Syslog option
#syslog:
# host: localhost
# port: 514
# Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128
# Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5

View File

@@ -1 +1 @@
v12.8.1
v12.9.1

View File

@@ -1,6 +1,57 @@
ChangeLog
=========
11.31.3 (2019/09/03)
--------------------
### 🐛Fixes
* 誰がリアクションしたか見れるやつの表示を改善
11.31.2 (2019/09/03)
--------------------
### 🐛Fixes
* 誰がリアクションしたか見れるやつの表示を改善
11.31.1 (2019/09/03)
--------------------
### 🐛Fixes
* 誰がリアクションしたか見れるやつの表示を改善
11.31.0 (2019/09/02)
--------------------
### ✨Improvements
* Syslogサポート
* チャートの同期機能をAPI経由で使えるように
* SMTPでProxyを使用できるように
* リアクションにホバーすることで誰がリアクションしたか見れるように
* リプライ時、返信元のlocalOnly属性を引き継ぐように
* 引用付き、ローカルのみなどの案内文にアイコン追加
* AP deliver/inbox job の並列度を変更できるように
* clusterLimitの既定値を1に
* AP inbox ジョブの並列度を下げる
* CWが付いた投稿はAP上でNote.sensitiveフラグを付けるように
* メモウィジェットの内容を自動で保存するように
* ページURLが他と重複してたらエラーを投げるように
* ページURLが空の時エラーを投げるように
* リアクションが解除されたときはアニメーションしないように
* 設定の各セクションごとにURLを割り当てるように
* 管理画面の各セクションごとにURLを割り当てるように
### 🐛Fixes
* 未実装のTLのRenoteクエリを実装
* タイムラインAPIのexcludeNsfwオプションを実装
* ユーザーページの投稿一覧の私の投稿にRenoteが表示される問題を修正
* meta APIでemojiプロパティに不要な情報が含まれているのを修正
* モバイル版でドライブのファイルを削除したときの挙動がおかしい問題を修正
* visiblity-chooserにlocalOnly属性が伝わらなかったのを修正
* 言語指定したときコードブロックが表示されない問題を修正
* トークのメッセージがはみ出す問題を修正
* CWの中のサムネイルのサイズが変なのを少し修正
* リアクションが初めて付いた時のエフェクトが消えている問題を修正
* 無効になっているスイッチを操作できる問題を修正
* Mキー連打で画面が真っ暗問題を修正
* AmazonのURLプレビューが出来ない問題を修正
* 表記ゆれを修正
11.30.0 (2019/08/24)
--------------------
### ✨Improvements

View File

@@ -1,4 +1,4 @@
FROM node:12.8-alpine AS base
FROM node:12.9.1-alpine AS base
ENV NODE_ENV=production

View File

@@ -104,7 +104,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<!-- PATREON_START -->
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20010324/b8af4bd31ae34fbf8806cc0e6228e400/1.png?token-time=2145916800&token-hash=iyiocfousNIUwASmatsIDq8EOsmLUdrQNkWyktHlmJg%3D" alt="Nemo" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
@@ -112,7 +111,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/user?u=20010324">Nemo</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
@@ -141,7 +139,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/619ab87cc08448439222631ebb26802f/1.gif?token-time=2145916800&token-hash=o27K7M02s1z-LkDUEO5Oa7cu-GviRXeOXxryi4o_6VU%3D" alt="Atsuko Tominaga" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
@@ -150,7 +147,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
<td><a href="https://www.patreon.com/noellabo">noellabo</a></td>
<td><a href="https://www.patreon.com/Corset">CG</a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
@@ -159,15 +155,17 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/23932002" alt="nenohi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
<td><a href="https://www.patreon.com/user?u=23932002">nenohi</a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Thu, 22 Aug 2019 17:38:06 UTC
**Last updated:** Sun, 01 Sep 2019 22:11:05 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright

View File

@@ -127,6 +127,7 @@ common:
geolocation-alert: "Vaše zařízení nedalo k dispozici lokaci"
error: "Chyba"
enter-username: "Zadejte své uživatelské jméno"
specified-recipient: "Pro"
add-visible-user: "Přidat uživatele"
username-prompt: "Zadejte své uživatelské jméno"
enter-file-name: "Upravit název souboru"
@@ -234,7 +235,7 @@ common:
deck-column-width-wide: "Široké"
use-shadow: "Používat v rozhraní stíny"
rounded-corners: "Zakulatit rohy v rozhraní"
circle-icons: "Používat kulaté ikony"
circle-icons: "Používat kulaté avatary"
contrasted-acct: "Přidat uživatelskému účtu kontrast"
wallpaper: "Obrázek na pozadí"
choose-wallpaper: "Zvolit pozadí"
@@ -383,6 +384,7 @@ common/views/components/games/reversi/reversi.vue:
cancel: "Zrušit"
common/views/components/games/reversi/reversi.game.vue:
surrender: "Vzdát se"
surrendered: "Vzdaním se"
looped-map: "Zacyklená mapa"
common/views/components/games/reversi/reversi.index.vue:
title: "Misskey Reversi"
@@ -416,6 +418,7 @@ common/views/components/connect-failed.vue:
title: "Nelze se připojit k serveru"
description: "Nastal problém s Vaším připojením k internetu, nebo server není dostupný nebo zrovna probíhá údržba. Prosím {zkuste to znova} za pár minut."
thanks: "Děkujeme že jste použili Misskey."
troubleshoot: "Odstranění problémů"
common/views/components/connect-failed.troubleshooter.vue:
title: "Poradce při potížích"
network: "Síťové připojení"
@@ -424,6 +427,8 @@ common/views/components/connect-failed.troubleshooter.vue:
checking-internet: "Ověřuji připojení k internetu."
server: "Připojení k serveru"
checking-server: "Spojuji se se serverem"
finding: "Vyšetřování problému"
no-network: "Žádné připojení k síti"
no-network-desc: "Ujistěte se že jste připojeni k Internetu."
no-internet: "Nejste připojeni k internetu"
no-internet-desc: "Jste připojen k síti, ale zdá se že stále chybí připojení k Internetu. Prosím zkontrolujte Vaše připojení k Internetu."

View File

@@ -213,7 +213,7 @@ common:
deck-column-width-wide: "Bred"
use-shadow: "Vis skygger"
rounded-corners: "Vis afrundede hjørner"
circle-icons: "Anvend cykliske ikoner"
circle-icons: "Anvend cykliske avatar"
contrasted-acct: "Tilføj kontrast til brugerkontoen"
wallpaper: "Baggrundsbillede"
choose-wallpaper: "Vælg en baggrund"
@@ -680,7 +680,7 @@ common/views/components/profile-editor.vue:
you-can-include-hashtags: "Du må gerne bruge hashtags i din profil beskrivelse"
language: "Sprog"
birthday: "Fødselsdag"
avatar: "Ikon"
avatar: "Avatar"
banner: "Banner"
is-cat: "Denne konto er en Kat"
is-bot: "Denne konto er en Bot"

View File

@@ -207,7 +207,7 @@ common:
deck-column-width-wide: "Sehr breit"
use-shadow: "Nutze Schatten"
rounded-corners: "Abgerundete Ecken"
circle-icons: "Kreisförmige Icons"
circle-icons: "Kreisförmige Avatar"
contrasted-acct: "Nutzernamen kontrastreicher darstellen"
wallpaper: "Hintergrund"
choose-wallpaper: "Hintergrund auswählen"
@@ -605,6 +605,7 @@ common/views/widgets/memo.vue:
save: "Speichern"
desktop:
banner: "Banner"
avatar: "Avatar"
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
desktop/views/components/activity.chart.vue:
total: "Schwarz ... komplett"

View File

@@ -133,6 +133,7 @@ common:
geolocation-alert: "Your device does not provide location services"
error: "Error"
enter-username: "Please enter username"
specified-recipient: "Recipient"
add-visible-user: "Add a user"
cw-placeholder: "Comments for the post (optional)"
username-prompt: "Please enter username"
@@ -246,7 +247,7 @@ common:
deck-column-width-wide: "Wide"
use-shadow: "Use shadows in the UI"
rounded-corners: "Round the corners of the UI"
circle-icons: "Use circular icons"
circle-icons: "Use circular avatar icon"
contrasted-acct: "Add contrast to user account"
wallpaper: "Background image"
choose-wallpaper: "Choose a background"
@@ -390,6 +391,9 @@ common/views/pages/explore.vue:
federated: "From the fediverse"
explore: "Explore {host}"
users-info: "Currently, {users} users are registered here"
common/views/components/reactions-viewer.details.vue:
few-users: "{users} reacted with {reaction}"
many-users: "{users}, and {omitted} more reacted with {reaction}"
common/views/components/url-preview.vue:
enable-player: "Enable playback"
disable-player: "Close the player"
@@ -752,8 +756,8 @@ common/views/components/profile-editor.vue:
uploading: "Uploading"
upload-failed: "Failed to upload"
unable-to-process: "The operation could not be completed."
avatar-not-an-image: "The file specified as an avatar is not an image"
banner-not-an-image: "The file specified as a banner is not an image"
avatar-not-an-image: "The file you specified as an avatar is not an image"
banner-not-an-image: "The file you specified as a banner is not an image"
email: "Email settings"
email-address: "Email Address"
email-verified: "Your email has been verified."
@@ -927,7 +931,7 @@ desktop/views/components/drive.file.vue:
copy-url: "Copy URL"
download: "Download"
else-files: "Other"
set-as-avatar: "Set as an avatar"
set-as-avatar: "Set as avatar"
set-as-banner: "Set as a banner"
open-in-app: "Open in app"
add-app: "Add app"
@@ -1817,6 +1821,7 @@ pages:
read-page: "Viewing the source"
page-created: "Created the page!"
page-updated: "Updated the page"
name-already-exists: "The specified page name already exists"
are-you-sure-delete: "Do you want to delete this page?"
page-deleted: "The page has been deleted"
edit-this-page: "Edit this page"

View File

@@ -177,7 +177,7 @@ common:
deck-column-width-wide: "Ancho"
use-shadow: "Usar sombras en la Interfaz de Usuario"
rounded-corners: "Esquinas redondeadas en la Interfaz de Usuario"
circle-icons: "Usar iconos circulares"
circle-icons: "Usar avatar circulares"
contrasted-acct: "Añadir contraste al nombre de usuario"
wallpaper: "Fondo de pantalla"
choose-wallpaper: "Escoge un fondo de pantalla"

View File

@@ -236,7 +236,7 @@ common:
deck-column-width-wide: "Large"
use-shadow: "Utiliser les ombres dans l'interface utilisateur"
rounded-corners: "Coins arrondis de l'interface utilisateur"
circle-icons: "Utiliser des icônes circulaires"
circle-icons: "Utiliser des avatar circulaires"
contrasted-acct: "Ajouter du contraste au nom de lutilisateur"
wallpaper: "Image du fond d'écran"
choose-wallpaper: "Sélectionner un fond d'écran"
@@ -284,6 +284,18 @@ common:
sync: "Synchroniser"
save: "Enregistrer"
saved: "enregistré"
home-profile: "Profil principal"
deck-profile: "Profil deck"
room: "Pièce"
_room:
graphicsQuality: "Qualité des graphismes"
_graphicsQuality:
ultra: "Très élevée"
high: "Élevée"
medium: "Moyenne"
low: "Basse"
cheep: "Minimale"
useOrthographicCamera: "Utiliser une caméra orthographique"
search: "Recherche"
delete: "Supprimer"
loading: "Chargement en cours…"
@@ -1115,6 +1127,7 @@ desktop/views/components/ui.header.account.vue:
groups: "Groupes"
follow-requests: "Demandes dabonnement"
admin: "Admin"
room: "Pièce"
desktop/views/components/ui.header.nav.vue:
game: "Jeux"
desktop/views/components/ui.header.notifications.vue:
@@ -2006,6 +2019,9 @@ room:
remove: "Enlever"
save: "Enregistrer"
saved: "enregistré"
clear: "Tout enlever"
clear-confirm: "Désirez-vous enlever tout les meubles de votre chambre ?"
leave-confirm: "Vous avez des modifications non-sauvegardées. Voulez-vous vraiment quitter ?"
chooseImage: "Sélectionnez une image"
room-type: "Type de chambre"
carpet-color: "Couleur du tapis"
@@ -2050,3 +2066,6 @@ room:
sofa: "Canapé"
spiral: "Escaliers en spirale"
bin: "Corbeille"
cup-noodle: "Bol de nouilles"
holo-display: "Affichage holographique"
energy-drink: "Boisson énergétique"

View File

@@ -139,6 +139,7 @@ common:
geolocation-alert: "お使いの端末は位置情報に対応していません"
error: "エラー"
enter-username: "ユーザー名を入力してください"
specified-recipient: "宛先"
add-visible-user: "ユーザーを追加"
cw-placeholder: "内容への注釈 (オプション)"
username-prompt: "ユーザー名を入力してください"
@@ -258,7 +259,7 @@ common:
deck-column-width-wide: "広"
use-shadow: "UIに影を使用"
rounded-corners: "UIの角を丸める"
circle-icons: "円形のアイコンを使用"
circle-icons: "円形のアバターを使用"
contrasted-acct: "ユーザー名にコントラストを付ける"
wallpaper: "壁紙"
choose-wallpaper: "壁紙を選択"
@@ -412,6 +413,10 @@ common/views/pages/explore.vue:
explore: "{host}を探索"
users-info: "現在{users}ユーザーが登録されています"
common/views/components/reactions-viewer.details.vue:
few-users: "{users}が{reaction}をリアクション"
many-users: "{users}と他{omitted}人が{reaction}をリアクション"
common/views/components/url-preview.vue:
enable-player: "プレイヤーを開く"
disable-player: "プレイヤーを閉じる"
@@ -795,7 +800,7 @@ common/views/components/profile-editor.vue:
you-can-include-hashtags: "ハッシュタグを含めることができます。"
language: "言語"
birthday: "誕生日"
avatar: "アイコン"
avatar: "アバター"
banner: "バナー"
is-cat: "このアカウントはCatです"
is-bot: "このアカウントはBotです"
@@ -809,7 +814,7 @@ common/views/components/profile-editor.vue:
uploading: "アップロード中"
upload-failed: "アップロードに失敗しました"
unable-to-process: "操作を完了できません"
avatar-not-an-image: "アイコンとして指定したファイルは画像ではありません"
avatar-not-an-image: "アバターとして指定したファイルは画像ではありません"
banner-not-an-image: "バナーとして指定したファイルは画像ではありません"
email: "メール設定"
email-address: "メールアドレス"
@@ -999,7 +1004,7 @@ desktop/views/components/drive-window.vue:
used: "使用中"
desktop/views/components/drive.file.vue:
avatar: "アイコン"
avatar: "アバター"
banner: "バナー"
nsfw: "閲覧注意"
contextmenu:
@@ -1009,7 +1014,7 @@ desktop/views/components/drive.file.vue:
copy-url: "URLをコピー"
download: "ダウンロード"
else-files: "その他"
set-as-avatar: "アイコンに設定"
set-as-avatar: "アバターに設定"
set-as-banner: "バナーに設定"
open-in-app: "アプリで開く"
add-app: "アプリを追加"
@@ -2014,6 +2019,9 @@ pages:
read-page: "ソースを表示中"
page-created: "ページを作成しました"
page-updated: "ページを更新しました"
name-already-exists: "指定されたページURLは既に存在しています"
title-invalid-name: "不正なページURLです"
text-invalid-name: "空白でないか確認してください"
are-you-sure-delete: "このページを削除しますか?"
page-deleted: "ページを削除しました"
edit-this-page: "このページを編集"

View File

@@ -467,7 +467,7 @@ common/views/components/profile-editor.vue:
description: "自己紹介"
language: "言語"
birthday: "誕生日"
avatar: "アイコン"
avatar: "アバター"
banner: "バナー"
is-cat: "このアカウントはCatやで"
is-bot: "このアカウントはBotやで"
@@ -605,7 +605,7 @@ desktop/views/components/crop-window.vue:
desktop/views/components/drive-window.vue:
used: "使うとる"
desktop/views/components/drive.file.vue:
avatar: "アイコン"
avatar: "アバター"
banner: "バナー"
nsfw: "見たらあかんで"
contextmenu:
@@ -615,7 +615,7 @@ desktop/views/components/drive.file.vue:
copy-url: "URLをコピー"
download: "ダウンロード"
else-files: "その他"
set-as-avatar: "アイコンにする"
set-as-avatar: "アバターにする"
set-as-banner: "バナーにする"
open-in-app: "アプリで開く"
add-app: "アプリ増やす"

View File

@@ -133,6 +133,7 @@ common:
geolocation-alert: "사용 중이신 장치에서는 위치 정보를 사용할 수 없습니다"
error: "오류"
enter-username: "사용자명을 입력해주세요"
specified-recipient: "수신인"
add-visible-user: "사용자 추가"
cw-placeholder: "내용에 대한 주석 (옵션)"
username-prompt: "사용자명을 입력해주세요"
@@ -246,7 +247,7 @@ common:
deck-column-width-wide: "넓음"
use-shadow: "UI에 그림자 효과 적용"
rounded-corners: "UI의 모서리를 둥글게 설정"
circle-icons: "원형 아이콘 사용"
circle-icons: "원형 아바타를 사용"
contrasted-acct: "사용자명에 대비 추가"
wallpaper: "배경"
choose-wallpaper: "배경 설정"
@@ -390,6 +391,9 @@ common/views/pages/explore.vue:
federated: "연합"
explore: "{host}을(를) 탐색"
users-info: "현재 {users} 사용자가 등록되어 있습니다"
common/views/components/reactions-viewer.details.vue:
few-users: "{users}님이 {reaction} 리액션"
many-users: "{users}님 외 {omitted}명이 {reaction} 리액션"
common/views/components/url-preview.vue:
enable-player: "플레이어 열기"
disable-player: "플레이어 닫기"
@@ -1817,6 +1821,7 @@ pages:
read-page: "소스 표시중"
page-created: "페이지를 만들었습니다"
page-updated: "페이지를 수정했습니다"
name-already-exists: "지정한 페이지 URL은 이미 존재합니다"
are-you-sure-delete: "이 페이지를 삭제하시겠습니까?"
page-deleted: "페이지가 삭제되었습니다"
edit-this-page: "이 페이지를 편집"

View File

@@ -232,6 +232,7 @@ common/views/pages/follow.vue:
follow: "Volgend"
desktop:
banner: "Omslagfoto"
avatar: "Gebruikersafbeelding"
unable-to-process: "De operatie kan niet worden voltooid."
desktop/views/components/activity.chart.vue:
total: "Zwart ... totaal"

View File

@@ -98,8 +98,10 @@ common:
visibility: "Widoczność"
error: "Błąd"
enter-username: "Wprowadź nazwę użytkownika"
specified-recipient: "Adresat"
add-visible-user: "Dodaj użytkownika"
username-prompt: "Wprowadź nazwę użytkownika"
enter-file-name: "Wprowadź nazwę pliku"
weekday-short:
sunday: "N"
monday: "Pn"
@@ -162,6 +164,7 @@ common:
note-visibility: "Widoczność wpisów"
remember-note-visibility: "Zapamiętaj widoczność wpisów"
web-search-engine: "Wyszukiwarka internetowa"
paste: "Wklej"
line-width: "Szerokości linii"
line-width-thin: "Cienka"
line-width-normal: "Normalna"

View File

@@ -133,6 +133,7 @@ common:
geolocation-alert: "您的设备不支持定位服务"
error: "错误"
enter-username: "输入用户名"
specified-recipient: "收件人"
add-visible-user: "添加用户"
cw-placeholder: "评论帖子(可选)"
username-prompt: "输入用户名"
@@ -246,7 +247,7 @@ common:
deck-column-width-wide: "宽"
use-shadow: "在UI中使用阴影效果"
rounded-corners: "UI界面圆角效果"
circle-icons: "使用圆形图标"
circle-icons: "使用圆形头像"
contrasted-acct: "增加用户名的对比度"
wallpaper: "壁纸"
choose-wallpaper: "选择壁纸"
@@ -390,6 +391,9 @@ common/views/pages/explore.vue:
federated: "联合"
explore: "查找{host}"
users-info: "当前有{users}个注册用户"
common/views/components/reactions-viewer.details.vue:
few-users: "{users}作出了{reaction}的回应"
many-users: "{users}和其他{omitted}人做出了{reaction}的回应"
common/views/components/url-preview.vue:
enable-player: "打开播放器"
disable-player: "关闭播放器"
@@ -927,7 +931,7 @@ desktop/views/components/drive.file.vue:
copy-url: "复制链接"
download: "下载"
else-files: "其他"
set-as-avatar: "设为头像"
set-as-avatar: "设为头像"
set-as-banner: "设置为背景"
open-in-app: "在应用程序中打开"
add-app: "添加应用"
@@ -1817,6 +1821,7 @@ pages:
read-page: "查看源"
page-created: "页面已创建"
page-updated: "页面已更新"
name-already-exists: "该页面URL已存在"
are-you-sure-delete: "是否删除此页面?"
page-deleted: "该页面已被删除。"
edit-this-page: "编辑此页面"
@@ -2095,6 +2100,7 @@ room:
saved: "已保存"
clear: "清理"
clear-confirm: "是否清除所有家具?"
leave-confirm: "有尚未保存的修改。是否离开?"
chooseImage: "选择图片"
room-type: "房间类型"
carpet-color: "地板颜色"
@@ -2143,3 +2149,5 @@ room:
spiral: "螺旋楼梯"
bin: "垃圾箱"
cup-noodle: "杯面"
holo-display: "全息显示器"
energy-drink: "能量饮料"

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "11.30.0",
"version": "11.31.3",
"codename": "daybreak",
"repository": {
"type": "git",
@@ -100,17 +100,17 @@
"@typescript-eslint/parser": "1.11.0",
"agentkeepalive": "4.0.2",
"animejs": "3.1.0",
"apexcharts": "3.8.4",
"apexcharts": "3.8.5",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
"aws-sdk": "2.512.0",
"aws-sdk": "2.520.0",
"bcryptjs": "2.4.3",
"bootstrap": "4.3.1",
"bootstrap-vue": "2.0.0-rc.13",
"bull": "3.10.0",
"cafy": "15.1.1",
"cbor": "4.2.1",
"cbor": "4.3.0",
"chai": "4.2.0",
"chalk": "2.4.2",
"cli-highlight": "2.1.1",
@@ -128,12 +128,12 @@
"eslint-plugin-vue": "5.2.3",
"eventemitter3": "4.0.0",
"feed": "3.0.0",
"file-type": "12.1.0",
"file-type": "12.2.0",
"fluent-ffmpeg": "2.1.2",
"fuckadblock": "3.2.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
"gulp-imagemin": "6.0.0",
"gulp-imagemin": "6.1.0",
"gulp-mocha": "6.0.0",
"gulp-rename": "1.4.0",
"gulp-replace": "1.0.0",
@@ -156,7 +156,7 @@
"json5-loader": "3.0.0",
"jsrsasign": "8.0.12",
"katex": "0.11.0",
"koa": "2.7.0",
"koa": "2.8.1",
"koa-bodyparser": "4.2.1",
"koa-compress": "3.0.0",
"koa-favicon": "2.0.1",
@@ -198,7 +198,7 @@
"randomcolor": "0.5.4",
"ratelimiter": "3.3.1",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.10",
"reconnecting-websocket": "4.2.0",
"redis": "2.8.0",
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
@@ -216,14 +216,15 @@
"speakeasy": "2.0.0",
"stringz": "2.0.0",
"style-loader": "0.23.1",
"stylus": "0.54.5",
"stylus": "0.54.7",
"stylus-loader": "3.0.2",
"summaly": "2.3.0",
"systeminformation": "4.14.4",
"summaly": "2.3.1",
"syslog-pro": "1.0.0",
"systeminformation": "4.14.8",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.4.1",
"textarea-caret": "3.1.0",
"three": "0.107.0",
"three": "0.108.0",
"tinycolor2": "1.4.1",
"tmp": "0.1.0",
"ts-loader": "5.3.3",
@@ -235,7 +236,7 @@
"uglify-es": "3.3.9",
"ulid": "2.3.0",
"url-loader": "2.1.0",
"uuid": "3.3.2",
"uuid": "3.3.3",
"v-animate-css": "0.0.3",
"v-debounce": "0.1.2",
"vue": "2.6.10",
@@ -244,21 +245,21 @@
"vue-cropperjs": "4.0.0",
"vue-i18n": "8.14.0",
"vue-js-modal": "1.3.31",
"vue-json-pretty": "1.6.0",
"vue-json-pretty": "1.6.1",
"vue-loader": "15.7.1",
"vue-marquee-text-component": "1.1.1",
"vue-prism-component": "1.1.1",
"vue-router": "3.1.2",
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.16",
"vue-svg-inline-loader": "1.2.17",
"vue-template-compiler": "2.6.10",
"vuedraggable": "2.23.0",
"vuewordcloud": "18.7.11",
"vuex": "3.1.1",
"vuex-persistedstate": "2.5.4",
"web-push": "3.3.5",
"webpack": "4.39.2",
"webpack": "4.39.3",
"webpack-cli": "3.3.7",
"websocket": "1.0.29",
"ws": "7.1.2",

View File

@@ -159,7 +159,7 @@ async function init(): Promise<Config> {
return config;
}
async function spawnWorkers(limit: number = Infinity) {
async function spawnWorkers(limit: number = 1) {
const workers = Math.min(limit, os.cpus().length);
bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
await Promise.all([...Array(workers)].map(spawnWorker));

View File

@@ -19,7 +19,8 @@ init(launch => {
mode: 'history',
base: '/admin/',
routes: [
{ path: '/', component: Index },
{ path: '/:page', component: Index },
{ path: '/', redirect: '/dashboard' },
{ path: '*', component: NotFound }
]
});

View File

@@ -18,18 +18,18 @@
<p class="name"><mk-user-name :user="$store.state.i"/></p>
</div>
<ul>
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
<li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
<li @click="nav('abuse')" :class="{ active: page == 'abuse' }"><fa :icon="faExclamationCircle" fixed-width/>{{ $t('abuse') }}</li>
<li><router-link to="/dashboard" active-class="active"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</router-link></li>
<li><router-link to="/instance" active-class="active"><fa icon="cog" fixed-width/>{{ $t('instance') }}</router-link></li>
<li><router-link to="/queue" active-class="active"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</router-link></li>
<li><router-link to="/logs" active-class="active"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</router-link></li>
<li><router-link to="/db" active-class="active"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</router-link></li>
<li><router-link to="/moderators" active-class="active"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</router-link></li>
<li><router-link to="/users" active-class="active"><fa icon="users" fixed-width/>{{ $t('users') }}</router-link></li>
<li><router-link to="/drive" active-class="active"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</router-link></li>
<li><router-link to="/federation" active-class="active"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</router-link></li>
<li><router-link to="/emoji" active-class="active"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</router-link></li>
<li><router-link to="/announcements" active-class="active"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</router-link></li>
<li><router-link to="/abuse" active-class="active"><fa :icon="faExclamationCircle" fixed-width/>{{ $t('abuse') }}</router-link></li>
</ul>
<div class="back-to-misskey">
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
@@ -102,7 +102,6 @@ export default Vue.extend({
},
data() {
return {
page: 'dashboard',
version,
isMobile,
navOpend: !isMobile,
@@ -116,9 +115,9 @@ export default Vue.extend({
faDatabase,
};
},
methods: {
nav(page: string) {
this.page = page;
computed: {
page() {
return this.$route.params.page;
}
}
});
@@ -240,11 +239,10 @@ export default Vue.extend({
list-style none
font-size 15px
> li
> li > a
display block
padding 10px 16px
margin 0
cursor pointer
user-select none
color #eee
transition margin-left 0.2s ease

View File

@@ -27,7 +27,8 @@ export default (opts: Opts = {}) => ({
data() {
return {
showContent: false,
hideThisNote: false
hideThisNote: false,
openingMenu: false
};
},
@@ -192,11 +193,16 @@ export default (opts: Opts = {}) => ({
},
menu(viaKeyboard = false) {
if (this.openingMenu) return;
this.openingMenu = true;
this.$root.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.appearNote,
animation: !viaKeyboard
}).$once('closed', this.focus);
}).$once('closed', () => {
this.openingMenu = false;
this.focus();
});
},
toggleShowContent() {

View File

@@ -153,6 +153,10 @@ export default (opts) => ({
// デフォルト公開範囲
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility);
if (this.reply && this.reply.localOnly) {
this.localOnly = true;
}
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {
this.visibility = this.reply.visibility;
@@ -162,13 +166,13 @@ export default (opts) => ({
}).then(users => {
this.visibleUsers.push(...users);
});
}
}
if (this.reply && this.reply.userId !== this.$store.state.i.id) {
this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
this.visibleUsers.push(user);
});
if (this.reply.userId !== this.$store.state.i.id) {
this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
this.visibleUsers.push(user);
});
}
}
}
// keep cw when reply
@@ -199,8 +203,9 @@ export default (opts) => ({
this.$emit('change-attached-files', this.files);
}
}
// 削除して編集
if (this.initialNote) {
// 削除して編集
const init = this.initialNote;
this.text = init.text ? init.text : '';
this.files = init.files;
@@ -318,7 +323,7 @@ export default (opts) => ({
setVisibility() {
const w = this.$root.new(MkVisibilityChooser, {
source: this.$refs.visibilityButton,
currentVisibility: this.visibility
currentVisibility: this.localOnly ? `local-${this.visibility}` : this.visibility
});
w.$once('chosen', v => {
this.applyVisibility(v);

View File

@@ -1,16 +1,16 @@
<template>
<prism :inline="inline" :language="lang || 'js'">{{ code }}</prism>
<x-prism :inline="inline" :language="prismLang">{{ code }}</x-prism>
</template>
<script lang="ts">
import Vue from 'vue';
import 'prismjs';
import 'prismjs/themes/prism-okaidia.css';
import Prism from 'vue-prism-component';
import XPrism from 'vue-prism-component';
export default Vue.extend({
components: {
Prism
XPrism
},
props: {
code: {
@@ -25,6 +25,12 @@ export default Vue.extend({
type: Boolean,
required: false
}
},
computed: {
prismLang() {
return Prism.languages[this.lang] ? this.lang : 'js';
}
}
});
</script>

View File

@@ -57,7 +57,8 @@ export default Vue.extend({
},
fit: {
type: String,
required: true
required: false,
default: 'cover'
},
detail: {
type: Boolean,

View File

@@ -35,7 +35,8 @@ export default Vue.extend({
mounted() {
//#region for Safari bug
if (this.$refs.grid) {
this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px` : '128px';
this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px`
: this.$store.state.device.inDeckMode ? '128px' : this.$root.isMobile ? '173px' : '287px';
}
//#endregion
},

View File

@@ -139,6 +139,7 @@ export default Vue.extend({
cursor pointer
> .content
max-width 100%
> .is-deleted
display block
@@ -155,6 +156,7 @@ export default Vue.extend({
padding 8px 16px
overflow hidden
overflow-wrap break-word
word-break break-word
font-size 1em
color rgba(#000, 0.8)

View File

@@ -227,6 +227,7 @@ export default Vue.extend({
},
closed() {
this.$emit('closed');
this.$nextTick(() => {
this.destroyDom();
});

View File

@@ -0,0 +1,117 @@
<template>
<transition name="zoom-in-top">
<div class="buebdbiu" ref="popover" v-if="show">
<i18n path="few-users" v-if="users.length <= 10">
<span slot="users">
<b v-for="u in users" :key="u.id" style="margin-right: 8px;">
<mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
<mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
</b>
</span>
<mk-reaction-icon slot="reaction" :reaction="reaction" ref="icon" />
</i18n>
<i18n path="many-users" v-if="10 < users.length">
<span slot="users">{{ users.slice(0, 10).join(', ') }}</span>
<span slot="ommited">{{ count - 10 }}</span>
<mk-reaction-icon slot="reaction" :reaction="reaction" ref="icon" />
</i18n>
</div>
</transition>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('common/views/components/reactions-viewer.details.vue'),
props: {
reaction: {
type: String,
required: true,
},
users: {
type: Array,
required: true,
},
count: {
type: Number,
required: true,
},
source: {
required: true,
}
},
data() {
return {
show: false
};
},
mounted() {
this.show = true;
this.$nextTick(() => {
const popover = this.$refs.popover as any;
if (this.source == null) {
this.destroyDom();
return;
}
const rect = this.source.getBoundingClientRect();
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
popover.style.left = (x - 28) + 'px';
popover.style.top = (y + 16) + 'px';
});
}
methods: {
close() {
this.show = false;
setTimeout(this.destroyDom, 300);
}
}
})
</script>
<style lang="stylus" scoped>
.buebdbiu
$bgcolor = var(--popupBg)
z-index 10000
display block
position absolute
max-width 240px
font-size 0.8em
padding 5px 8px
background $bgcolor
text-align center
color var(--text)
border-radius 4px
box-shadow 0 var(--lineWidth) 4px rgba(#000, 0.25)
pointer-events none
transform-origin center -16px
&:before
content ""
pointer-events none
display block
position absolute
top -28px
left 12px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px rgba(#000, 0.1)
border-left solid 14px transparent
&:after
content ""
pointer-events none
display block
position absolute
top -27px
left 12px
border-top solid 14px transparent
border-right solid 14px transparent
border-bottom solid 14px $bgcolor
border-left solid 14px transparent
</style>

View File

@@ -5,6 +5,9 @@
@click="toggleReaction(reaction)"
v-if="count > 0"
v-particle="!isMe"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
ref="reaction"
>
<mk-reaction-icon :reaction="reaction" ref="icon"/>
<span>{{ count }}</span>
@@ -15,6 +18,7 @@
import Vue from 'vue';
import Icon from './reaction-icon.vue';
import anime from 'animejs';
import XDetails from './reactions-viewer.details.vue';
export default Vue.extend({
props: {
@@ -26,6 +30,10 @@ export default Vue.extend({
type: Number,
required: true,
},
isInitial: {
type: Boolean,
required: true,
},
note: {
type: Object,
required: true,
@@ -36,14 +44,25 @@ export default Vue.extend({
default: true,
},
},
data() {
return {
details: null,
detailsTimeoutId: null,
isHovering: false
};
},
computed: {
isMe(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
},
},
mounted() {
if (!this.isInitial) this.anime();
},
watch: {
count() {
this.anime();
count(newCount, oldCount) {
if (oldCount < newCount) this.anime();
if (this.details != null) this.openDetails();
},
},
methods: {
@@ -70,11 +89,49 @@ export default Vue.extend({
});
}
},
onMouseover() {
this.isHovering = true;
this.detailsTimeoutId = setTimeout(this.openDetails, 300);
},
onMouseleave() {
this.isHovering = false;
clearTimeout(this.detailsTimeoutId);
this.closeDetails();
},
openDetails() {
if (this.$root.isMobile) return;
this.$root.api('notes/reactions', {
noteId: this.note.id,
type: this.reaction,
limit: 11
}).then((reactions: any[]) => {
const users = reactions
.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
.map(x => x.user);
this.closeDetails();
if (!this.isHovering) return;
this.details = this.$root.new(XDetails, {
reaction: this.reaction,
users,
count: this.count,
source: this.$refs.reaction
});
});
},
closeDetails() {
if (this.details != null) {
this.details.close();
this.details = null;
}
},
anime() {
if (this.$store.state.device.reduceMotion) return;
if (document.hidden) return;
this.$nextTick(() => {
if (this.$refs.icon == null) return;
const rect = this.$refs.icon.$el.getBoundingClientRect();
const x = rect.left;
@@ -120,6 +177,14 @@ export default Vue.extend({
border-radius 4px
cursor pointer
&, *
-webkit-touch-callout none
-webkit-user-select none
-khtml-user-select none
-moz-user-select none
-ms-user-select none
user-select none
*
user-select none
pointer-events none

View File

@@ -1,6 +1,6 @@
<template>
<div class="mk-reactions-viewer" :class="{ isMe }">
<x-reaction v-for="(count, reaction) in reactions" :reaction="reaction" :count="count" :note="note" :key="reaction"/>
<x-reaction v-for="(count, reaction) in note.reactions" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note" :key="reaction"/>
</div>
</template>
@@ -12,6 +12,11 @@ export default Vue.extend({
components: {
XReaction
},
data() {
return {
initialReactions: new Set(Object.keys(this.note.reactions))
};
},
props: {
note: {
type: Object,
@@ -19,9 +24,6 @@ export default Vue.extend({
},
},
computed: {
reactions(): any {
return this.note.reactions;
},
isMe(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
},

View File

@@ -49,6 +49,7 @@ export default Vue.extend({
},
methods: {
toggle() {
if (this.disabled) return;
this.$emit('change', !this.checked);
}
}

View File

@@ -220,37 +220,48 @@ export default Vue.extend({
methods: {
save() {
const options = {
title: this.title.trim(),
name: this.name.trim(),
summary: this.summary,
font: this.font,
hideTitleWhenPinned: this.hideTitleWhenPinned,
alignCenter: this.alignCenter,
content: this.content,
variables: this.variables,
eyeCatchingImageId: this.eyeCatchingImageId,
};
const onError = err => {
if (err.id == '3d81ceae-475f-4600-b2a8-2bc116157532') {
if (err.info.param == 'name') {
this.$root.dialog({
type: 'error',
title: this.$t('title-invalid-name'),
text: this.$t('text-invalid-name')
});
}
} else if (err.code == 'NAME_ALREADY_EXISTS') {
this.$root.dialog({
type: 'error',
text: this.$t('name-already-exists')
});
}
};
if (this.pageId) {
this.$root.api('pages/update', {
pageId: this.pageId,
title: this.title.trim(),
name: this.name.trim(),
summary: this.summary,
font: this.font,
hideTitleWhenPinned: this.hideTitleWhenPinned,
alignCenter: this.alignCenter,
content: this.content,
variables: this.variables,
eyeCatchingImageId: this.eyeCatchingImageId,
}).then(page => {
options.pageId = this.pageId;
this.$root.api('pages/update', options)
.then(page => {
this.currentName = this.name.trim();
this.$root.dialog({
type: 'success',
text: this.$t('page-updated')
});
});
}).catch(onError);
} else {
this.$root.api('pages/create', {
title: this.title.trim(),
name: this.name.trim(),
summary: this.summary,
font: this.font,
hideTitleWhenPinned: this.hideTitleWhenPinned,
alignCenter: this.alignCenter,
content: this.content,
variables: this.variables,
eyeCatchingImageId: this.eyeCatchingImageId,
}).then(page => {
this.$root.api('pages/create', options)
.then(page => {
this.pageId = page.id;
this.currentName = this.name.trim();
this.$root.dialog({
@@ -258,7 +269,7 @@ export default Vue.extend({
text: this.$t('page-created')
});
this.$router.push(`/i/pages/edit/${this.pageId}`);
});
}).catch(onError);
}
},

View File

@@ -25,7 +25,8 @@ export default define({
data() {
return {
text: null,
changed: false
changed: false,
timeoutId: null
};
},
@@ -45,6 +46,8 @@ export default define({
onChange() {
this.changed = true;
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(this.saveMemo, 1000);
},
saveMemo() {

View File

@@ -185,7 +185,8 @@ init(async (launch, os) => {
{ path: '/i/messaging/:user', component: MkMessagingRoom },
{ path: '/i/drive', component: MkDrive },
{ path: '/i/drive/folder/:folder', component: MkDrive },
{ path: '/i/settings', component: MkSettings },
{ path: '/i/settings', redirect: '/i/settings/profile' },
{ path: '/i/settings/:page', component: MkSettings },
{ path: '/selectdrive', component: MkSelectDrive },
{ path: '/@:acct/room', props: true, component: () => import('../common/views/pages/room/room.vue').then(m => m.default) },
{ path: '/share', component: MkShare },

View File

@@ -10,14 +10,18 @@
<b>{{ $t('@.post-form.recent-tags') }}:</b>
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('@.post-form.click-to-tagging')">#{{ tag }}</a>
</div>
<div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }} <a @click="quoteId = null">[x]</a></div>
<div v-if="visibility === 'specified'" class="visibleUsers">
<span v-for="u in visibleUsers">
<mk-user-name :user="u"/><a @click="removeVisibleUser(u)">[x]</a>
</span>
<a @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</a>
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('@.post-form.quote-attached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
<div v-if="visibility === 'specified'" class="to-specified">
<fa icon="envelope"/> {{ $t('@.post-form.specified-recipient') }}
<div class="visibleUsers">
<span v-for="u in visibleUsers">
<mk-user-name :user="u"/>
<button @click="removeVisibleUser(u)"><fa icon="times"/></button>
</span>
<button @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</button>
</div>
</div>
<div class="local-only" v-if="localOnly === true">{{ $t('@.post-form.local-only-message') }}</div>
<div class="local-only" v-if="localOnly === true"><fa icon="heart"/> {{ $t('@.post-form.local-only-message') }}</div>
<input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }">
<div class="textarea">
<textarea :class="{ with: (files.length != 0 || poll) }"
@@ -207,13 +211,37 @@ export default Vue.extend({
margin 0 0 8px 0
color var(--primary)
> .visibleUsers
margin-bottom 8px
font-size 14px
> button
padding 4px 8px
color var(--primaryAlpha04)
> span
margin-right 16px
color var(--primary)
&:hover
color var(--primaryAlpha06)
&:active
color var(--primaryDarken30)
> .to-specified
margin 0 0 8px 0
color var(--primary)
> .visibleUsers
display inline
top -1px
font-size 14px
> span
margin-left 14px
> button
padding 4px 8px
color var(--primaryAlpha04)
&:hover
color var(--primaryAlpha06)
&:active
color var(--primaryDarken30)
> .local-only
margin 0 0 8px 0

View File

@@ -1,17 +1,17 @@
<template>
<div class="mk-settings">
<div class="nav" :class="{ inWindow }">
<p :class="{ active: page == 'profile' }" @mousedown="page = 'profile'"><fa icon="user" fixed-width/>{{ $t('@._settings.profile') }}</p>
<p :class="{ active: page == 'appearance' }" @mousedown="page = 'appearance'"><fa icon="palette" fixed-width/>{{ $t('@._settings.appearance') }}</p>
<p :class="{ active: page == 'behavior' }" @mousedown="page = 'behavior'"><fa icon="desktop" fixed-width/>{{ $t('@._settings.behavior') }}</p>
<p :class="{ active: page == 'notification' }" @mousedown="page = 'notification'"><fa :icon="['far', 'bell']" fixed-width/>{{ $t('@._settings.notification') }}</p>
<p :class="{ active: page == 'drive' }" @mousedown="page = 'drive'"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</p>
<p :class="{ active: page == 'hashtags' }" @mousedown="page = 'hashtags'"><fa icon="hashtag" fixed-width/>{{ $t('@._settings.tags') }}</p>
<p :class="{ active: page == 'muteAndBlock' }" @mousedown="page = 'muteAndBlock'"><fa icon="ban" fixed-width/>{{ $t('@._settings.mute-and-block') }}</p>
<p :class="{ active: page == 'apps' }" @mousedown="page = 'apps'"><fa icon="puzzle-piece" fixed-width/>{{ $t('@._settings.apps') }}</p>
<p :class="{ active: page == 'security' }" @mousedown="page = 'security'"><fa icon="unlock-alt" fixed-width/>{{ $t('@._settings.security') }}</p>
<p :class="{ active: page == 'api' }" @mousedown="page = 'api'"><fa icon="key" fixed-width/>API</p>
<p :class="{ active: page == 'other' }" @mousedown="page = 'other'"><fa icon="cogs" fixed-width/>{{ $t('@._settings.other') }}</p>
<router-link to="/i/settings/profile" active-class="active"><fa icon="user" fixed-width/>{{ $t('@._settings.profile') }}</router-link>
<router-link to="/i/settings/appearance" active-class="active"><fa icon="palette" fixed-width/>{{ $t('@._settings.appearance') }}</router-link>
<router-link to="/i/settings/behavior" active-class="active"><fa icon="desktop" fixed-width/>{{ $t('@._settings.behavior') }}</router-link>
<router-link to="/i/settings/notification" active-class="active"><fa :icon="['far', 'bell']" fixed-width/>{{ $t('@._settings.notification') }}</router-link>
<router-link to="/i/settings/drive" active-class="active"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</router-link>
<router-link to="/i/settings/hashtags" active-class="active"><fa icon="hashtag" fixed-width/>{{ $t('@._settings.tags') }}</router-link>
<router-link to="/i/settings/muteAndBlock" active-class="active"><fa icon="ban" fixed-width/>{{ $t('@._settings.mute-and-block') }}</router-link>
<router-link to="/i/settings/apps" active-class="active"><fa icon="puzzle-piece" fixed-width/>{{ $t('@._settings.apps') }}</router-link>
<router-link to="/i/settings/security" active-class="active"><fa icon="unlock-alt" fixed-width/>{{ $t('@._settings.security') }}</router-link>
<router-link to="/i/settings/api" active-class="active"><fa icon="key" fixed-width/>API</router-link>
<router-link to="/i/settings/other" active-class="active"><fa icon="cogs" fixed-width/>{{ $t('@._settings.other') }}</router-link>
</div>
<div class="pages">
<x-settings :page="page"/>
@@ -30,9 +30,9 @@ export default Vue.extend({
XSettings,
},
props: {
initialPage: {
page: {
type: String,
required: false
required: true,
},
inWindow: {
type: Boolean,
@@ -40,11 +40,6 @@ export default Vue.extend({
default: true
}
},
data() {
return {
page: this.initialPage || 'profile',
};
},
});
</script>
@@ -63,7 +58,7 @@ export default Vue.extend({
z-index 1
font-size 15px
> p
> a
display block
padding 10px 16px
margin 0

View File

@@ -1,7 +1,7 @@
<template>
<mk-ui>
<main>
<x-settings :in-window="false"/>
<x-settings :in-window="false" :page="$route.params.page" />
</main>
</mk-ui>
</template>

View File

@@ -140,6 +140,7 @@ init((launch, os) => {
]),
{ path: '/signup', name: 'signup', component: MkSignup },
{ path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) },
{ path: '/i/settings/:page', redirect: '/i/settings' },
{ path: '/i/favorites', name: 'favorites', component: UI, props: route => ({ component: () => import('../common/views/pages/favorites.vue').then(m => m.default), platform: 'mobile' }) },
{ path: '/i/pages', name: 'pages', component: UI, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
{ path: '/i/lists', name: 'user-lists', component: UI, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },

View File

@@ -122,7 +122,7 @@ export default Vue.extend({
this.$root.api('drive/files/delete', {
fileId: this.file.id
}).then(() => {
this.browser.cd(this.file.folderId, true);
this.browser.cd(this.file.folderId);
});
},

View File

@@ -163,8 +163,6 @@ export default Vue.extend({
},
cd(target, silent = false) {
this.file = null;
if (target == null) {
this.goRoot(silent);
return;
@@ -172,6 +170,7 @@ export default Vue.extend({
target = target.id;
}
this.file = null;
this.fetching = true;
this.$root.api('drive/folders/show', {
@@ -244,13 +243,14 @@ export default Vue.extend({
},
goRoot(silent = false) {
if (this.folder || this.file) {
this.file = null;
this.folder = null;
this.hierarchyFolders = [];
this.$emit('move-root', silent);
this.fetch();
}
// すでにrootにいるなら何もしない
if (this.folder == null && this.file == null) return;
this.file = null;
this.folder = null;
this.hierarchyFolders = [];
this.$emit('move-root', silent);
this.fetch();
},
fetch() {

View File

@@ -17,15 +17,18 @@
<div class="form">
<mk-note-preview class="preview" v-if="reply" :note="reply"/>
<mk-note-preview class="preview" v-if="renote" :note="renote"/>
<div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }} <a @click="quoteId = null">[x]</a></div>
<div v-if="visibility === 'specified'" class="visibleUsers">
<span v-for="u in visibleUsers">
<mk-user-name :user="u"/>
<a @click="removeVisibleUser(u)">[x]</a>
</span>
<a @click="addVisibleUser">+{{ $t('@.post-form.add-visible-user') }}</a>
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('@.post-form.quote-attached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
<div v-if="visibility === 'specified'" class="to-specified">
<fa icon="envelope"/> {{ $t('@.post-form.specified-recipient') }}
<div class="visibleUsers">
<span v-for="u in visibleUsers">
<mk-user-name :user="u"/>
<button @click="removeVisibleUser(u)"><fa icon="times"/></button>
</span>
<button @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</button>
</div>
</div>
<div class="local-only" v-if="localOnly === true">{{ $t('@.post-form.local-only-message') }}</div>
<div class="local-only" v-if="localOnly === true"><fa icon="heart"/> {{ $t('@.post-form.local-only-message') }}</div>
<input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }">
<textarea v-model="text" ref="text" :disabled="posting" :placeholder="placeholder" v-autocomplete="{ model: 'text' }" @paste="onPaste"></textarea>
<x-post-form-attaches class="attaches" :files="files"/>
@@ -145,13 +148,37 @@ export default Vue.extend({
margin 0 0 8px 0
color var(--primary)
> .visibleUsers
margin 5px
font-size 14px
> button
padding 4px 8px
color var(--primaryAlpha04)
> span
margin-right 16px
color var(--text)
&:hover
color var(--primaryAlpha06)
&:active
color var(--primaryDarken30)
> .to-specified
margin 0 0 8px 0
color var(--primary)
> .visibleUsers
display inline
top -1px
font-size 14px
> span
margin-left 14px
> button
padding 4px 8px
color var(--primaryAlpha04)
&:hover
color var(--primaryAlpha06)
&:active
color var(--primaryDarken30)
> .local-only
margin 0 0 8px 0

View File

@@ -34,6 +34,7 @@ export type Source = {
autoAdmin?: boolean;
proxy?: string;
proxySmtp?: string;
accesslog?: string;
@@ -42,6 +43,14 @@ export type Source = {
id: string;
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
deliverJobConcurrency?: number;
inboxJobConcurrency?: number;
syslog: {
host: string;
port: number;
};
};
/**

View File

@@ -203,8 +203,8 @@ export function createCleanRemoteFilesJob() {
export default function() {
if (!program.onlyServer) {
deliverQueue.process(128, processDeliver);
inboxQueue.process(128, processInbox);
deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver);
inboxQueue.process(config.inboxJobConcurrency || 16, processInbox);
processDb(dbQueue);
procesObjectStorage(objectStorageQueue);
}

View File

@@ -196,7 +196,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
// ハッシュタグ更新
updateUsertags(user!, tags);
//#region アイコンとヘッダー画像をフェッチ
//#region アバターとヘッダー画像をフェッチ
const [avatar, banner] = (await Promise.all<DriveFile | null>([
person.icon,
person.image
@@ -285,7 +285,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
logger.info(`Updating the Person: ${person.id}`);
// アイコンとヘッダー画像をフェッチ
// アバターとヘッダー画像をフェッチ
const [avatar, banner] = (await Promise.all<DriveFile | null>([
person.icon,
person.image

View File

@@ -158,7 +158,7 @@ export default async function renderNote(note: Note, dive = true): Promise<any>
cc,
inReplyTo,
attachment: files.map(renderDocument),
sensitive: files.some(file => file.isSensitive),
sensitive: note.cw != null || files.some(file => file.isSensitive),
tag,
...asPoll
};

View File

@@ -0,0 +1,21 @@
import define from '../../define';
import { driveChart, notesChart, usersChart, instanceChart } from '../../../../services/chart';
import { insertModerationLog } from '../../../../services/insert-moderation-log';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
};
export default define(meta, async (ps, me) => {
insertModerationLog(me, 'chartResync');
driveChart.resync();
notesChart.resync();
usersChart.resync();
instanceChart.resync();
// TODO: ユーザーごとのチャートもキューに入れて更新する
});

View File

@@ -66,7 +66,7 @@ export const meta = {
avatarId: {
validator: $.optional.nullable.type(ID),
desc: {
'ja-JP': 'アイコンに設定する画像のドライブファイルID'
'ja-JP': 'アバターに設定する画像のドライブファイルID'
}
},

View File

@@ -139,7 +139,12 @@ export default define(meta, async (ps, me) => {
errorImageUrl: instance.errorImageUrl,
iconUrl: instance.iconUrl,
maxNoteTextLength: instance.maxNoteTextLength,
emojis: emojis,
emojis: emojis.map(e => ({
id: e.id,
aliases: e.aliases,
name: e.name,
url: e.url,
})),
enableEmail: instance.enableEmail,
enableTwitterIntegration: instance.enableTwitterIntegration,

View File

@@ -130,59 +130,35 @@ export default define(meta, async (ps, user) => {
generateVisibilityQuery(query, user);
generateMuteQuery(query, user);
/* TODO
// MongoDBではトップレベルで否定ができないため、De Morganの法則を利用してクエリします。
// つまり、「『自分の投稿かつRenote』ではない」を「『自分の投稿ではない』または『Renoteではない』」と表現します。
// for details: https://en.wikipedia.org/wiki/De_Morgan%27s_laws
if (ps.includeMyRenotes === false) {
query.$and.push({
$or: [{
userId: { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.userId != :meId', { meId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.includeRenotedMyNotes === false) {
query.$and.push({
$or: [{
'_renote.userId': { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteUserId != :meId', { meId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.includeLocalRenotes === false) {
query.$and.push({
$or: [{
'_renote.user.host': { $ne: null }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteUserHost IS NOT NULL');
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
*/
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');

View File

@@ -112,12 +112,8 @@ export default define(meta, async (ps, user) => {
}));
if (ps.excludeNsfw) {
// v11 TODO
/*
query['_files.isSensitive'] = {
$ne: true
};
*/
query.andWhere('note.cw IS NULL');
query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)');
}
}
//#endregion

View File

@@ -4,6 +4,8 @@ import define from '../../define';
import { getNote } from '../../common/getters';
import { ApiError } from '../../error';
import { NoteReactions } from '../../../../models';
import { DeepPartial } from 'typeorm';
import { NoteReaction } from '../../../../models/entities/note-reaction';
export const meta = {
desc: {
@@ -24,6 +26,10 @@ export const meta = {
}
},
type: {
validator: $.optional.nullable.str,
},
limit: {
validator: $.optional.num.range(1, 100),
default: 10
@@ -70,7 +76,11 @@ export default define(meta, async (ps, user) => {
const query = {
noteId: note.id
};
} as DeepPartial<NoteReaction>;
if (ps.type) {
query.reaction = ps.type;
}
const reactions = await NoteReactions.find({
where: query,

View File

@@ -116,58 +116,35 @@ export default define(meta, async (ps, user) => {
generateVisibilityQuery(query, user);
generateMuteQuery(query, user);
/* v11 TODO
// MongoDBではトップレベルで否定ができないため、De Morganの法則を利用してクエリします。
// つまり、「『自分の投稿かつRenote』ではない」を「『自分の投稿ではない』または『Renoteではない』」と表現します。
// for details: https://en.wikipedia.org/wiki/De_Morgan%27s_laws
if (ps.includeMyRenotes === false) {
query.$and.push({
$or: [{
userId: { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.userId != :meId', { meId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.includeRenotedMyNotes === false) {
query.$and.push({
$or: [{
'_renote.userId': { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteUserId != :meId', { meId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.includeLocalRenotes === false) {
query.$and.push({
$or: [{
'_renote.user.host': { $ne: null }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
}*/
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteUserHost IS NOT NULL');
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');

View File

@@ -6,6 +6,7 @@ import { UserLists, UserListJoinings, Notes } from '../../../../models';
import { makePaginationQuery } from '../../common/make-pagination-query';
import { generateVisibilityQuery } from '../../common/generate-visibility-query';
import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm';
export const meta = {
desc: {
@@ -134,58 +135,35 @@ export default define(meta, async (ps, user) => {
generateVisibilityQuery(query, user);
/* TODO
// MongoDBではトップレベルで否定ができないため、De Morganの法則を利用してクエリします。
// つまり、「『自分の投稿かつRenote』ではない」を「『自分の投稿ではない』または『Renoteではない』」と表現します。
// for details: https://en.wikipedia.org/wiki/De_Morgan%27s_laws
if (ps.includeMyRenotes === false) {
query.$and.push({
$or: [{
userId: { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.userId != :meId', { meId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.includeRenotedMyNotes === false) {
query.$and.push({
$or: [{
'_renote.userId': { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteUserId != :meId', { meId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.includeLocalRenotes === false) {
query.$and.push({
$or: [{
'_renote.user.host': { $ne: null }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
}*/
query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteUserHost IS NOT NULL');
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');

View File

@@ -29,7 +29,7 @@ export const meta = {
},
name: {
validator: $.str,
validator: $.str.min(1),
},
summary: {
@@ -76,6 +76,11 @@ export const meta = {
code: 'NO_SUCH_FILE',
id: 'b7b97489-0f66-4b12-a5ff-b21bd63f6e1c'
},
nameAlreadyExists: {
message: 'Specified name already exists.',
code: 'NAME_ALREADY_EXISTS',
id: '4650348e-301c-499a-83c9-6aa988c66bc1'
}
}
};
@@ -92,6 +97,15 @@ export default define(meta, async (ps, user) => {
}
}
await Pages.find({
userId: user.id,
name: ps.name
}).then(result => {
if (result.length > 0) {
throw new ApiError(meta.errors.nameAlreadyExists);
}
});
const page = await Pages.save(new Page({
id: genId(),
createdAt: new Date(),

View File

@@ -4,6 +4,7 @@ import define from '../../define';
import { ApiError } from '../../error';
import { Pages, DriveFiles } from '../../../../models';
import { ID } from '../../../../misc/cafy-id';
import { Not } from 'typeorm';
export const meta = {
desc: {
@@ -35,7 +36,7 @@ export const meta = {
},
name: {
validator: $.optional.str,
validator: $.str.min(1),
},
summary: {
@@ -85,6 +86,11 @@ export const meta = {
code: 'NO_SUCH_FILE',
id: 'cfc23c7c-3887-490e-af30-0ed576703c82'
},
nameAlreadyExists: {
message: 'Specified name already exists.',
code: 'NAME_ALREADY_EXISTS',
id: '2298a392-d4a1-44c5-9ebb-ac1aeaa5a9ab'
}
}
};
@@ -109,6 +115,16 @@ export default define(meta, async (ps, user) => {
}
}
await Pages.find({
id: Not(ps.pageId),
userId: user.id,
name: ps.name
}).then(result => {
if (result.length > 0) {
throw new ApiError(meta.errors.nameAlreadyExists);
}
});
await Pages.update(page.id, {
updatedAt: new Date(),
title: ps.title,

View File

@@ -74,23 +74,7 @@ export const meta = {
validator: $.optional.bool,
default: true,
desc: {
'ja-JP': '自分の行ったRenoteを含めるかどうか'
}
},
includeRenotedMyNotes: {
validator: $.optional.bool,
default: true,
desc: {
'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
}
},
includeLocalRenotes: {
validator: $.optional.bool,
default: true,
desc: {
'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
'ja-JP': 'Renoteを含めるかどうか'
}
},
@@ -166,10 +150,8 @@ export default define(meta, async (ps, me) => {
}));
if (ps.excludeNsfw) {
// v11 TODO
/*query['_files.isSensitive'] = {
$ne: true
};*/
query.andWhere('note.cw IS NULL');
query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)');
}
}
@@ -177,23 +159,15 @@ export default define(meta, async (ps, me) => {
query.andWhere('note.replyId IS NULL');
}
/* TODO
if (ps.includeMyRenotes === false) {
query.$and.push({
$or: [{
userId: { $ne: user.id }
}, {
renoteId: null
}, {
text: { $ne: null }
}, {
fileIds: { $ne: [] }
}, {
poll: { $ne: null }
}]
});
query.andWhere(new Brackets(qb => {
qb.orWhere('note.userId != :userId', { userId: user.id });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
*/
//#endregion

View File

@@ -6,7 +6,7 @@ import { name, schema } from '../schemas/test';
type TestLog = SchemaType<typeof schema>;
export default class TestChart extends Chart<TestLog> {
private total = 0;
public total = 0; // publicにするのはテストのため
constructor() {
super(name, schema);

View File

@@ -65,7 +65,7 @@ export default abstract class Chart<T extends Record<string, any>> {
public schema: Schema;
protected repository: Repository<Log>;
protected abstract genNewLog(latest: T): DeepPartial<T>;
protected abstract async fetchActual(group?: string): Promise<DeepPartial<T>>;
protected abstract async fetchActual(group: string | null): Promise<DeepPartial<T>>;
@autobind
private static convertSchemaToFlatColumnDefinitions(schema: Schema) {
@@ -341,6 +341,24 @@ export default abstract class Chart<T extends Record<string, any>> {
]);
}
@autobind
public async resync(group: string | null = null): Promise<any> {
const data = await this.fetchActual(group);
const update = async (log: Log) => {
await this.repository.createQueryBuilder()
.update()
.set(Chart.convertObjectToFlattenColumns(data))
.where('id = :id', { id: log.id })
.execute();
};
return Promise.all([
this.getCurrentLog('day', group).then(log => update(log)),
this.getCurrentLog('hour', group).then(log => update(log)),
]);
}
@autobind
protected async inc(inc: DeepPartial<T>, group: string | null = null): Promise<void> {
await this.commit(Chart.convertQuery(inc as any), group);

View File

@@ -6,6 +6,9 @@ import { program } from '../argv';
import { getRepository } from 'typeorm';
import { Log } from '../models/entities/log';
import { genId } from '../misc/gen-id';
import config from '../config';
const SyslogPro = require('syslog-pro');
type Domain = {
name: string;
@@ -18,6 +21,7 @@ export default class Logger {
private domain: Domain;
private parentLogger: Logger | null = null;
private store: boolean;
private syslogClient: any | null = null;
constructor(domain: string, color?: string, store = true) {
this.domain = {
@@ -25,6 +29,20 @@ export default class Logger {
color: color,
};
this.store = store;
if (config.syslog) {
this.syslogClient = new SyslogPro.RFC5424({
applacationName: 'Misskey',
timestamp: true,
encludeStructuredData: true,
color: true,
extendedColor: true,
server: {
target: config.syslog.host,
port: config.syslog.port,
}
});
}
}
public createSubLogger(domain: string, color?: string, store = true): Logger {
@@ -66,17 +84,29 @@ export default class Logger {
console.log(important ? chalk.bold(log) : log);
if (store) {
const Logs = getRepository(Log);
Logs.insert({
id: genId(),
createdAt: new Date(),
machine: os.hostname(),
worker: worker.toString(),
domain: [this.domain].concat(subDomains).map(d => d.name),
level: level,
message: message,
data: data,
} as Log);
if (this.syslogClient) {
const send =
level === 'error' ? this.syslogClient.error :
level === 'warning' ? this.syslogClient.warning :
level === 'success' ? this.syslogClient.info :
level === 'debug' ? this.syslogClient.info :
level === 'info' ? this.syslogClient.info :
null as never;
send(message);
} else {
const Logs = getRepository(Log);
Logs.insert({
id: genId(),
createdAt: new Date(),
machine: os.hostname(),
worker: worker.toString(),
domain: [this.domain].concat(subDomains).map(d => d.name),
level: level,
message: message,
data: data,
} as Log);
}
}
}

View File

@@ -1,6 +1,7 @@
import * as nodemailer from 'nodemailer';
import { fetchMeta } from '../misc/fetch-meta';
import Logger from './logger';
import config from '../config';
export const logger = new Logger('email');
@@ -14,6 +15,7 @@ export async function sendEmail(to: string, subject: string, text: string) {
port: meta.smtpPort,
secure: meta.smtpSecure,
ignoreTLS: !enableAuth,
proxy: config.proxySmtp,
auth: enableAuth ? {
user: meta.smtpUser,
pass: meta.smtpPass

View File

@@ -2,10 +2,10 @@
* Tests of API (visibility)
*
* How to run the tests:
* > mocha test/api-visibility.ts --require ts-node/register
* > npx mocha test/api-visibility.ts --require ts-node/register
*
* To specify test:
* > mocha test/api-visibility.ts --require ts-node/register -g 'test name'
* > npx mocha test/api-visibility.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of API
*
* How to run the tests:
* > mocha test/api.ts --require ts-node/register
* > npx mocha test/api.ts --require ts-node/register
*
* To specify test:
* > mocha test/api.ts --require ts-node/register -g 'test name'
* > npx mocha test/api.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of chart engine
*
* How to run the tests:
* > mocha test/chart.ts --require ts-node/register
* > npx mocha test/chart.ts --require ts-node/register
*
* To specify test:
* > mocha test/chart.ts --require ts-node/register -g 'test name'
* > npx mocha test/chart.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true
@@ -320,4 +320,60 @@ describe('Chart', () => {
});
}));
});
describe('Resync', () => {
it('Can resync', async(async () => {
testChart.total = 1;
await testChart.resync();
const chartHours = await testChart.getChart('hour', 3);
const chartDays = await testChart.getChart('day', 3);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [1, 0, 0]
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [0, 0, 0],
total: [1, 0, 0]
},
});
}));
it('Can resync (2)', async(async () => {
await testChart.increment();
clock.tick('01:00:00');
testChart.total = 100;
await testChart.resync();
const chartHours = await testChart.getChart('hour', 3);
const chartDays = await testChart.getChart('day', 3);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [0, 1, 0],
total: [100, 1, 0]
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [100, 0, 0]
},
});
}));
});
});

View File

@@ -2,10 +2,10 @@
* Tests of MFM
*
* How to run the tests:
* > mocha test/mfm.ts --require ts-node/register
* > npx mocha test/mfm.ts --require ts-node/register
*
* To specify test:
* > mocha test/mfm.ts --require ts-node/register -g 'test name'
* > npx mocha test/mfm.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of mute
*
* How to run the tests:
* > mocha test/mute.ts --require ts-node/register
* > npx mocha test/mute.ts --require ts-node/register
*
* To specify test:
* > mocha test/mute.ts --require ts-node/register -g 'test name'
* > npx mocha test/mute.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of Note
*
* How to run the tests:
* > mocha test/note.ts --require ts-node/register
* > npx mocha test/note.ts --require ts-node/register
*
* To specify test:
* > mocha test/note.ts --require ts-node/register -g 'test name'
* > npx mocha test/note.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of Maybe
*
* How to run the tests:
* > mocha test/prelude/maybe.ts --require ts-node/register
* > npx mocha test/prelude/maybe.ts --require ts-node/register
*
* To specify test:
* > mocha test/prelude/maybe.ts --require ts-node/register -g 'test name'
* > npx mocha test/prelude/maybe.ts --require ts-node/register -g 'test name'
*/
import * as assert from 'assert';

View File

@@ -2,10 +2,10 @@
* Tests of MFM
*
* How to run the tests:
* > mocha test/reaction-lib.ts --require ts-node/register
* > npx mocha test/reaction-lib.ts --require ts-node/register
*
* To specify test:
* > mocha test/reaction-lib.ts --require ts-node/register -g 'test name'
* > npx mocha test/reaction-lib.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of streaming API
*
* How to run the tests:
* > mocha test/streaming.ts --require ts-node/register
* > npx mocha test/streaming.ts --require ts-node/register
*
* To specify test:
* > mocha test/streaming.ts --require ts-node/register -g 'test name'
* > npx mocha test/streaming.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

View File

@@ -2,10 +2,10 @@
* Tests of Note
*
* How to run the tests:
* > mocha test/user-notes.ts --require ts-node/register
* > npx mocha test/user-notes.ts --require ts-node/register
*
* To specify test:
* > mocha test/user-notes.ts --require ts-node/register -g 'test name'
* > npx mocha test/user-notes.ts --require ts-node/register -g 'test name'
*
* If the tests not start, try set following enviroment variables:
* TS_NODE_FILES=true and TS_NODE_TRANSPILE_ONLY=true

710
yarn.lock

File diff suppressed because it is too large Load Diff