Compare commits

...

39 Commits

Author SHA1 Message Date
796252357e Merge branch 'develop' 2019-05-10 17:33:21 +09:00
b0344d52e9 11.12.0 2019-05-10 17:32:57 +09:00
8e6da3a0d9 インスタンス運営者がピン留めユーザーを設定できるように
Related #4892
2019-05-10 17:30:28 +09:00
756e4eaeec テキストのリスト内で変数埋め込みできるように 2019-05-10 16:08:01 +09:00
3126d0730a MisskeyPagesで変数を並べ替えられるように 2019-05-10 16:04:32 +09:00
748e9f15df Add notes/unrenote API 2019-05-10 15:53:53 +09:00
7c714f5788 Improve MisskeyPages 2019-05-10 14:18:18 +09:00
d3c3ad839b Update ObjectStorage example (#4890) 2019-05-10 01:46:11 +09:00
168de3c316 Resolve #4870 2019-05-09 23:27:34 +09:00
9e20fc5c88 Validate Note on createNote (#4881) 2019-05-09 15:43:31 +09:00
1ff5151786 Fix: みつけるで人気のタグが表示されない (#4883) 2019-05-09 15:42:56 +09:00
a56738a331 Update README.md [AUTOGEN] (#4877) 2019-05-07 20:55:37 +09:00
2d04a3d6d2 Merge branch 'develop' 2019-05-07 18:56:33 +09:00
e0a55b9100 11.11.2 2019-05-07 18:56:19 +09:00
0a57eecb3a New Crowdin translations (#4812)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (German)

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

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

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

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

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

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

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

* 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 (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

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

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

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Norwegian)

* 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 (Chinese Simplified)

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

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Czech)

* 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 (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Norwegian)

* 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 (Czech)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Czech)

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

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

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

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)
2019-05-07 18:49:51 +09:00
51f98020f6 Fix #4874 2019-05-07 18:48:38 +09:00
70123805e1 Fix #4866 2019-05-07 18:23:12 +09:00
1448b11d00 Clean up 2019-05-07 18:20:18 +09:00
490b81ed05 Fix #4873 2019-05-07 18:14:37 +09:00
96f675abed Fix: IPv4 onlyホストからDualstackホストにAP deliverできない (#4872) 2019-05-07 17:49:25 +09:00
4cd451c613 2段階認証コードの入力フォームタイプの訂正 (#4869)
Fix #4849, Resolve #4848
2019-05-07 08:45:29 +09:00
187792dfc4 Fix bug 2019-05-06 20:09:13 +09:00
3dde561fe5 Resolve #4864 (#4865) 2019-05-06 17:28:55 +09:00
a75ec45172 Merge branch 'develop' 2019-05-05 20:36:25 +09:00
3732ddf75f 11.11.1 2019-05-05 20:36:11 +09:00
b8d8097734 Fix bug 2019-05-05 20:36:09 +09:00
1092532292 Merge branch 'develop' 2019-05-05 20:33:44 +09:00
8a79ba0e2b 11.11.0 2019-05-05 20:33:29 +09:00
ca2949fbb4 MisskeyPagesにリストから選択関数を追加 2019-05-05 20:31:15 +09:00
17b373ac07 ✌️ 2019-05-05 20:16:05 +09:00
7aa66f438f Resolve #4853 2019-05-05 20:12:35 +09:00
73641fd78d Fix #4852 2019-05-05 15:17:29 +09:00
64aac9d6ad Fix #4862 2019-05-05 15:12:25 +09:00
2be13736c8 Update user-profile.ts
#4809
2019-05-05 09:42:38 +09:00
ff4f5fec1d meidg (#4835) 2019-05-05 09:29:15 +09:00
7d64f8abe4 外部サービス連携後のPackedUserがその情報を持つように (#4850) 2019-05-05 09:28:55 +09:00
88e6929e9f 外部サービス連携ログインリンクにアイコン追加 (#4858) 2019-05-05 09:28:23 +09:00
5fb0a995dd 様々な修正 (#4859)
Typo, Redundant code, Syntax error の修正
2019-05-05 09:27:55 +09:00
58a04ce1a5 ログアウトの処理と外部サービス連携Viewがセッションクッキーを作らないように (#4856) 2019-05-05 04:04:30 +09:00
87 changed files with 1410 additions and 486 deletions

View File

@ -84,40 +84,54 @@ redis:
drive:
storage: 'fs'
# OR
# OR
# storage: 'minio'
# bucket:
# prefix:
# config:
# endPoint:
# port:
# useSSL:
# accessKey:
# secretKey:
#drive:
# storage: 'minio'
# bucket:
# prefix:
# config:
# endPoint:
# port:
# useSSL:
# accessKey:
# secretKey:
# S3 example
# storage: 'minio'
# bucket: bucket-name
# prefix: files
# config:
# endPoint: s3-us-west-2.amazonaws.com
# region: us-west-2
# useSSL: true
# accessKey: XXX
# secretKey: YYY
# S3/GCS example
#
# * Replace <endpoint> to
# S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
# GCS: use 'storage.googleapis.com'
#
# * Replace <region> to
# S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
# GCS: not needed (just delete the region line)
#
#drive:
# storage: 'minio'
# bucket: bucket-name
# prefix: files
# baseUrl: https://bucket-name.<endpoint>
# config:
# endPoint: <endpoint>
# region: <region>
# useSSL: true
# accessKey: XXX
# secretKey: YYY
# S3 example (with CDN, custom domain)
# storage: 'minio'
# bucket: drive.example.com
# prefix: files
# baseUrl: https://drive.example.com
# config:
# endPoint: s3-us-west-2.amazonaws.com
# region: us-west-2
# useSSL: true
# accessKey: XXX
# secretKey: YYY
# S3/GCS example (with CDN, custom domain)
#
#drive:
# storage: 'minio'
# bucket: drive.example.com
# prefix: files
# baseUrl: https://drive.example.com
# config:
# endPoint: <endpoint>
# region: <region>
# useSSL: true
# accessKey: XXX
# secretKey: YYY
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
@ -149,3 +163,6 @@ autoAdmin: true
# Clustering
#clusterLimit: 1
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4

View File

@ -73,6 +73,54 @@ mongodb:
8. master ブランチに戻す
9. enjoy
11.12.0 (2019/05/10)
--------------------
### 注意
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
### Improvements
* インスタンス運営者がおすすめアカウントを設定できるように
* MisskeyPagesでNAME環境変数がNULLにならないように
* MisskeyPagesにNULL環境変数を追加
* MisskeyPagesで変数を並べ替えられるように
* MisskeyPagesのテキストのリスト内で変数埋め込みできるように
* 自分の指定した投稿のRenoteを全て解除するAPIを追加
### Fixes
* Noteをpull取得した時にhost名がvalidateされていない問題を修正
* みつけるで人気のタグが表示されない問題を修正
### その他
* アカウントのisVerifiedフラグを廃止
11.11.2 (2019/05/07)
--------------------
### Fixes
* IPv4 onlyホストからDualstackホストにAP deliverできない問題を修正
* ストリーミングに接続するまでラグがある問題を修正
* 2段階認証のコードが0から始まる時正しく入力できない問題を修正
* ユーザーの更新日時が新しい順で更新日時がnullのユーザーが先頭に来る問題を修正
* 値選択時の問題を修正
* リバーシでマップの変更が反映されない問題を修正
* リバーシで対局終了直後に盤面を巻き戻してもすぐ最終ターンまでリセットされる問題を修正
11.11.1 (2019/05/05)
--------------------
### Fixes
* MisskeyPagesのリストから選択関数が使えない問題を修正
11.11.0 (2019/05/05)
--------------------
### Improvements
* MisskeyPagesにリストから選択関数を追加
* MisskeyPagesに確率を指定できるテキストランダム選択関数を追加
* 外部サービス連携ログインリンクにアイコン追加
### Fixes
* MisskeyPagesでifを入れ子にできなくなっていた問題を修正
* MisskeyPagesで数値入力を作成するとテキスト入力になる問題を修正
* 外部サービス連携に関する問題を修正
11.10.1 (2019/05/04)
--------------------
### Fixes

View File

@ -105,6 +105,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<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/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://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
<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>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
@ -112,6 +113,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
</tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</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=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
@ -127,7 +129,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
@ -138,9 +139,9 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
<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/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
@ -149,8 +150,8 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<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>
<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>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
@ -159,17 +160,18 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/Corset">CG</a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
</tr></table>
<table><tr>
<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://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/mastodon">Gargron</a></td>
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Fri, 03 May 2019 05:33:07 UTC
**Last updated:** Tue, 07 May 2019 11:55:07 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright

View File

@ -61,6 +61,7 @@ common:
month-and-day: "{day}. {month}."
trash: "Koš"
drive: "Disk"
pages: "Stránky"
messaging: "Konverzace"
home: "Domů"
deck: "Deck"
@ -71,12 +72,20 @@ common:
favorites: "Oblíbené"
permissions:
"read:account": "Zobrazit informace o účtu"
"write:account": "Narábět s účtem"
"read:blocks": "Prohlížet blokování"
"write:blocks": "Narábět s blokováním"
"read:drive": "Prohlížet Disk"
"write:drive": "Pracovat s Diskem"
"read:favorites": "Prohlížet oblíbené"
"write:favorites": "Narábět s oblíbeními"
"read:following": "Prohlížet následování"
"write:following": "Pracovat s následováním"
"read:messaging": "Prohlížet konverzaci"
"write:messaging": "Pracovat s konverzaci"
"read:mutes": "Prohlížet ztlumené"
"write:mutes": "Narábět s utíšeními"
"write:notes": "Narábět s poznámkami"
"read:notifications": "Prohlížet oznámení"
"write:notifications": "Pracovat s oznámeními"
"read:reactions": "Prohlížet reakce"
@ -377,6 +386,7 @@ common/views/components/theme.vue:
installed: "\"{}\" byl nainstalován"
create-a-theme: "Vytvořit motiv"
save-created-theme: "Uložit motiv"
text-color: "Barva textu"
base-theme: "Základní vzhled"
base-theme-light: "Světlý"
base-theme-dark: "Tmavý"
@ -454,6 +464,7 @@ common/views/components/user-menu.vue:
suspend: "Zmrazit"
common/views/components/poll.vue:
vote-count: "{} hlasů"
total-votes: "{} hlasů celkem"
vote: "Hlasovat"
show-result: "Podívat se na výsledky"
voted: "Už jste hlasovaly"
@ -464,10 +475,21 @@ common/views/components/poll.vue:
remaining-seconds: "zbývá {s} sekund"
common/views/components/poll-editor.vue:
no-only-one-choice: "Musíte vybrat alespoň dvě možnosti"
choice-n: "Volba {}"
remove: "Odstranit tuto možnost"
add: "+ Přidat možnost"
destroy: "Zahodit dotazník"
expiration: "Termín"
infinite: "Nekonečne"
at: "Výběr data a času"
deadline-date: "Termín ukončení"
interval: "Trvání"
second: "Sekunda"
minute: "Minuta"
hour: "Hodina"
day: "Ne"
common/views/components/reaction-picker.vue:
choose-reaction: "Vyberte svoji reakci"
common/views/components/emoji-picker.vue:
custom-emoji: "Emoji"
people: "Lidé"
@ -1198,3 +1220,42 @@ deck/deck.user-column.vue:
activity: "Aktivita"
dev/views/new-app.vue:
app-name-desc: "Jméno vaší aplikace"
pages:
title: "Titulek"
blocks:
post: "Formulář pro psaní"
_post:
text: "Obsah"
_textInput:
text: "Titulek"
_textareaInput:
text: "Titulek"
_numberInput:
text: "Titulek"
_switch:
text: "Titulek"
_counter:
text: "Titulek"
_button:
text: "Titulek"
_action:
_dialog:
content: "Obsah"
script:
categories:
random: "Náhodně"
list: "Seznamy"
blocks:
_join:
arg1: "Seznamy"
random: "Náhodně"
_randomPick:
arg1: "Seznamy"
_dailyRandomPick:
arg1: "Seznamy"
_seedRandomPick:
arg2: "Seznamy"
_pick:
arg1: "Seznamy"
types:
array: "Seznamy"

View File

@ -53,6 +53,7 @@ common:
month-and-day: "{day}/{month}"
trash: "Papierkorb"
drive: "Drive"
pages: "Seite"
messaging: "Unterhaltungen"
home: "Home"
deck: "Stapel"
@ -64,6 +65,7 @@ common:
permissions:
"read:account": "Accountinformationen anzeigen."
"write:account": "Accountinformationen bearbeiten."
"read:blocks": "Blöcke anzeigen"
"read:drive": "Dateien anzeigen"
"write:drive": "Dateien bearbeiten"
"read:favorites": "Favoriten anzeigen"
@ -718,25 +720,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "Hast Du einen Fehler gefunden oder Lust, diese Dokumentation zu verbessern?"
edit-this-page-on-github-link: "Seite auf GitHub bearbeiten!"
api:
entities:
properties: "Eigenschaften"
endpoints:
params: "Parameter"
no-params: "Keine Parameter."
res: "Antwort"
require-credential: "Dieser Endpunkt erfordert eine Authentifizierung."
require-permission: "Dieser Endpunkt erfordert die {permission} Berechtigung."
has-limit: "Es gibt eine Ratenbegrenzung."
duration-limit: "Es sind maximal {max} Anfragen pro {duration} Millisekunden möglich."
min-interval-limit: "Es ist nur eine Anfrage alle {interval} Millisekunden möglich."
show-src: "Quellcode anzeigen."
show-src-link: "Quellcode auf GitHub anzeigen"
generated: "Dieses Dokument wird automatisch anhand der API-Definition generiert."
props:
name: "Name"
type: "Typ"
description: "Beschreibung"
dev/views/index.vue:
manage-apps: "Anwendungen verwalten"
dev/views/apps.vue:
@ -753,3 +736,24 @@ dev/views/new-app.vue:
authority: "Berechtigungen"
authority-desc: "Nur die hier eingetragenen Berechtigungen, werden per API zur Verfügung stehen."
authority-warning: "Dies kann auch nach dem erstellen der Anwendung geändert werden, allerdings werden dann alle bisher generierten Token ungültig."
pages:
blocks:
post: "\"Neuer Beitrag\"-Formular"
script:
categories:
random: "Zufällige Auswahl"
list: "Listen"
blocks:
_join:
arg1: "Listen"
random: "Zufällige Auswahl"
_randomPick:
arg1: "Listen"
_dailyRandomPick:
arg1: "Listen"
_seedRandomPick:
arg2: "Listen"
_pick:
arg1: "Listen"
types:
array: "Listen"

View File

@ -34,6 +34,7 @@ common:
signup: "Sign up"
signout: "Logout"
reload-to-apply-the-setting: "You'll need to reload the page to reflect this setting. Do you want to reload it now?"
fetching-as-ap-object: "Inquiring to union"
got-it: "Got it!"
customization-tips:
title: "Customization tips"
@ -61,6 +62,7 @@ common:
month-and-day: "{month}/{day}"
trash: "Trash"
drive: "Drive"
pages: "Pages"
messaging: "Talk"
home: "Home"
deck: "Deck"
@ -1621,25 +1623,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "Found an error, or do you want to contribute to the documentation?"
edit-this-page-on-github-link: "Edit this page at GitHub!"
api:
entities:
properties: "Properties"
endpoints:
params: "Parameters"
no-params: "No parameter."
res: "Response"
require-credential: "This endpoint requires the authentication information."
require-permission: "This endpoint requires {permission} permission."
has-limit: "There is a rate limit."
duration-limit: "If you have sent your requests more than {max} times in {duration} milliseconds, you will be unable to send more requests."
min-interval-limit: "If {interval} milliseconds haven't passed since the last request, you can't send a request."
show-src: "You can view the source code for this endpoint."
show-src-link: "See the code on GitHub"
generated: "This document is generated by the API definition."
props:
name: "Name"
type: "Type"
description: "Description"
dev/views/index.vue:
manage-apps: "Manage apps"
dev/views/apps.vue:
@ -1648,13 +1631,252 @@ dev/views/apps.vue:
app-missing: "No apps"
dev/views/new-app.vue:
new-app: "New Application"
new-app-info: "You can also create an application with the API. (app/create)"
create-app: "Creating application"
app-name: "Application name"
app-name-placeholder: "ex) Misskey for iOS"
app-name-desc: "The name of your app"
app-overview: "Application summary"
app-overview-placeholder: " ex) Misskey iOS Client."
app-overview-desc: "A brief description, or an introduction of your app."
callback-url: "The callback URL (optional)"
callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
callback-url-desc: "The URL to redirect to after the user is authenticated via the authentication form."
authority: "Permissions"
authority-desc: "Only the functions requested here can be accessed via the API."
authority-warning: "You can change it even after creating the application, but if you give different permissions, all user keys associated at that time will be invalidated."
pages:
new-page: "Create a page"
edit-page: "Edit a page"
read-page: "Viewing the source"
page-created: "Created the page!"
page-updated: "Updated the page"
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-source: "View Source"
view-page: "View page"
inspector: "Inspector"
content: "Page block"
variables: "Variables"
more-details: "Description"
title: "Title"
url: "Page URL"
summary: "Summary of page"
align-center: "Center align"
font: "Font"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
set-eye-catching-image: "Set an eye-catching image"
remove-eye-catching-image: "Delete an eye-catching image"
choose-block: "Add a block"
select-type: "Select a type"
enter-variable-name: "Please choose a variable name"
the-variable-name-is-already-used: "This variable name is already used"
content-blocks: "Content"
input-blocks: "Input"
special-blocks: "Special"
post-from-post-form: "Post this content"
posted-from-post-form: "Posted!"
blocks:
text: "Text"
textarea: "Text area"
section: "Section"
image: "Images"
button: "Button"
if: "If"
_if:
variable: "Variables"
post: "Post form"
_post:
text: "Content"
textInput: "Text input"
_textInput:
name: "Variable name"
text: "Title"
default: "Default value"
textareaInput: "Multiple type text input"
_textareaInput:
name: "Variable name"
text: "Title"
default: "Default value"
numberInput: "Numeric input"
_numberInput:
name: "Variable name"
text: "Title"
default: "Default value"
switch: "Switch"
_switch:
name: "Variable name"
text: "Title"
default: "Default value"
counter: "Counter"
_counter:
name: "Variable name"
text: "Title"
inc: "Increase number"
_button:
text: "Title"
action: "Operation when the button pressed"
_action:
dialog: "Show a dialog"
_dialog:
content: "Content"
resetRandom: "Reset a random number"
script:
categories:
flow: "Control"
logical: "Logical operation"
operation: "Compute"
comparison: "Compare"
random: "Random"
value: "Value"
fn: "Function"
text: "Text operation"
convert: "Variable"
list: "Lists"
blocks:
text: "Text"
multiLineText: "Text (Multiple lines)"
textList: "List of text"
_textList:
info: "Separate each one with a newline"
strLen: "Length of text"
_strLen:
arg1: "Text"
strPick: "Extract character"
_strPick:
arg1: "Text"
arg2: "Position of character"
strReplace: "Replace string(s)"
_strReplace:
arg1: "Text"
arg2: "Before replacement"
arg3: "After replacement"
strReverse: "Flip text"
_strReverse:
arg1: "Text"
_join:
arg1: "Lists"
arg2: "Separator"
add: "+ Plus"
_add:
arg1: "A"
arg2: "B"
subtract: "- Minus"
_subtract:
arg1: "A"
arg2: "B"
multiply: "× Multiply"
_multiply:
arg1: "A"
arg2: "B"
divide: "÷ Divide"
_divide:
arg1: "A"
arg2: "B"
_remind:
arg1: "A"
arg2: "B"
eq: "A and B are equal"
_eq:
arg1: "A"
arg2: "B"
notEq: "A and B are different"
_notEq:
arg1: "A"
arg2: "B"
and: "A and B"
_and:
arg1: "A"
arg2: "B"
or: "A or B"
_or:
arg1: "A"
arg2: "B"
lt: "A is smaller than B"
_lt:
arg1: "A"
arg2: "B"
gt: "A is bigger than B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "A is smaller or same than B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: "A is bigger or same than B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Branch"
_if:
arg1: "If"
arg2: "then"
arg3: "else"
not: "denial"
_not:
arg1: "denial"
random: "Random"
_random:
arg1: "Probability"
rannum: "Random number"
_rannum:
arg1: "Minimum"
arg2: "Maximum"
randomPick: "Choose at random from the list"
_randomPick:
arg1: "Lists"
dailyRandom: "Random (Daily for each user)"
_dailyRandom:
arg1: "Probability"
dailyRannum: "Random number (Daily for each user)"
_dailyRannum:
arg1: "Minimum"
arg2: "Maximum"
dailyRandomPick: "Choose at random from the list (Daily for each user)"
_dailyRandomPick:
arg1: "Lists"
_seedRandom:
arg1: "Seed"
arg2: "Probability"
_seedRannum:
arg1: "Seed"
arg2: "Minimum"
arg3: "Maximum"
_seedRandomPick:
arg1: "Seed"
arg2: "Lists"
_DRPWPM:
arg1: "List of text"
_pick:
arg1: "Lists"
arg2: "Position"
number: "Number"
stringToNumber: "Text to number"
_stringToNumber:
arg1: "Text"
numberToString: "Number to text"
_numberToString:
arg1: "Number"
splitStrByLine: "Split the text by lines"
_splitStrByLine:
arg1: "Text"
ref: "Variables"
fn: "Function"
_fn:
slots: "Slots"
arg1: "Output"
for: "Repeat"
thereIsEmptySlot: "Slot {slot} is empty!"
types:
string: "Text"
number: "Number"
boolean: "Flag"
array: "Lists"
stringArray: "List of text"
emptySlot: "Empty slot"
enviromentVariables: "Environment variable"
pageVariables: "Page element"
argVariables: "Input slot"

View File

@ -969,3 +969,24 @@ deck:
rename: "Renombrar"
deck/deck.user-column.vue:
activity: "Actividad"
pages:
blocks:
post: "Formulario"
script:
categories:
random: "Aleatorio"
list: "Listas"
blocks:
_join:
arg1: "Listas"
random: "Aleatorio"
_randomPick:
arg1: "Listas"
_dailyRandomPick:
arg1: "Listas"
_seedRandomPick:
arg2: "Listas"
_pick:
arg1: "Listas"
types:
array: "Listas"

View File

@ -60,6 +60,7 @@ common:
month-and-day: "{day}-{month}"
trash: "Corbeille"
drive: "Drive"
pages: "Pages"
messaging: "Conversations"
home: "Principal"
deck: "Deck"
@ -1469,6 +1470,7 @@ mobile/views/pages/drive.vue:
contextmenu:
upload: "Téléverser un fichier"
create-folder: "Créer un dossier"
rename-folder: "Renommer le dossier"
mobile/views/pages/user-lists.vue:
title: "Listes"
mobile/views/pages/signup.vue:
@ -1570,25 +1572,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?"
edit-this-page-on-github-link: "Éditez cette page sur GitHub !"
api:
entities:
properties: "Propriétés"
endpoints:
params: "Paramètres"
no-params: "Aucun paramètre"
res: "Réponse"
require-credential: "Ce point de communication nécessite une authentification."
require-permission: "Ce point de communication nécessite la permission {permission}."
has-limit: "Il ya un taux limite."
duration-limit: "Si vous avez envoyé plus de {max} requêtes en {duration} millisecondes, vous ne serez pas en mesure d'envoyer d'autres requêtes."
min-interval-limit: "Vous ne pourrez pas effectuer une nouvelle requête si {interval} millisecondes ne se sont pas écoulées depuis la dernière demande."
show-src: "Vous pouvez voir le code source ce point de communication."
show-src-link: "Consulter le code sur GitHub"
generated: "Ce document est généré à partir de la définition de lAPI."
props:
name: "Nom"
type: "Type"
description: "Description"
dev/views/index.vue:
manage-apps: "Gestion des applications"
dev/views/apps.vue:
@ -1605,3 +1588,142 @@ dev/views/new-app.vue:
authority: "Autorisations "
authority-desc: "Sont accessibles via lAPI, uniquement les fonctionnalités demandées ici."
authority-warning: "Vous pouvez le changer même après avoir créé l'application, mais si vous attribuez une nouvelle permission, toutes les clés utilisateur associées seront dès lors invalides."
pages:
title: "Titre"
blocks:
text: "Texte"
textarea: "Zone de texte"
section: "Section"
image: "Images"
button: "Bouton"
if: "Si"
_if:
variable: "Variables"
post: "Champs de publication"
_post:
text: "Contenu"
_textInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
_textareaInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
_numberInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
switch: "Basculer"
_switch:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
counter: "Compteur"
_counter:
name: "Nom de la variable"
text: "Titre"
_button:
text: "Titre"
_action:
_dialog:
content: "Contenu"
script:
categories:
flow: "Contrôle"
logical: "Opération logique"
operation: "Calculer"
comparison: "Comparer"
random: "Aléatoire"
value: "Valeur"
fn: "Fonction"
list: "Listes"
blocks:
text: "Texte"
strLen: "Longueur du texte"
_strLen:
arg1: "Texte"
strPick: "Extraire un caractère"
_strPick:
arg1: "Texte"
_strReplace:
arg1: "Texte"
arg2: "Avant le remplacement"
arg3: "Après le remplacement"
strReverse: "Inverser le texte"
_strReverse:
arg1: "Texte"
_join:
arg1: "Listes"
arg2: "Séparateur"
add: "+ Plus"
_add:
arg1: "A"
arg2: "B"
subtract: "- Moins"
_subtract:
arg1: "A"
arg2: "B"
multiply: "× Multiplier par"
_multiply:
arg1: "A"
arg2: "B"
divide: "÷ Diviser par"
_divide:
arg1: "A"
arg2: "B"
_remind:
arg1: "A"
arg2: "B"
eq: "A et B sont équivalents"
_eq:
arg1: "A"
arg2: "B"
notEq: "A et B sont différents"
_notEq:
arg1: "A"
arg2: "B"
_and:
arg1: "A"
arg2: "B"
_or:
arg1: "A"
arg2: "B"
_lt:
arg1: "A"
arg2: "B"
_gt:
arg1: "A"
arg2: "B"
_ltEq:
arg1: "A"
arg2: "B"
_gtEq:
arg1: "A"
arg2: "B"
random: "Aléatoire"
_randomPick:
arg1: "Listes"
_dailyRandomPick:
arg1: "Listes"
_seedRandomPick:
arg2: "Listes"
_pick:
arg1: "Listes"
number: "Numérique"
_stringToNumber:
arg1: "Texte"
_numberToString:
arg1: "Numérique"
_splitStrByLine:
arg1: "Texte"
fn: "Fonction"
_fn:
arg1: "Sortie"
for: "Répéter"
types:
string: "Texte"
number: "Numérique"
array: "Listes"
stringArray: "Liste de texte"
enviromentVariables: "Variables d'environnement"

View File

@ -263,7 +263,6 @@ common:
update-available-title: "更新があります"
update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。"
my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
verified-user: "公式アカウント"
hide-password: "パスワードを隠す"
show-password: "パスワードを表示する"
@ -339,7 +338,7 @@ auth/views/index.vue:
sign-in: "サインインしてください"
common/views/pages/explore.vue:
verified-users: "公式アカウント"
pinned-users: "ピン留めされたユーザー"
popular-users: "人気のユーザー"
recently-updated-users: "最近投稿したユーザー"
recently-registered-users: "新規ユーザー"
@ -1264,7 +1263,7 @@ admin/views/instance.vue:
invite: "招待"
save: "保存"
saved: "保存しました"
user-recommendation-config: "おすすめユーザー"
pinned-users: "ピン留めユーザー"
email-config: "メールサーバーの設定"
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
enable-email: "メール配信を有効にする"
@ -1351,12 +1350,6 @@ admin/views/users.vue:
silence-confirm: "サイレンスしますか?"
unmake-silence: "サイレンスの解除"
unsilence-confirm: "サイレンスを解除しますか?"
verify: "公式アカウントにする"
verify-confirm: "公式アカウントにしますか?"
verified: "公式アカウントにしました"
unverify: "公式アカウントを解除する"
unverify-confirm: "公式アカウントを解除しますか?"
unverified: "公式アカウントを解除しました"
update-remote-user: "リモートユーザー情報の更新"
remote-user-updated: "リモートユーザー情報を更新しました"
users:
@ -1373,7 +1366,6 @@ admin/views/users.vue:
admin: "管理者"
moderator: "モデレーター"
adminOrModerator: "管理者+モデレーター"
verified: "公式アカウント"
silenced: "サイレンス済み"
suspended: "凍結済み"
origin:
@ -1865,8 +1857,8 @@ pages:
font: "フォント"
fontSerif: "セリフ"
fontSansSerif: "サンセリフ"
set-eye-catchig-image: "アイキャッチ画像を設定"
remove-eye-catchig-image: "アイキャッチ画像を削除"
set-eye-catching-image: "アイキャッチ画像を設定"
remove-eye-catching-image: "アイキャッチ画像を削除"
choose-block: "ブロックを追加"
select-type: "種類を選択"
enter-variable-name: "変数名を決めてください"
@ -1941,6 +1933,7 @@ pages:
fn: "関数"
text: "テキスト操作"
convert: "変換"
list: "リスト"
blocks:
text: "テキスト"
multiLineText: "テキスト(複数行)"
@ -2059,6 +2052,13 @@ pages:
_seedRandomPick:
arg1: "シード"
arg2: "リスト"
DRPWPM: "確率付きリストからランダムに選択 (ユーザーごとに日替わり)"
_DRPWPM:
arg1: "テキストのリスト"
pick: "リストから選択"
_pick:
arg1: "リスト"
arg2: "位置"
number: "数値"
stringToNumber: "テキストを数値に"
_stringToNumber:

View File

@ -1246,25 +1246,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "間違いや改善点を見つけましたか?"
edit-this-page-on-github-link: "このページをGitHubで編集"
api:
entities:
properties: "プロパティ"
endpoints:
params: "パラメータ"
no-params: "パラメータはありません"
res: "レスポンス"
require-credential: "このエンドポイントは認証情報が必須です。"
require-permission: "このエンドポイントは{permission}の権限を必要とします。"
has-limit: "レートリミットがあります。"
duration-limit: "直近{duration}ミリ秒の間のこのエンドポイントへのリクエスト数の合計が{max}を超える場合はリクエストできません。"
min-interval-limit: "前回のリクエストから{interval}ミリ秒経っていない場合はリクエストできません。"
show-src: "このエンドポイントのソースコードも閲覧できます。"
show-src-link: "コードをGitHubで見る"
generated: "このドキュメントはAPI定義に基づき自動生成されています。"
props:
name: "名前"
type: "型"
description: "説明"
dev/views/index.vue:
manage-apps: "アプリの管理"
dev/views/apps.vue:
@ -1281,3 +1262,25 @@ dev/views/new-app.vue:
authority: "権限"
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
pages:
blocks:
image: "画像"
post: "投稿フォーム"
script:
categories:
random: "いんじゃんほい"
list: "リスト"
blocks:
_join:
arg1: "リスト"
random: "いんじゃんほい"
_randomPick:
arg1: "リスト"
_dailyRandomPick:
arg1: "リスト"
_seedRandomPick:
arg2: "リスト"
_pick:
arg1: "リスト"
types:
array: "リスト"

View File

@ -34,6 +34,7 @@ common:
signup: "신규 등록"
signout: "로그아웃"
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
fetching-as-ap-object: "연합에서 조회 중"
got-it: "알겠습니다"
customization-tips:
title: "커스터마이징 도움말"
@ -61,6 +62,7 @@ common:
month-and-day: "{month}월 {day}일"
trash: "휴지통"
drive: "드라이브"
pages: "페이지"
messaging: "대화"
home: "홈"
deck: "덱"
@ -72,8 +74,23 @@ common:
permissions:
"read:account": "계정 정보 보기"
"write:account": "계정 정보 변경"
"read:blocks": "차단 보기"
"write:blocks": "차단 수정"
"read:drive": "드라이브 보기"
"write:drive": "드라이브 수정"
"read:favorites": "즐겨찾기 보기"
"write:favorites": "즐겨찾기 수정"
"read:following": "팔로우 정보 보기"
"write:following": "팔로잉, 팔로우 수정"
"read:messaging": "대화 보기"
"write:messaging": "대화 수정"
"read:mutes": "뮤트 보기"
"write:mutes": "뮤트 수정"
"write:notes": "글 작성, 삭제"
"read:notifications": "글 보기"
"write:notifications": "알림 수정"
"read:reactions": "리액션 보기"
"write:reactions": "리액션 수정"
"write:votes": "투표하기"
empty-timeline-info:
follow-users-to-make-your-timeline: "사용자를 팔로우하면 글이 타임라인에 표시됩니다."
@ -293,12 +310,12 @@ auth/views/form.vue:
accept: "접근 권한 허용"
auth/views/index.vue:
loading: "로드 중"
denied: "플리케이션의 연계를 취소하였습니다."
denied: "플리케이션의 연계를 취소하였습니다."
denied-paragraph: "이 앱이 당신의 계정에 액세스할 수 없습니다."
already-authorized: "이 앱은 이미 연결되어 있습니다."
allowed: "플리케이션의 연동을 허용하였습니다."
callback-url: "플리케이션으로 돌아갑니다."
please-go-back: "플리케이션으로 돌아가여 시도하여 주십시오."
allowed: "플리케이션의 연동을 허용하였습니다."
callback-url: "플리케이션으로 돌아갑니다."
please-go-back: "플리케이션으로 돌아가여 시도하여 주십시오."
error: "세션이 존재하지 않습니다."
sign-in: "로그인 해주시기 바랍니다"
common/views/pages/explore.vue:
@ -474,8 +491,12 @@ common/views/components/user-menu.vue:
mention: "멘션"
mute: "뮤트"
unmute: "뮤트 해제"
mute-confirm: "이 사용자를 뮤트하시겠습니까?"
unmute-confirm: "이 사용자를 뮤트 해제하시겠습니까?"
block: "차단"
unblock: "차단 해제"
block-confirm: "이 사용자를 차단하시겠습니까?"
unblock-confirm: "이 사용자를 차단 해제하시겠습니까?"
push-to-list: "리스트에 추가"
select-list: "리스트를 선택하여 주십시오"
report-abuse: "스팸 신고"
@ -483,8 +504,12 @@ common/views/components/user-menu.vue:
report-abuse-reported: "관리자에게 보고되었습니다. 협조해주셔서 감사합니다."
silence: "침묵"
unsilence: "침묵 해제"
silence-confirm: "이 사용자를 침묵하시겠습니까?"
unsilence-confirm: "이 사용자를 침묵 해제하시겠습니까?"
suspend: "정지"
unsuspend: "정지 해제"
suspend-confirm: "이 사용자를 정지하시겠습니까?"
unsuspend-confirm: "이 사용자를 정지 해제하시겠습니까?"
common/views/components/poll.vue:
vote-to: "\"{}\"에 투표하기"
vote-count: "{}표"
@ -668,6 +693,7 @@ common/views/components/user-list-editor.vue:
delete-are-you-sure: "리스트 \"$1\"을 삭제하시겠습니까?"
deleted: "삭제하였습니다"
common/views/components/user-lists.vue:
create-list: "리스트 만들기"
list-name: "리스트 이름"
common/views/widgets/broadcast.vue:
fetching: "확인중"
@ -946,7 +972,7 @@ common/views/components/api-settings.vue:
sending: "응답을 기다리는 중"
response: "결과"
desktop/views/components/settings.apps.vue:
no-apps: "연결된 플리케이션이 없습니다"
no-apps: "연결된 플리케이션이 없습니다"
common/views/components/drive-settings.vue:
max: "최대 용량"
in-use: "사용중"
@ -1194,7 +1220,9 @@ admin/views/users.vue:
unsuspend-confirm: "정지를 해제하시겠습니까?"
unsuspended: "정지를 해제하였습니다"
make-silence: "침묵"
silence-confirm: "침묵으로 설정합니까?"
unmake-silence: "침묵 해제"
unsilence-confirm: "침묵 해제하시겠습니까?"
verify: "공식 계정으로 설정"
verify-confirm: "공식 계정으로 설정하시겠습니까?"
verified: "공식 계정으로 설정하였습니다"
@ -1388,6 +1416,7 @@ desktop/views/widgets/polls.vue:
desktop/views/widgets/post-form.vue:
title: "글쓰기"
note: "글쓰기"
something-happened: "알 수 없는 문제로 글을 게시할 수 없습니다."
desktop/views/widgets/profile.vue:
update-banner: "클릭하여 배너 변경"
update-avatar: "클릭하여 아바타 변경"
@ -1410,6 +1439,7 @@ mobile/views/components/drive.vue:
here-is-root: "현재 경로는 루트 경로로 폴더가 아닙니다."
url-prompt: "업로드 하려는 파일의 URL"
uploading: "업로드를 요청하였습니다. 업로드가 완료될 때까지 시간이 소요될 수 있습니다."
folder-name-cannot-empty: "폴더 이름은 비워둘 수 없습니다."
mobile/views/components/drive-file-chooser.vue:
select-file: "파일 선택"
mobile/views/components/drive-folder-chooser.vue:
@ -1486,7 +1516,11 @@ mobile/views/components/ui.nav.vue:
mobile/views/pages/drive.vue:
contextmenu:
upload: "파일 업로드"
url-upload: "파일을 URL로부터 업로드"
create-folder: "폴더 만들기"
rename-folder: "폴더 이름 바꾸기"
move-folder: "이 폴더를 이동"
delete-folder: "이 폴더를 삭제"
mobile/views/pages/user-lists.vue:
title: "리스트"
mobile/views/pages/signup.vue:
@ -1589,25 +1623,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "틀린 점이나 개선할 점을 찾으셨나요?"
edit-this-page-on-github-link: "이 페이지를 GitHub에서 편집"
api:
entities:
properties: "프로퍼티"
endpoints:
params: "매개변수"
no-params: "매개변수가 없습니다"
res: "응답"
require-credential: "이 엔드포인트는 인증 정보가 필수적입니다."
require-permission: "이 엔드포인트는 {permission} 권한을 필요로 합니다."
has-limit: "Rate limit(요청 비율 제한)이 있습니다."
duration-limit: "최근 {duration} 밀리초 사이에 엔드포인트로의 요청 수의 합계가 {max}를 넘는 경우 요청이 불가능합니다."
min-interval-limit: "이전 요청으로부터 {interval} 밀리초가 지나지 않은 경우 요청할 수 없습니다."
show-src: "이 엔드포인트에 대한 소스코드를 확인할 수 있습니다."
show-src-link: "코드를 Github에서 보기"
generated: "이 문서는 API 정의를 기반으로 자동으로 생성됩니다."
props:
name: "이름"
type: "자료형"
description: "설명"
dev/views/index.vue:
manage-apps: "앱 관리"
dev/views/apps.vue:
@ -1615,12 +1630,64 @@ dev/views/apps.vue:
create-app: "앱 생성"
app-missing: "앱 없음"
dev/views/new-app.vue:
create-app: "플리케이션 생성"
app-name: "플리케이션 이름"
new-app: "새 애플리케이션"
new-app-info: "플리케이션은 API에서도 생성할 수 있습니다. (app/create)"
create-app: "애플리케이션 생성"
app-name: "애플리케이션 이름"
app-name-placeholder: "ex) Misskey for iOS"
app-name-desc: "앱의 이름."
app-overview: "앱 개요"
app-overview-placeholder: "ex) Misskey iOS 클라이언트."
app-overview-desc: "애플리케이션에 대한 간단한 설명이나 소개"
callback-url: "콜백 URL (옵션)"
callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
callback-url-desc: "사용자가 인증 폼에서 인증한 뒤 리다이렉트할 URL을 설정합니다."
authority: "권한"
authority-desc: "이곳에서 요청한 권한에 한정하여 API로 액세스할 수 있습니다."
authority-warning: "앱을 생성한 뒤에도 변경할 수 있지만, 새로운 권한을 설정하는 경우 그 시점부터 예전에 발급받았던 유저 키는 모두 무효화됩니다."
pages:
new-page: "페이지 만들기"
edit-page: "페이지 수정"
read-page: "소스 표시중"
page-created: "페이지를 만들었습니다"
page-updated: "페이지를 수정했습니다"
are-you-sure-delete: "이 페이지를 삭제하시겠습니까?"
title: "제목"
blocks:
image: "이미지"
post: "게시 양식"
_post:
text: "내용"
_textInput:
text: "제목"
_textareaInput:
text: "제목"
_numberInput:
text: "제목"
_switch:
text: "제목"
_counter:
text: "제목"
_button:
text: "제목"
_action:
_dialog:
content: "내용"
script:
categories:
random: "랜덤"
list: "리스트"
blocks:
_join:
arg1: "리스트"
random: "랜덤"
_randomPick:
arg1: "리스트"
_dailyRandomPick:
arg1: "리스트"
_seedRandomPick:
arg2: "리스트"
_pick:
arg1: "리스트"
types:
array: "리스트"

View File

@ -619,13 +619,22 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "Heb je een fout ontdekt of wil je bijdragen aan de documentatie? "
edit-this-page-on-github-link: "Bewerk deze pagina op GitHub!"
api:
entities:
properties: "Eigenschappen"
endpoints:
params: "Parameters"
res: "Antwoord"
props:
name: "Naam"
type: "Type"
description: "Omschrijving"
pages:
blocks:
image: "Afbeeldingen"
script:
categories:
list: "Lijsten"
blocks:
_join:
arg1: "Lijsten"
_randomPick:
arg1: "Lijsten"
_dailyRandomPick:
arg1: "Lijsten"
_seedRandomPick:
arg2: "Lijsten"
_pick:
arg1: "Lijsten"
types:
array: "Lijsten"

View File

@ -489,14 +489,24 @@ deck/deck.user-column.vue:
following: "Følger"
followers: "Følgere"
images: "Bilder"
docs:
api:
entities:
properties: "Egenskaper"
endpoints:
params: "Parametere"
res: "Respons"
props:
name: "Navn"
type: "Type"
description: "Beskrivelse"
pages:
blocks:
image: "Bilder"
script:
categories:
random: "Tilfeldig"
list: "Lister"
blocks:
_join:
arg1: "Lister"
random: "Tilfeldig"
_randomPick:
arg1: "Lister"
_dailyRandomPick:
arg1: "Lister"
_seedRandomPick:
arg2: "Lister"
_pick:
arg1: "Lister"
types:
array: "Lister"

View File

@ -54,6 +54,7 @@ common:
month-and-day: "{month}-{day}"
trash: "Kosz"
drive: "Dysk"
pages: "Strony"
messaging: "Rozmowy"
home: "Strona główna"
deck: "Tablice"
@ -127,6 +128,7 @@ common:
behavior: "Zachowanie"
fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół"
note-visibility: "Widoczność wpisów"
remember-note-visibility: "Zapamiętaj widoczność wpisów"
web-search-engine: "Wyszukiwarka internetowa"
line-width: "Szerokości linii"
line-width-thin: "Cienka"
@ -1190,23 +1192,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "Znalazłeś błąd lub chcesz pomóc w tworzeniu dokumentacji?"
edit-this-page-on-github-link: "Edytuj stronę na GitHubie!"
api:
entities:
properties: "Właściwości"
endpoints:
params: "Parametry"
no-params: "Brak parametrów."
res: "Odpowiedź"
require-credential: "Punkt końcowy wymaga informacji o uwierzytelnieniu."
require-permission: "Ten punkt końcowy wymaga uprawnienia {permission}."
has-limit: "Istnieje limit częstotliwości."
min-interval-limit: "Nie możesz wykonać żądania przed upłynięciem {interval} od ostatniego żądania."
show-src: "Możesz zobaczyć kod źródłowy tego punktu końcowego."
show-src-link: "Zobacz kod na GitHubie"
props:
name: "Nazwa"
type: "Rodzaj"
description: "Opis"
dev/views/index.vue:
manage-apps: "Zarządzaj aplikacjami"
dev/views/apps.vue:
@ -1215,3 +1200,38 @@ dev/views/apps.vue:
dev/views/new-app.vue:
app-name: "Nazwa Aplikacji"
authority: "Uprawnienia"
pages:
title: "Tytuł"
blocks:
image: "Zdjęcia"
post: "Formularz tworzenia"
_textInput:
text: "Tytuł"
_textareaInput:
text: "Tytuł"
_numberInput:
text: "Tytuł"
_switch:
text: "Tytuł"
_counter:
text: "Tytuł"
_button:
text: "Tytuł"
script:
categories:
random: "Losowy"
list: "Listy"
blocks:
_join:
arg1: "Listy"
random: "Losowy"
_randomPick:
arg1: "Listy"
_dailyRandomPick:
arg1: "Listy"
_seedRandomPick:
arg2: "Listy"
_pick:
arg1: "Listy"
types:
array: "Listy"

View File

@ -274,18 +274,9 @@ deck/deck.user-column.vue:
timeline: "Linha do tempo"
docs:
edit-this-page-on-github-link: "Edite esta página no GitHub!"
api:
entities:
properties: "Propriedades"
endpoints:
params: "Parâmetros"
no-params: "Sem parâmetros"
res: "Resposta"
show-src-link: "Veja o código no GitHub"
generated: "Este documento foi gerado pelas definições da API."
props:
name: "Nome"
type: "Tipo"
description: "Descrição"
dev/views/index.vue:
manage-apps: "Gerenciar aplicativos"
pages:
blocks:
image: "Imagens"
post: "Formulário de publicação"

View File

@ -146,3 +146,9 @@ mobile/views/components/sub-note-content.vue:
poll: "Голосования"
mobile/views/pages/widgets.vue:
customization-tips: "Советы по настройке"
pages:
script:
categories:
random: "Случайно"
blocks:
random: "Случайно"

View File

@ -62,6 +62,7 @@ common:
month-and-day: "{month}月 {day}日"
trash: "垃圾箱"
drive: "网盘"
pages: "页面"
messaging: "聊天"
home: "首页"
deck: "Deck"
@ -1622,25 +1623,6 @@ deck/deck.user-column.vue:
docs:
edit-this-page-on-github: "发现错误或想要为文档做出贡献?"
edit-this-page-on-github-link: "在GitHub上编辑这个页面。"
api:
entities:
properties: "属性"
endpoints:
params: "参数"
no-params: "没有参数"
res: "回应"
require-credential: "此端点需要身份验证信息。"
require-permission: "此端点需要{permission}权限。"
has-limit: "有一个速率限制。"
duration-limit: "如果您发送请求在{duration}毫秒内多于{max}次,则无法发送更多请求。"
min-interval-limit: "如果自上次请求后未传递{interval}毫秒,则无法发送请求。"
show-src: "您可以查看此端点的源代码。"
show-src-link: "查阅GitHub上的代码"
generated: "该文档由API定义生成。"
props:
name: "名称"
type: "型号"
description: "描述"
dev/views/index.vue:
manage-apps: "管理应用"
dev/views/apps.vue:
@ -1663,3 +1645,247 @@ dev/views/new-app.vue:
authority: "权限"
authority-desc: "只能通过API访问此处请求的功能。"
authority-warning: "您可以在创建应用程序后对其进行更改,但如果您授予不同的权限,则当时关联的所有用户密钥都将失效。"
pages:
new-page: "创建页面"
edit-page: "编辑页面"
read-page: "查看源"
page-created: "页面已创建"
page-updated: "页面已更新"
are-you-sure-delete: "是否删除此页面?"
page-deleted: "该页面已被删除。"
edit-this-page: "编辑此页面"
view-source: "查看源代码"
view-page: "查看页面"
inspector: "检查器"
content: "页面内容"
variables: "变量"
variables-info: "您可以使用变量创建动态页面。在文本中通过<b>{变量名}</b>的写法来嵌入变量值。例如在文本<b>Hello { thing } world!</b>中,如果变量(thing)的值为<b>ai</b>,那么该文本会成为<b>Hello ai world!</b>。"
variables-info2: "因为变量的计算(计算变量值)是从上到下执行的,所以不能在变量中引用下面的变量。例如从上到下依次定义了<b>ABC</b>3个变量那么<b>C</b>中可以引用<b>A</b>或<b>B</b>,但是<b>A</b>无法引用<b>B</b>或<b>C</b>。"
variables-info3: "为了接收来自用户的输入,页面上设有“用户输入”块,在“变量名称”中设置要在其中保存输入值的变量名(变量会自动创建)。您可以使用该变量执行操作以响应用户输入。"
variables-info4: "通过使用函数,您可以将数值计算过程组合成可重用的形式。要创建函数,需要创建一个“函数”类型的变量。你可以将函数设定为槽函数(参数)的格式槽函数的值可作为函数中的变量使用。另外AiScript标准中还有一些函数会将函数作为参数(称为高阶函数)。\n除了已经预先定义的函数外您也可以将它们设置为这些高阶函数的槽函数。"
more-details: "详细说明"
title: "标题"
url: "页面URL"
summary: "页面摘要"
align-center: "居中"
font: "字体"
fontSerif: "衬线字体"
fontSansSerif: "无衬线字体"
choose-block: "添加块"
select-type: "类型选择"
enter-variable-name: "请确定变量名"
the-variable-name-is-already-used: "变量名已使用"
content-blocks: "内容"
input-blocks: "输入"
special-blocks: "特殊"
post-from-post-form: "发布此内容"
posted-from-post-form: "已发布"
blocks:
text: "文本"
textarea: "文本区域"
section: "章节"
image: "图片"
button: "按钮"
if: "如果"
_if:
variable: "变量"
post: "投稿形式"
_post:
text: "内容"
textInput: "文本输入"
_textInput:
name: "变量名"
text: "标题"
default: "默认值"
textareaInput: "多行文本输入"
_textareaInput:
name: "变量名"
text: "标题"
default: "默认值"
numberInput: "数值输入"
_numberInput:
name: "变量名"
text: "标题"
default: "默认值"
switch: "开关"
_switch:
name: "变量名"
text: "标题"
default: "默认值"
counter: "计数器"
_counter:
name: "变量名"
text: "标题"
inc: "增加值"
_button:
text: "标题"
action: "按下按钮时的行为"
_action:
dialog: "显示对话框"
_dialog:
content: "内容"
resetRandom: "随机值重置"
script:
categories:
flow: "控制"
logical: "逻辑运算"
operation: "计算"
comparison: "比较"
random: "随机"
value: "值"
fn: "函数"
text: "文本操作"
list: "列表"
blocks:
text: "文本"
multiLineText: "文本 (多行)"
textList: "文本列表"
_textList:
info: "情使用换行符分隔每行"
strLen: "文本长度"
_strLen:
arg1: "文本"
strPick: "字符提取"
_strPick:
arg1: "文本"
arg2: "字符位置"
strReplace: "文本替换"
_strReplace:
arg1: "文本"
arg2: "替换之前"
arg3: "替换之后"
strReverse: "文本反向"
_strReverse:
arg1: "文本"
join: "合并文本"
_join:
arg1: "列表"
arg2: "分隔符"
add: "+ 加"
_add:
arg1: "A"
arg2: "B"
subtract: "- 减"
_subtract:
arg1: "A"
arg2: "B"
multiply: "× 乘"
_multiply:
arg1: "A"
arg2: "B"
divide: "÷ 除"
_divide:
arg1: "A"
arg2: "B"
remind: "÷ 取模"
_remind:
arg1: "A"
arg2: "B"
eq: "A和B相等"
_eq:
arg1: "A"
arg2: "B"
notEq: "A和B不等"
_notEq:
arg1: "A"
arg2: "B"
and: "A和B"
_and:
arg1: "A"
arg2: "B"
or: "A或B"
_or:
arg1: "A"
arg2: "B"
lt: "< A小于B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A大于B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A小于等于B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A大于等于B"
_gtEq:
arg1: "A"
arg2: "B"
if: "分支"
_if:
arg1: "如果"
arg2: "的话"
arg3: "否则"
random: "随机"
_random:
arg1: "概率"
rannum: "随机"
_rannum:
arg1: "最小"
arg2: "最大"
randomPick: "从列表中随机选择"
_randomPick:
arg1: "列表"
dailyRandom: "随机(每个用户每日)"
_dailyRandom:
arg1: "概率"
dailyRannum: "随机数(每个用户每日)"
_dailyRannum:
arg1: "最小"
arg2: "最大"
dailyRandomPick: "从列表中随机选择(每个用户每日)"
_dailyRandomPick:
arg1: "列表"
seedRandom: "随机 (种子)"
_seedRandom:
arg1: "种子"
arg2: "概率"
seedRannum: "随机数(种子)"
_seedRannum:
arg1: "种子"
arg2: "最小"
arg3: "最大"
seedRandomPick: "从列表中随机选择 (种子)"
_seedRandomPick:
arg1: "种子"
arg2: "列表"
_DRPWPM:
arg1: "文本列表"
pick: "从列表中选择"
_pick:
arg1: "列表"
arg2: "位置"
number: "数值"
stringToNumber: "文本到数字"
_stringToNumber:
arg1: "文本"
numberToString: "数字到文本"
_numberToString:
arg1: "数值"
splitStrByLine: "将文本按行拆分"
_splitStrByLine:
arg1: "文本"
ref: "变量"
fn: "函数"
_fn:
slots: "槽函数"
slots-info: "请使用换行符分隔每个槽函数"
arg1: "输出"
for: "重复"
_for:
arg1: "次数"
arg2: "处理"
typeError: "槽函数{slot}需要传入“{expect}”,但是实际传入为“{actual}”!"
thereIsEmptySlot: "槽函数{slot}为空!"
types:
string: "文本"
number: "数值"
boolean: "布尔值"
array: "列表"
stringArray: "文本列表"
emptySlot: "空白槽函数"
enviromentVariables: "环境变量"
pageVariables: "页面元素"
argVariables: "输入槽函数"

View File

@ -0,0 +1,13 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class PinnedUsers1557476068003 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedUsers" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedUsers"`);
}
}

View File

@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "11.10.1",
"version": "11.12.0",
"codename": "daybreak",
"repository": {
"type": "git",

View File

@ -82,6 +82,14 @@
</section>
</ui-card>
<ui-card>
<template #title>{{ $t('pinned-users') }}</template>
<section>
<ui-textarea v-model="pinnedUsers"></ui-textarea>
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
</section>
</ui-card>
<ui-card>
<template #title>{{ $t('invite') }}</template>
<section>
@ -190,6 +198,7 @@ export default Vue.extend({
enableServiceWorker: false,
swPublicKey: null,
swPrivateKey: null,
pinnedUsers: [],
faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt
};
},
@ -239,6 +248,7 @@ export default Vue.extend({
this.enableServiceWorker = meta.enableServiceWorker;
this.swPublicKey = meta.swPublickey;
this.swPrivateKey = meta.swPrivateKey;
this.pinnedUsers = meta.pinnedUsers.join('\n');
});
},
@ -297,7 +307,8 @@ export default Vue.extend({
smtpPass: this.smtpAuth ? this.smtpPass : '',
enableServiceWorker: this.enableServiceWorker,
swPublicKey: this.swPublicKey,
swPrivateKey: this.swPrivateKey
swPrivateKey: this.swPrivateKey,
pinnedUsers: this.pinnedUsers.split('\n')
}).then(() => {
this.$root.dialog({
type: 'success',

View File

@ -11,7 +11,6 @@
<span class="username">@{{ user | acct }}</span>
<span class="is-admin" v-if="user.isAdmin">admin</span>
<span class="is-moderator" v-if="user.isModerator">moderator</span>
<span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
<span class="is-silenced" v-if="user.isSilenced" :title="$t('@.silenced-user')"><fa :icon="faMicrophoneSlash"/></span>
<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
</header>
@ -77,7 +76,6 @@ export default Vue.extend({
background var(--noteHeaderAdminBg)
color var(--noteHeaderAdminFg)
> .is-verified
> .is-silenced
> .is-suspended
margin 0 0 0 .5em

View File

@ -12,10 +12,6 @@
<x-user :user='user'/>
<div class="actions">
<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
<ui-horizon-group>
<ui-button @click="verifyUser" :disabled="verifying"><fa :icon="faCertificate"/> {{ $t('verify') }}</ui-button>
<ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button>
</ui-horizon-group>
<ui-horizon-group>
<ui-button @click="silenceUser"><fa :icon="faMicrophoneSlash"/> {{ $t('make-silence') }}</ui-button>
<ui-button @click="unsilenceUser">{{ $t('unmake-silence') }}</ui-button>
@ -47,7 +43,6 @@
<option value="all">{{ $t('users.state.all') }}</option>
<option value="admin">{{ $t('users.state.admin') }}</option>
<option value="moderator">{{ $t('users.state.moderator') }}</option>
<option value="verified">{{ $t('users.state.verified') }}</option>
<option value="silenced">{{ $t('users.state.silenced') }}</option>
<option value="suspended">{{ $t('users.state.suspended') }}</option>
</ui-select>
@ -71,7 +66,7 @@
import Vue from 'vue';
import i18n from '../../i18n';
import parseAcct from "../../../../misc/acct/parse";
import { faCertificate, faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
import { faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
import XUser from './users.user.vue';
@ -84,8 +79,6 @@ export default Vue.extend({
return {
user: null,
target: null,
verifying: false,
unverifying: false,
suspending: false,
unsuspending: false,
sort: '+createdAt',
@ -95,7 +88,7 @@ export default Vue.extend({
offset: 0,
users: [],
existMore: false,
faTerminal, faCertificate, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
faTerminal, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
};
},
@ -181,56 +174,6 @@ export default Vue.extend({
});
},
async verifyUser() {
if (!await this.getConfirmed(this.$t('verify-confirm'))) return;
this.verifying = true;
const process = async () => {
await this.$root.api('admin/verify-user', { userId: this.user.id });
this.$root.dialog({
type: 'success',
text: this.$t('verified')
});
};
await process().catch(e => {
this.$root.dialog({
type: 'error',
text: e.toString()
});
});
this.verifying = false;
this.refreshUser();
},
async unverifyUser() {
if (!await this.getConfirmed(this.$t('unverify-confirm'))) return;
this.unverifying = true;
const process = async () => {
await this.$root.api('admin/unverify-user', { userId: this.user.id });
this.$root.dialog({
type: 'success',
text: this.$t('unverified')
});
};
await process().catch(e => {
this.$root.dialog({
type: 'error',
text: e.toString()
});
});
this.unverifying = false;
this.refreshUser();
},
async silenceUser() {
if (!await this.getConfirmed(this.$t('silence-confirm'))) return;

View File

@ -34,7 +34,7 @@ body
.peg
display block
position absolute
right 0px
right 0
width 100px
height 100%
box-shadow 0 0 10px var(--primary), 0 0 5px var(--primary)

View File

@ -98,7 +98,7 @@ export default Vue.extend({
margin 0 auto
text-align center
background #fff
box-shadow 0px 4px 16px rgba(#000, 0.2)
box-shadow 0 4px 16px rgba(#000, 0.2)
> .fetching
margin 0

View File

@ -52,7 +52,7 @@ function match(e: KeyboardEvent, patterns: action['patterns']): boolean {
pattern.ctrl == e.ctrlKey &&
pattern.shift == e.shiftKey &&
pattern.alt == e.altKey &&
e.metaKey == false
!e.metaKey
);
}

View File

@ -11,9 +11,9 @@ export default function(me, settings, note) {
return (
(!isMyNote && note.reply && includesMutedWords(note.reply.text)) ||
(!isMyNote && note.renote && includesMutedWords(note.renote.text)) ||
(settings.showMyRenotes === false && isMyNote && isPureRenote) ||
(settings.showRenotedMyNotes === false && isPureRenote && note.renote.userId == me.id) ||
(settings.showLocalRenotes === false && isPureRenote && note.renote.user.host == null) ||
(!settings.showMyRenotes && isMyNote && isPureRenote) ||
(!settings.showRenotedMyNotes && isPureRenote && note.renote.userId == me.id) ||
(!settings.showLocalRenotes && isPureRenote && note.renote.user.host == null) ||
(!isMyNote && includesMutedWords(note.text))
);
}

View File

@ -21,7 +21,7 @@ export default class Stream extends EventEmitter {
const user = os.store.state.i;
this.stream = new ReconnectingWebsocket(wsUrl + (user ? `?i=${user.token}` : ''));
this.stream = new ReconnectingWebsocket(wsUrl + (user ? `?i=${user.token}` : ''), '', { minReconnectionDelay: 1 }); // https://github.com/pladaria/reconnecting-websocket/issues/91
this.stream.addEventListener('open', this.onOpen);
this.stream.addEventListener('close', this.onClose);
this.stream.addEventListener('message', this.onMessage);

View File

@ -80,7 +80,7 @@ export default Vue.extend({
ms(): number {
return this.now.getMilliseconds() * this.smooth;
}
},
s(): number {
return this.now.getSeconds();
},

View File

@ -200,6 +200,7 @@ export default Vue.extend({
// 通信を取りこぼしてもいいように定期的にポーリングさせる
if (this.game.isStarted && !this.game.isEnded) {
this.pollingClock = setInterval(() => {
if (this.game.isEnded) return;
const crc32 = CRC32.str(this.logs.map(x => x.pos.toString()).join(''));
this.connection.send('check', {
crc32: crc32

View File

@ -230,7 +230,7 @@ export default Vue.extend({
this.game.map = Object.values(maps).find(x => x.name == this.mapName).data;
}
this.$forceUpdate();
this.updateSettings();
this.updateSettings('map');
},
onPixelClick(pos, pixel) {

View File

@ -202,7 +202,7 @@ export default Vue.extend({
left 0
z-index 1
width 100%
box-shadow 0 0px 2px rgba(#000, 0.2)
box-shadow 0 0 2px rgba(#000, 0.2)
> .form
background rgba(0, 0, 0, 0.02)

View File

@ -8,7 +8,6 @@
<span class="is-bot" v-if="note.user.isBot">bot</span>
<span class="is-cat" v-if="note.user.isCat">cat</span>
<span class="username"><mk-acct :user="note.user"/></span>
<span class="is-verified" v-if="note.user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
<div class="info">
<span class="app" v-if="note.app && !mini && $store.state.settings.showVia">via <b>{{ note.app.name }}</b></span>
<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
@ -95,10 +94,6 @@ export default Vue.extend({
color var(--noteHeaderAcct)
flex-shrink 2147483647
> .is-verified
margin 0 .5em 0 0
color #4dabf7
> .info
margin-left auto
font-size 0.9em

View File

@ -14,7 +14,7 @@ import XImage from './els/page-editor.el.image.vue';
import XButton from './els/page-editor.el.button.vue';
import XTextInput from './els/page-editor.el.text-input.vue';
import XTextareaInput from './els/page-editor.el.textarea-input.vue';
import XNumberInput from './els/page-editor.el.text-input.vue';
import XNumberInput from './els/page-editor.el.number-input.vue';
import XSwitch from './els/page-editor.el.switch.vue';
import XIf from './els/page-editor.el.if.vue';
import XPost from './els/page-editor.el.post.vue';

View File

@ -1,5 +1,5 @@
<template>
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn">
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable">
<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
<template #func>
<button @click="changeType()">
@ -93,6 +93,10 @@ export default Vue.extend({
fnSlots: {
required: false,
},
draggable: {
required: false,
default: false
}
},
data() {

View File

@ -36,10 +36,10 @@
</ui-select>
<div class="eyeCatch">
<ui-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catchig-image') }}</ui-button>
<ui-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catching-image') }}</ui-button>
<div v-else-if="eyeCatchingImage">
<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name"/>
<ui-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catchig-image') }}</ui-button>
<ui-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catching-image') }}</ui-button>
</div>
</div>
</template>
@ -53,20 +53,19 @@
<ui-container :body-togglable="true">
<template #header><fa :icon="faMagic"/> {{ $t('variables') }}</template>
<div class="qmuvgica">
<div class="variables" v-show="variables.length > 0">
<template v-for="variable in variables">
<x-variable
:value="variable"
:removable="true"
@input="v => updateVariable(v)"
@remove="() => removeVariable(variable)"
:key="variable.name"
:ai-script="aiScript"
:name="variable.name"
:title="variable.name"
/>
</template>
</div>
<x-draggable tag="div" class="variables" v-show="variables.length > 0" :list="variables" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
<x-variable v-for="variable in variables"
:value="variable"
:removable="true"
@input="v => updateVariable(v)"
@remove="() => removeVariable(variable)"
:key="variable.name"
:ai-script="aiScript"
:name="variable.name"
:title="variable.name"
:draggable="true"
/>
</x-draggable>
<ui-button @click="addVariable()" class="add" v-if="!readonly"><fa :icon="faPlus"/></ui-button>
@ -92,6 +91,7 @@
<script lang="ts">
import Vue from 'vue';
import * as XDraggable from 'vuedraggable';
import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../../../i18n';
@ -107,7 +107,7 @@ export default Vue.extend({
i18n: i18n('pages'),
components: {
XVariable, XBlocks
XDraggable, XVariable, XBlocks
},
props: {
@ -337,7 +337,7 @@ export default Vue.extend({
getScriptBlockList(type: string = null) {
const list = [];
const blocks = blockDefs.filter(block => type === null || block.out === null || block.out === type);
const blocks = blockDefs.filter(block => type === null || block.out === null || block.out === type || typeof block.out === 'number');
for (const block of blocks) {
const category = list.find(x => x.category === block.category);

View File

@ -54,7 +54,11 @@ export default Vue.extend({
},
mounted() {
document.cookie = `i=${this.$store.state.i.token}`;
if (!document.cookie.match(/i=(\w+)/)) {
document.cookie = `i=${this.$store.state.i.token}; path=/;` +
` domain=${document.location.hostname}; max-age=31536000;` +
(document.location.protocol.startsWith('https') ? ' secure' : '');
}
this.$watch('$store.state.i', () => {
if (this.$store.state.i.twitter) {
if (this.twitterForm) this.twitterForm.close();

View File

@ -273,7 +273,7 @@ export default Vue.extend({
import_() {
(this.$refs.file as any).click();
}
},
export_() {
const blob = new Blob([this.selectedThemeCode], {

View File

@ -10,14 +10,14 @@
<span>{{ $t('password') }}</span>
<template #prefix><fa icon="lock"/></template>
</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="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<span>{{ $t('@.2fa') }}</span>
<template #prefix><fa icon="gavel"/></template>
</ui-input>
<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`">{{ $t('signin-with-twitter') }}</a></p>
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`">{{ $t('signin-with-github') }}</a></p>
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`">{{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p>
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p>
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
</form>
</template>

View File

@ -322,7 +322,7 @@ root(fill)
> .value
display block
width 0%
width 0
height 100%
background transparent
border-radius 6px

View File

@ -5,10 +5,9 @@
<span class="label" ref="label"><slot name="label"></slot></span>
<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
<select ref="input"
:value="v"
v-model="v"
:required="required"
:disabled="disabled"
@input="$emit('input', $event.target.value)"
@focus="focused = true"
@blur="focused = false"
>
@ -56,20 +55,22 @@ export default Vue.extend({
},
data() {
return {
v: this.value,
focused: false
};
},
computed: {
v: {
get() {
return this.value;
},
set(v) {
this.$emit('input', v);
}
},
filled(): boolean {
return this.v != '' && this.v != null;
}
},
watch: {
value(v) {
this.v = v;
}
},
mounted() {
if (this.$refs.prefix) {
this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';

View File

@ -166,7 +166,7 @@ export default Vue.extend({
> .follow-button
position absolute
top 8px
right 0px
right 0
> .more
display block

View File

@ -160,7 +160,7 @@ export default Vue.extend({
this.$emit('top');
}
if (this.$store.state.settings.fetchOnScroll !== false) {
if (this.$store.state.settings.fetchOnScroll) {
const current = this.$refs.body.scrollTop + this.$refs.body.clientHeight;
if (current > this.$refs.body.scrollHeight - 1) this.$emit('bottom');
}

View File

@ -205,7 +205,7 @@ export default Vue.extend({
top -32px
left 0
right 0
width 0px
width 0
margin 0 auto
border-top solid 16px transparent
border-left solid 16px transparent

View File

@ -102,7 +102,7 @@ class Autocomplete {
}
}
if (isHashtag && opened == false) {
if (isHashtag && !opened) {
const hashtag = text.substr(hashtagIndex + 1);
if (!hashtag.includes(' ')) {
this.open('hashtag', hashtag);
@ -110,7 +110,7 @@ class Autocomplete {
}
}
if (isEmoji && opened == false) {
if (isEmoji && !opened) {
const emoji = text.substr(emojiIndex + 1);
if (!emoji.includes(' ')) {
this.open('emoji', emoji);

View File

@ -13,8 +13,8 @@
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
<div class="vxjfqztj">
<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.name}`" :key="'local:' + tag.name" class="local">{{ tag.name }}</router-link>
<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.name}`" :key="'remote:' + tag.name">{{ tag.name }}</router-link>
<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</router-link>
<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</router-link>
</div>
</ui-container>
@ -26,8 +26,8 @@
</mk-user-list>
<template v-if="tag == null">
<mk-user-list :make-promise="verifiedUsers">
<fa :icon="faBookmark" fixed-width/>{{ $t('verified-users') }}
<mk-user-list :make-promise="pinnedUsers">
<fa :icon="faBookmark" fixed-width/>{{ $t('pinned-users') }}
</mk-user-list>
<mk-user-list :make-promise="popularUsers">
<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
@ -60,12 +60,7 @@ export default Vue.extend({
data() {
return {
verifiedUsers: () => this.$root.api('users', {
state: 'verified',
origin: 'local',
sort: '+follower',
limit: 10
}),
pinnedUsers: () => this.$root.api('pinned-users'),
popularUsers: () => this.$root.api('users', {
state: 'alive',
origin: 'local',

View File

@ -47,7 +47,7 @@ class Script {
public interpolate(str: string) {
if (str == null) return null;
return str.replace(/\{(.+?)\}/g, match => {
return str.replace(/{(.+?)}/g, match => {
const v = this.vars[match.slice(1, -1).trim()];
return v == null ? 'NULL' : v.toString();
});

View File

@ -750,12 +750,17 @@ export default Vue.extend({
bottom 0
animation-delay -1.0s
@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
@keyframes sk-rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
} 50% {
}
50% {
transform: scale(1.0);
}
}

View File

@ -181,7 +181,7 @@ export default Vue.extend({
this.releaseQueue();
}
if (this.$store.state.settings.fetchOnScroll !== false) {
if (this.$store.state.settings.fetchOnScroll) {
const current = window.scrollY + window.innerHeight;
if (current > document.body.offsetHeight - 8) this.fetchMore();
}

View File

@ -377,7 +377,7 @@ export default Vue.extend({
}, err => {
this.$root.dialog({
type: 'error',
title: this.$t('error')
title: this.$t('error'),
text: err.message
});
}, {

View File

@ -480,7 +480,7 @@ export default Vue.extend({
&:focus
&:not([data-is-modal])
> .body
box-shadow 0 0 0px 1px var(--primaryAlpha05), 0 2px 12px 0 var(--desktopWindowShadow)
box-shadow 0 0 0 1px var(--primaryAlpha05), 0 2px 12px 0 var(--desktopWindowShadow)
> .handle
$size = 8px

View File

@ -352,7 +352,7 @@ export default Vue.extend({
padding 0 16px
line-height 48px
background var(--faceHeader)
box-shadow 0 1px 0px rgba(0, 0, 0, 0.1)
box-shadow 0 1px 0 rgba(0, 0, 0, 0.1)
& + div
max-height calc(100% - 48px)

View File

@ -505,7 +505,7 @@ class WindowSystem extends EventEmitter {
function urlBase64ToUint8Array(base64String: string): Uint8Array {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);

View File

@ -83,7 +83,7 @@ export default Vue.extend({
hierarchyFolders: [],
selectedFiles: [],
info: null,
connection: null
connection: null,
fetching: true,
fetchingMoreFiles: false,
@ -385,7 +385,7 @@ export default Vue.extend({
createFolder() {
this.$root.dialog({
title: this.$t('folder-name')
title: this.$t('folder-name'),
input: {
default: this.folder.name
}
@ -415,7 +415,7 @@ export default Vue.extend({
return;
}
this.$root.dialog({
title: this.$t('folder-name')
title: this.$t('folder-name'),
input: {
default: this.folder.name
}
@ -597,12 +597,17 @@ export default Vue.extend({
bottom 0
animation-delay -1.0s
@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
@keyframes sk-rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
} 50% {
}
50% {
transform: scale(1.0);
}
}

View File

@ -175,7 +175,7 @@ export default Vue.extend({
this.releaseQueue();
}
if (this.$store.state.settings.fetchOnScroll !== false) {
if (this.$store.state.settings.fetchOnScroll) {
// 親要素が display none だったら弾く
// https://github.com/syuilo/misskey/issues/1569
// http://d.hatena.ne.jp/favril/20091105/1257403319

View File

@ -115,7 +115,7 @@ export default Vue.extend({
},
onScroll() {
if (this.$store.state.settings.fetchOnScroll !== false) {
if (this.$store.state.settings.fetchOnScroll) {
// 親要素が display none だったら弾く
// https://github.com/syuilo/misskey/issues/1569
// http://d.hatena.ne.jp/favril/20091105/1257403319

View File

@ -295,7 +295,7 @@ export default Vue.extend({
}, err => {
this.$root.dialog({
type: 'error',
title: this.$t('error')
title: this.$t('error'),
text: err.message
});
}, {
@ -341,7 +341,7 @@ export default Vue.extend({
post() {
this.posting = true;
const viaMobile = this.$store.state.settings.disableViaMobile !== true;
const viaMobile = !this.$store.state.settings.disableViaMobile;
this.$root.api('notes/create', {
text: this.text == '' ? undefined : this.text,
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,

View File

@ -49,7 +49,7 @@ export default Vue.extend({
padding 0 8px
&.shadow
box-shadow 0 0px 8px rgba(0, 0, 0, 0.25)
box-shadow 0 0 8px rgba(0, 0, 0, 0.25)
&, *
user-select none

View File

@ -1,5 +1,5 @@
input
min-width 0px
min-width 0
input:not([type])
input[type='text']

View File

@ -126,7 +126,7 @@ export default (os: MiOS) => new Vuex.Store({
logout(ctx) {
ctx.commit('updateI', null);
document.cookie = 'i=;';
document.cookie = `i=; max-age=0; domain=${document.location.hostname}`;
localStorage.removeItem('i');
},

View File

@ -44,6 +44,8 @@ export type Source = {
clusterLimit?: number;
id: string;
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
};
/**

View File

@ -129,7 +129,7 @@ export const mfmLanguage = P.createLanguage({
mention: () => {
return P((input, i) => {
const text = input.substr(i);
const match = text.match(/^@\w([\w-]*\w)?(?:@[\w\.\-]+\w)?/);
const match = text.match(/^@\w([\w-]*\w)?(?:@[\w.\-]+\w)?/);
if (!match) return P.makeFailure(i, 'not a mention');
if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a mention');
return P.makeSuccess(i + match[0].length, match[0]);
@ -141,7 +141,7 @@ export const mfmLanguage = P.createLanguage({
},
hashtag: () => P((input, i) => {
const text = input.substr(i);
const match = text.match(/^#([^\s\.,!\?'"#:\/\[\]【】]+)/i);
const match = text.match(/^#([^\s.,!?'"#:\/\[\]【】]+)/i);
if (!match) return P.makeFailure(i, 'not a hashtag');
let hashtag = match[1];
hashtag = removeOrphanedBrackets(hashtag);

View File

@ -36,4 +36,4 @@ export function createTree(type: string, children: MfmForest, props: any): MfmTr
return T.createTree({ type, props }, children);
}
export const urlRegex = /^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/;
export const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;

View File

@ -31,7 +31,7 @@ export class ASEvaluator {
VERSION: opts.version,
URL: opts.page ? `${opts.url}/@${opts.page.user.username}/pages/${opts.page.name}` : '',
LOGIN: opts.visitor != null,
NAME: opts.visitor ? opts.visitor.name : '',
NAME: opts.visitor ? opts.visitor.name || opts.visitor.username : '',
USERNAME: opts.visitor ? opts.visitor.username : '',
USERID: opts.visitor ? opts.visitor.id : '',
NOTES_COUNT: opts.visitor ? opts.visitor.notesCount : 0,
@ -42,7 +42,8 @@ export class ASEvaluator {
MY_FOLLOWERS_COUNT: opts.user ? opts.user.followersCount : 0,
MY_FOLLOWING_COUNT: opts.user ? opts.user.followingCount : 0,
SEED: opts.randomSeed ? opts.randomSeed : '',
YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`,
NULL: null
};
}
@ -64,7 +65,7 @@ export class ASEvaluator {
@autobind
private interpolate(str: string, scope: Scope) {
return str.replace(/\{(.+?)\}/g, match => {
return str.replace(/{(.+?)}/g, match => {
const v = scope.getState(match.slice(1, -1).trim());
return v == null ? 'NULL' : v.toString();
});
@ -104,7 +105,7 @@ export class ASEvaluator {
}
if (block.type === 'textList') {
return block.value.trim().split('\n');
return this.interpolate(block.value || '', scope).trim().split('\n');
}
if (block.type === 'ref') {
@ -169,6 +170,7 @@ export class ASEvaluator {
stringToNumber: (a: string) => parseInt(a),
numberToString: (a: number) => a.toString(),
splitStrByLine: (a: string) => a.split('\n'),
pick: (list: any[], i: number) => list[i - 1],
random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
@ -178,6 +180,27 @@ export class ASEvaluator {
seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
DRPWPM: (list: string[]) => {
const xs = [];
let totalFactor = 0;
for (const x of list) {
const parts = x.split(' ');
const factor = parseInt(parts.pop()!, 10);
const text = parts.join(' ');
totalFactor += factor;
xs.push({ factor, text });
}
const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
let stackedFactor = 0;
for (const x of xs) {
if (r >= stackedFactor && r <= x.factor) {
return x.text;
} else {
stackedFactor += x.factor;
}
}
return xs[0].text;
},
};
const fnName = block.type;

View File

@ -23,6 +23,7 @@ import {
faSortNumericUp,
faExchangeAlt,
faRecycle,
faIndent,
} from '@fortawesome/free-solid-svg-icons';
import { faFlag } from '@fortawesome/free-regular-svg-icons';
@ -72,6 +73,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: faExchangeAlt, },
numberToString: { in: ['number'], out: 'string', category: 'convert', icon: faExchangeAlt, },
splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: faExchangeAlt, },
pick: { in: [null, 'number'], out: null, category: 'list', icon: faIndent, },
rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: faDice, },
@ -81,6 +83,7 @@ export const funcDefs: Record<string, { in: any[]; out: any; category: string; i
randomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: faDice, },
DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: faDice, }, // dailyRandomPickWithProbabilityMapping
};
export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = {
@ -124,6 +127,7 @@ export const envVarsDef: Record<string, Type> = {
MY_FOLLOWING_COUNT: 'number',
SEED: null,
YMD: 'string',
NULL: null,
};
export function isLiteralBlock(v: Block) {

View File

@ -1,6 +1,7 @@
import { ulid } from 'ulid';
import { genAid } from './id/aid';
import { genMeid } from './id/meid';
import { genMeidg } from './id/meidg';
import { genObjectId } from './id/object-id';
import config from '../config';
@ -12,6 +13,7 @@ export function genId(date?: Date): string {
switch (metohd) {
case 'aid': return genAid(date);
case 'meid': return genMeid(date);
case 'meidg': return genMeidg(date);
case 'ulid': return ulid(date.getTime());
case 'objectid': return genObjectId(date);
default: throw new Error('unknown id generation method');

28
src/misc/id/meidg.ts Normal file
View File

@ -0,0 +1,28 @@
const CHARS = '0123456789abcdef';
// 4bit Fixed hex value 'g'
// 44bit UNIX Time ms in Hex
// 48bit Random value in Hex
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
return time.toString(16).padStart(11, CHARS[0]);
}
function getRandom() {
let str = '';
for (let i = 0; i < 12; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genMeidg(date: Date): string {
return 'g' + getTime(date.getTime()) + getRandom();
}

View File

@ -69,6 +69,11 @@ export class Meta {
})
public langs: string[];
@Column('varchar', {
length: 256, array: true, default: '{}'
})
public pinnedUsers: string[];
@Column('varchar', {
length: 256, array: true, default: '{}'
})

View File

@ -26,7 +26,7 @@ export class UserProfile {
public birthday: string | null;
@Column('varchar', {
length: 1024, nullable: true,
length: 2048, nullable: true,
comment: 'The description (bio) of the User.'
})
public description: string | null;

View File

@ -157,11 +157,6 @@ export class User {
})
public isModerator: boolean;
@Column('boolean', {
default: false,
})
public isVerified: boolean;
@Column('varchar', {
length: 128, array: true, default: '{}'
})

View File

@ -87,7 +87,6 @@ export class UserRepository extends Repository<User> {
isAdmin: user.isAdmin || falsy,
isBot: user.isBot || falsy,
isCat: user.isCat || falsy,
isVerified: user.isVerified || falsy,
// カスタム絵文字添付
emojis: user.emojis.length > 0 ? Emojis.find({
@ -128,6 +127,19 @@ export class UserRepository extends Repository<User> {
detail: true
}),
twoFactorEnabled: profile!.twoFactorEnabled,
twitter: profile!.twitter ? {
id: profile!.twitterUserId,
screenName: profile!.twitterScreenName
} : null,
github: profile!.github ? {
id: profile!.githubId,
login: profile!.githubLogin
} : null,
discord: profile!.discord ? {
id: profile!.discordId,
username: profile!.discordUsername,
discriminator: profile!.discordDiscriminator
} : null,
} : {}),
...(opts.detail && meId === user.id ? {
@ -217,7 +229,7 @@ export class UserRepository extends Repository<User> {
}
public isValidBirthday(birthday: string): boolean {
return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday);
return typeof birthday == 'string' && /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.test(birthday);
}
//#endregion
}
@ -356,10 +368,6 @@ export const packedUserSchema = {
nullable: bool.false, optional: bool.true,
description: 'Whether this account is a moderator.'
},
isVerified: {
type: types.boolean,
nullable: bool.false, optional: bool.true,
},
isLocked: {
type: types.boolean,
nullable: bool.false, optional: bool.true,

View File

@ -25,6 +25,28 @@ import { ensure } from '../../../prelude/ensure';
const logger = apLogger;
export function validateNote(object: any, uri: string) {
const expectHost = extractDbHost(uri);
if (object == null) {
return new Error('invalid Note: object is null');
}
if (!['Note', 'Question', 'Article'].includes(object.type)) {
return new Error(`invalid Note: invalied object type ${object.type}`);
}
if (object.id && extractDbHost(object.id) !== expectHost) {
return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`);
}
if (object.attributedTo && extractDbHost(object.attributedTo) !== expectHost) {
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`);
}
return null;
}
/**
* Noteをフェッチします。
*
@ -59,8 +81,10 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
const object: any = await resolver.resolve(value);
if (!object || !['Note', 'Question', 'Article'].includes(object.type)) {
logger.error(`invalid note: ${value}`, {
const entryUri = value.id || value;
const err = validateNote(object, entryUri);
if (err) {
logger.error(`${err.message}`, {
resolver: {
history: resolver.getHistory()
},

View File

@ -101,11 +101,18 @@ export default async (user: ILocalUser, url: string, object: any) => {
* Resolve host (with cached, asynchrony)
*/
async function resolveAddr(domain: string) {
const af = config.outgoingAddressFamily || 'ipv4';
const useV4 = af == 'ipv4' || af == 'dual';
const useV6 = af == 'ipv6' || af == 'dual';
const promises = [];
if (!useV4 && !useV6) throw 'No usable address family available';
if (useV4) promises.push(resolveAddrInner(domain, { family: 4 }));
if (useV6) promises.push(resolveAddrInner(domain, { family: 6 }));
// v4/v6で先に取得できた方を採用する
return await promiseAny([
resolveAddrInner(domain, { family: 4 }),
resolveAddrInner(domain, { family: 6 })
]);
return await promiseAny(promises);
}
function resolveAddrInner(domain: string, options: IRunOptions = {}): Promise<string> {

View File

@ -36,7 +36,6 @@ export const meta = {
'admin',
'moderator',
'adminOrModerator',
'verified',
'silenced',
'suspended',
]),
@ -61,7 +60,6 @@ export default define(meta, async (ps, me) => {
case 'admin': query.where('user.isAdmin = TRUE'); break;
case 'moderator': query.where('user.isModerator = TRUE'); break;
case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
case 'verified': query.where('user.isVerified = TRUE'); break;
case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
case 'silenced': query.where('user.isSilenced = TRUE'); break;
case 'suspended': query.where('user.isSuspended = TRUE'); break;

View File

@ -1,38 +0,0 @@
import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { Users } from '../../../../models';
export const meta = {
desc: {
'ja-JP': '指定したユーザーの公式アカウントを解除します。',
'en-US': 'Mark a user as unverified.'
},
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
userId: {
validator: $.type(ID),
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to unverify'
}
},
}
};
export default define(meta, async (ps) => {
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await Users.update(user.id, {
isVerified: false
});
});

View File

@ -56,6 +56,13 @@ export const meta = {
}
},
pinnedUsers: {
validator: $.optional.nullable.arr($.str),
desc: {
'ja-JP': 'ピン留めユーザー'
}
},
hiddenTags: {
validator: $.optional.nullable.arr($.str),
desc: {
@ -353,6 +360,10 @@ export default define(meta, async (ps) => {
set.useStarForReactionFallback = ps.useStarForReactionFallback;
}
if (Array.isArray(ps.pinnedUsers)) {
set.pinnedUsers = ps.pinnedUsers;
}
if (Array.isArray(ps.hiddenTags)) {
set.hiddenTags = ps.hiddenTags;
}

View File

@ -1,38 +0,0 @@
import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { Users } from '../../../../models';
export const meta = {
desc: {
'ja-JP': '指定したユーザーを公式アカウントにします。',
'en-US': 'Mark a user as verified.'
},
tags: ['admin'],
requireCredential: true,
requireModerator: true,
params: {
userId: {
validator: $.type(ID),
desc: {
'ja-JP': '対象のユーザーID',
'en-US': 'The user ID which you want to verify'
}
},
}
};
export default define(meta, async (ps) => {
const user = await Users.findOne(ps.userId as string);
if (user == null) {
throw new Error('user not found');
}
await Users.update(user.id, {
isVerified: true
});
});

View File

@ -37,7 +37,7 @@ export const meta = {
},
type: {
validator: $.optional.str.match(/^[a-zA-Z\/\-\*]+$/)
validator: $.optional.str.match(/^[a-zA-Z\/\-*]+$/)
}
},

View File

@ -27,7 +27,7 @@ export const meta = {
},
type: {
validator: $.optional.str.match(/^[a-zA-Z\/\-\*]+$/)
validator: $.optional.str.match(/^[a-zA-Z\/\-*]+$/)
}
},

View File

@ -160,6 +160,7 @@ export default define(meta, async (ps, me) => {
if (me && (me.isAdmin || me.isModerator)) {
response.useStarForReactionFallback = instance.useStarForReactionFallback;
response.pinnedUsers = instance.pinnedUsers;
response.hiddenTags = instance.hiddenTags;
response.recaptchaSecretKey = instance.recaptchaSecretKey;
response.proxyAccount = instance.proxyAccount;

View File

@ -0,0 +1,60 @@
import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id';
import deleteNote from '../../../../services/note/delete';
import define from '../../define';
import * as ms from 'ms';
import { getNote } from '../../common/getters';
import { ApiError } from '../../error';
import { Notes } from '../../../../models';
export const meta = {
desc: {
'ja-JP': '指定した投稿のRenoteを解除します。',
},
tags: ['notes'],
requireCredential: true,
kind: 'write:notes',
limit: {
duration: ms('1hour'),
max: 300,
minInterval: ms('1sec')
},
params: {
noteId: {
validator: $.type(ID),
desc: {
'ja-JP': '対象の投稿のID',
'en-US': 'Target note ID.'
}
}
},
errors: {
noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: 'efd4a259-2442-496b-8dd7-b255aa1a160f'
},
}
};
export default define(meta, async (ps, user) => {
const note = await getNote(ps.noteId).catch(e => {
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
throw e;
});
const renotes = await Notes.find({
userId: user.id,
renoteId: note.id
});
for (const note of renotes) {
deleteNote(user, note);
}
});

View File

@ -0,0 +1,33 @@
import define from '../define';
import { Users } from '../../../models';
import { types, bool } from '../../../misc/schema';
import { fetchMeta } from '../../../misc/fetch-meta';
import parseAcct from '../../../misc/acct/parse';
import { User } from '../../../models/entities/user';
export const meta = {
tags: ['users'],
requireCredential: false,
params: {
},
res: {
type: types.array,
optional: bool.false, nullable: bool.false,
items: {
type: types.object,
optional: bool.false, nullable: bool.false,
ref: 'User',
}
},
};
export default define(meta, async (ps, me) => {
const meta = await fetchMeta();
const users = await Promise.all(meta.pinnedUsers.map(acct => Users.findOne(parseAcct(acct))));
return await Users.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true });
});

View File

@ -37,7 +37,6 @@ export const meta = {
'admin',
'moderator',
'adminOrModerator',
'verified',
'alive'
]),
default: 'all'
@ -71,7 +70,6 @@ export default define(meta, async (ps, me) => {
case 'admin': query.where('user.isAdmin = TRUE'); break;
case 'moderator': query.where('user.isModerator = TRUE'); break;
case 'adminOrModerator': query.where('user.isAdmin = TRUE OR isModerator = TRUE'); break;
case 'verified': query.where('user.isVerified = TRUE'); break;
case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break;
}
@ -85,8 +83,8 @@ export default define(meta, async (ps, me) => {
case '-follower': query.orderBy('user.followersCount', 'ASC'); break;
case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break;
case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break;
case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break;
case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break;
case '+updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'DESC'); break;
case '-updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'ASC'); break;
default: query.orderBy('user.id', 'ASC'); break;
}

View File

@ -52,7 +52,7 @@ for (const endpoint of endpoints) {
} else {
if (endpoint.name.includes('-')) {
// 後方互換性のため
router.post(`/${endpoint.name.replace(/\-/g, '_')}`, handler.bind(null, endpoint));
router.post(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint));
}
router.post(`/${endpoint.name}`, handler.bind(null, endpoint));
}

View File

@ -18,7 +18,7 @@ export function convertSchemaToOpenApiSchema(schema: Schema) {
const res: any = schema;
if (schema.type === 'object' && schema.properties) {
res.required = Object.entries(schema.properties).filter(([k, v]) => v.optional !== true).map(([k]) => k);
res.required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k);
for (const k of Object.keys(schema.properties)) {
res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]);

View File

@ -302,16 +302,13 @@ export default class extends Channel {
}
@autobind
private async check(crc32: string) {
private async check(crc32: string | number) {
const game = await ReversiGames.findOne(this.gameId!);
if (game == null) throw new Error('game not found');
if (!game.isStarted) return;
// 互換性のため
if (game.crc32 == null) return;
if (crc32 !== game.crc32) {
if (crc32.toString() !== game.crc32) {
this.send('rescue', await ReversiGames.pack(game, this.user));
}
}

View File

@ -34,7 +34,7 @@
"radix": false,
"ban-types": [
true,
"Object"
["Object", "Use {} instead."]
],
"ban": [
true,

View File

@ -95,7 +95,7 @@ module.exports = {
loader: 'css-loader'
}, postcss]
}, {
test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
test: /\.(eot|woff|woff2|svg|ttf)([?]?.*)$/,
loader: 'url-loader'
}, {
test: /\.json5$/,