Compare commits
157 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aa19767ebc | ||
![]() |
143190ab38 | ||
![]() |
baf94f86c4 | ||
![]() |
d36b129369 | ||
![]() |
f36d88246a | ||
![]() |
03f87140b3 | ||
![]() |
1dc07f6b72 | ||
![]() |
0aa0a9d24b | ||
![]() |
a9a93db2b4 | ||
![]() |
f187df3933 | ||
![]() |
8abe8042d7 | ||
![]() |
58fd46ff6f | ||
![]() |
fef8b662c1 | ||
![]() |
8de2f4ce76 | ||
![]() |
e5e344e1cd | ||
![]() |
e70d7edf41 | ||
![]() |
71d4d51fb2 | ||
![]() |
aaf38f1cbe | ||
![]() |
0e0d6692c0 | ||
![]() |
29f927fe72 | ||
![]() |
ee39d9594e | ||
![]() |
cefd2a4c54 | ||
![]() |
a08c20d9af | ||
![]() |
dc11f1afbf | ||
![]() |
b0f2b209a2 | ||
![]() |
a25fdfd519 | ||
![]() |
c1aa58596d | ||
![]() |
b6a3eb2445 | ||
![]() |
310f4d2edb | ||
![]() |
701fee3139 | ||
![]() |
593c2b9517 | ||
![]() |
96b2267cb8 | ||
![]() |
84730a071a | ||
![]() |
d0b0cf8dfb | ||
![]() |
749200d22b | ||
![]() |
a47baad943 | ||
![]() |
50abb51ece | ||
![]() |
1f890c5bed | ||
![]() |
97f23af86d | ||
![]() |
d77aa1f26a | ||
![]() |
0b075ad4e9 | ||
![]() |
423f776ed0 | ||
![]() |
084fd8152b | ||
![]() |
89d35c2e63 | ||
![]() |
be33581642 | ||
![]() |
2d6d9f30e1 | ||
![]() |
85721065fd | ||
![]() |
9d65768d4d | ||
![]() |
13f69e4291 | ||
![]() |
6a0affcec1 | ||
![]() |
ab6a84cd45 | ||
![]() |
ba93bf7478 | ||
![]() |
1c4e1af7c3 | ||
![]() |
a85f4c4fc4 | ||
![]() |
9d6c8806af | ||
![]() |
ff52ea2a7c | ||
![]() |
f247ee9dd3 | ||
![]() |
f4cec53ba1 | ||
![]() |
ad70b50fee | ||
![]() |
ea7b2b3141 | ||
![]() |
c2f932e28b | ||
![]() |
c637882578 | ||
![]() |
ef7221e39e | ||
![]() |
6b571a7799 | ||
![]() |
0638b6cb69 | ||
![]() |
a39c1706a1 | ||
![]() |
c741e27057 | ||
![]() |
ede854c215 | ||
![]() |
6a953b4d94 | ||
![]() |
1d763096c7 | ||
![]() |
630d873ec0 | ||
![]() |
d427957ea7 | ||
![]() |
2430cc0e2c | ||
![]() |
e57ee24864 | ||
![]() |
3bc05ab3f2 | ||
![]() |
4e50dcfa93 | ||
![]() |
fcf5531e5b | ||
![]() |
3bef69ee58 | ||
![]() |
311a4f28b0 | ||
![]() |
01e692b353 | ||
![]() |
3b445af6fc | ||
![]() |
1e43ece637 | ||
![]() |
6f1048c006 | ||
![]() |
d686e70f2b | ||
![]() |
70f524b82d | ||
![]() |
79c6475028 | ||
![]() |
7dee5309dc | ||
![]() |
958ec7b03f | ||
![]() |
9153434906 | ||
![]() |
3a08364c24 | ||
![]() |
a9beeab502 | ||
![]() |
38c901069a | ||
![]() |
3f7606060e | ||
![]() |
777f20e9be | ||
![]() |
8e39aecffe | ||
![]() |
9e1ab54097 | ||
![]() |
0f9e09f4bd | ||
![]() |
743ebc17b9 | ||
![]() |
9bc4af76b8 | ||
![]() |
46fa26426d | ||
![]() |
58d0dc1795 | ||
![]() |
bc11702f7d | ||
![]() |
6288de5813 | ||
![]() |
bbf59c7d9f | ||
![]() |
d7787bacf7 | ||
![]() |
23ae0515c4 | ||
![]() |
fe88b34b8a | ||
![]() |
74aa031a22 | ||
![]() |
6aeed212d9 | ||
![]() |
45b972c059 | ||
![]() |
7ecfc007a9 | ||
![]() |
fc78c75bab | ||
![]() |
59493a0cd9 | ||
![]() |
6060c6d56e | ||
![]() |
6cdbb27169 | ||
![]() |
ed8b073e54 | ||
![]() |
7dd193636c | ||
![]() |
01d018510c | ||
![]() |
1c273a0a75 | ||
![]() |
fa2c7658a0 | ||
![]() |
84ca3a7d45 | ||
![]() |
902c73e6ac | ||
![]() |
3b626f72e4 | ||
![]() |
f5ce137a6b | ||
![]() |
3ce9d12361 | ||
![]() |
2fe2f3b1eb | ||
![]() |
17b3ee41db | ||
![]() |
56d2a5d5d3 | ||
![]() |
3a17ff0983 | ||
![]() |
9a9270bbe9 | ||
![]() |
512eee4f51 | ||
![]() |
db01fa0eef | ||
![]() |
0c49a1ebd5 | ||
![]() |
636d6394e3 | ||
![]() |
c67c091b3a | ||
![]() |
333604898c | ||
![]() |
076ac3b614 | ||
![]() |
0e1468b159 | ||
![]() |
66409029e7 | ||
![]() |
8ec6b2ec11 | ||
![]() |
14736620ec | ||
![]() |
831ca53b63 | ||
![]() |
6138d46509 | ||
![]() |
c3003cb363 | ||
![]() |
4277e53433 | ||
![]() |
6516bd2ade | ||
![]() |
27d22f954a | ||
![]() |
88f5e8e8e2 | ||
![]() |
fd2ae6d3cf | ||
![]() |
238edd36f7 | ||
![]() |
5d847f9808 | ||
![]() |
ac914af9c3 | ||
![]() |
636f90ca0c | ||
![]() |
29469bb7c6 | ||
![]() |
4f043b1841 | ||
![]() |
85008303f5 | ||
![]() |
3432d6e615 |
@@ -116,8 +116,25 @@ autoAdmin: true
|
||||
# Whether disable HSTS
|
||||
#disableHsts: true
|
||||
|
||||
# Clustering
|
||||
# Number of worker processes
|
||||
#clusterLimit: 1
|
||||
|
||||
# Job concurrency per worker
|
||||
# deliverJobConcurrency: 128;
|
||||
# inboxJobConcurrency: 16;
|
||||
|
||||
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||
#outgoingAddressFamily: ipv4
|
||||
|
||||
# Syslog option
|
||||
#syslog:
|
||||
# host: localhost
|
||||
# port: 514
|
||||
|
||||
# Proxy for HTTP/HTTPS
|
||||
#proxy: http://127.0.0.1:3128
|
||||
|
||||
# Proxy for SMTP/SMTPS
|
||||
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
|
||||
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
|
||||
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
|
||||
|
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,3 +1,7 @@
|
||||
*.svg -diff -text
|
||||
*.psd -diff -text
|
||||
*.ai -diff -text
|
||||
*.mqo -diff -text
|
||||
*.glb -diff -text
|
||||
*.blend -diff -text
|
||||
*.afdesign -diff -text
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -30,3 +30,10 @@ api-docs.json
|
||||
.DS_Store
|
||||
/files
|
||||
ormconfig.json
|
||||
|
||||
# blender backups
|
||||
*.blend1
|
||||
*.blend2
|
||||
*.blend3
|
||||
*.blend4
|
||||
*.blend5
|
||||
|
@@ -1 +1 @@
|
||||
v12.6.0
|
||||
v12.9.1
|
||||
|
138
CHANGELOG.md
138
CHANGELOG.md
@@ -1,6 +1,144 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
11.31.4 (2019/09/03)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 誰がリアクションしたか見れるやつの表示を改善
|
||||
|
||||
11.31.3 (2019/09/03)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 誰がリアクションしたか見れるやつの表示を改善
|
||||
|
||||
11.31.2 (2019/09/03)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 誰がリアクションしたか見れるやつの表示を改善
|
||||
|
||||
11.31.1 (2019/09/03)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 誰がリアクションしたか見れるやつの表示を改善
|
||||
|
||||
11.31.0 (2019/09/02)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* Syslogサポート
|
||||
* チャートの同期機能をAPI経由で使えるように
|
||||
* SMTPでProxyを使用できるように
|
||||
* リアクションにホバーすることで誰がリアクションしたか見れるように
|
||||
* リプライ時、返信元のlocalOnly属性を引き継ぐように
|
||||
* 引用付き、ローカルのみなどの案内文にアイコン追加
|
||||
* AP deliver/inbox job の並列度を変更できるように
|
||||
* clusterLimitの既定値を1に
|
||||
* AP inbox ジョブの並列度を下げる
|
||||
* CWが付いた投稿はAP上でNote.sensitiveフラグを付けるように
|
||||
* メモウィジェットの内容を自動で保存するように
|
||||
* ページURLが他と重複してたらエラーを投げるように
|
||||
* ページURLが空の時エラーを投げるように
|
||||
* リアクションが解除されたときはアニメーションしないように
|
||||
* 設定の各セクションごとにURLを割り当てるように
|
||||
* 管理画面の各セクションごとにURLを割り当てるように
|
||||
|
||||
### 🐛Fixes
|
||||
* 未実装のTLのRenoteクエリを実装
|
||||
* タイムラインAPIのexcludeNsfwオプションを実装
|
||||
* ユーザーページの投稿一覧の私の投稿にRenoteが表示される問題を修正
|
||||
* meta APIでemojiプロパティに不要な情報が含まれているのを修正
|
||||
* モバイル版でドライブのファイルを削除したときの挙動がおかしい問題を修正
|
||||
* visiblity-chooserにlocalOnly属性が伝わらなかったのを修正
|
||||
* 言語指定したときコードブロックが表示されない問題を修正
|
||||
* トークのメッセージがはみ出す問題を修正
|
||||
* CWの中のサムネイルのサイズが変なのを少し修正
|
||||
* リアクションが初めて付いた時のエフェクトが消えている問題を修正
|
||||
* 無効になっているスイッチを操作できる問題を修正
|
||||
* Mキー連打で画面が真っ暗問題を修正
|
||||
* AmazonのURLプレビューが出来ない問題を修正
|
||||
* 表記ゆれを修正
|
||||
|
||||
11.30.0 (2019/08/24)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* Room: 家具をすべてしまうボタンを追加
|
||||
* Room: カップ麺追加
|
||||
* Room: ホログラフィックディスプレイ追加
|
||||
* Room: エナジードリンク追加
|
||||
* Room: ピンギンの色を変えられるように
|
||||
* Room: プレビューの見やすさを向上
|
||||
* Room保存時にダイアログを表示するように
|
||||
* Roomから移動するときに未保存ならば警告するように
|
||||
|
||||
### 🐛Fixes
|
||||
* MisskeyRoomからページを戻した時、テキスト入力画面で選択位置変更ができない問題を修正
|
||||
|
||||
11.29.0 (2019/08/19)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* Room: ソファ追加
|
||||
* Room: 螺旋階段追加
|
||||
* Room: ゴミ箱追加
|
||||
|
||||
### 🐛Fixes
|
||||
* Room: 部屋を離れても裏でレンダリングが続く問題を修正
|
||||
* Room: アバターのレンダリングを修正
|
||||
* Room: ライティングの調整
|
||||
|
||||
11.28.2 (2019/08/18)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 他人の部屋なのに部屋編集UIが表示されるのを修正
|
||||
* オブジェクトストレージを使用している場合Roomで画像を読み込めない問題を修正
|
||||
|
||||
11.28.1 (2019/08/18)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* オブジェクトストレージを使用している場合Roomで画像を読み込めない問題を修正
|
||||
* Roomで家具を移動など確定せずに「しまう」と部屋ごと消える問題を修正
|
||||
|
||||
11.28.0 (2019/08/18)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 自分の部屋を作れるように
|
||||
* Delキーを押して投稿を削除するときに確認ダイアログを出すように
|
||||
* Elasticsearchのインデックス名をconfigで変更できるように
|
||||
|
||||
### 🐛Fixes
|
||||
* コンテンツを遡ってる途中に新しいアイテムが先頭に追加されると上限に達している場合末尾のアイテムが削除される問題を修正
|
||||
* ユーザー名が突き抜けるのを修正
|
||||
* ユーザー一覧とかでサイレンス・凍結されたユーザーの状態が表示されてなかったのを修正
|
||||
* タイトルやアイコンがきちんと設定されないことがあるのを修正
|
||||
* ドライブアップロード直後に取得できるURLがoriginalじゃない問題を修正
|
||||
* リモートユーザー向けのNoteUnreadsレコードが作成される問題を修正
|
||||
* Hashtagがupdateできない問題を修正
|
||||
* ハッシュタグの更新がタグの数だけ並列で行われてDBを重くしてしまうことがあるのを修正
|
||||
|
||||
11.27.1 (2019/08/01)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* オブジェクトストレージに関する問題を修正
|
||||
|
||||
11.27.0 (2019/07/29)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 「削除して編集」機能を追加
|
||||
* HTTPリクエストのKeep-AliveとPrxoy対応(サーバーのパフォーマンス向上)
|
||||
* 通知を種類でフィルタリングして表示できるように
|
||||
* モバイルで通知ページを表示することができるように
|
||||
* 非ログイン時の警告処理
|
||||
|
||||
### 🐛Fixes
|
||||
* リモートの絵文字が更新されない問題を修正
|
||||
* リンクバリデーションリンクが一瞬表示されてしまう問題を修正
|
||||
* 選択していない状態でウィジェットが追加できる問題を修正
|
||||
|
||||
11.26.2 (2019/07/25)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* すでに使われたことのあるユーザー名を再度使えないように
|
||||
* モバイルのウィジェットページが常に i/update-client-setting を呼び続ける問題を修正
|
||||
* 投稿フォームのヘッダに添付ファイル数がちゃんと表示されない問題を修正
|
||||
|
||||
11.26.1 (2019/07/21)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
|
@@ -7,12 +7,18 @@ Feature suggestions and bug reports are filed in https://github.com/syuilo/missk
|
||||
* Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one.
|
||||
* If you have multiple independent issues, please submit them separately.
|
||||
|
||||
## Branches
|
||||
* **master** branch is tracking the latest release and used for production purposes.
|
||||
* **develop** branch is where we work for the next release.
|
||||
* **l10n_develop** branch is reserved for localization management.
|
||||
|
||||
## Localization (l10n)
|
||||
Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management.
|
||||
You can improve our translations with your Crowdin account.
|
||||
Changes you make in Crowdin will be merged into the develop branch by @syuilo.
|
||||
Your changes in Crowdin are automatically submitted as a PR (with the title "New Crowdin translations") to the repository.
|
||||
The owner [@syuilo](https://github.com/syuilo) merges the PR into the develop branch before the next release.
|
||||
|
||||
If you cannot find the language you want to contribute with, please open an issue.
|
||||
If your language is not listed in Crowdin, please open an issue.
|
||||
|
||||

|
||||
|
||||
@@ -32,8 +38,18 @@ Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/intr
|
||||
Misskey uses CircleCI for executing automated tests.
|
||||
Configuration files are located in [`/.circleci`](/.circleci).
|
||||
|
||||
## FAQ
|
||||
## Adding MisskeyRoom items
|
||||
* Use English for material, object and texture names.
|
||||
* Use meter for unit of length.
|
||||
* Your PR should include all source files (e.g. `.png`, `.blend`) of your models (for later editing).
|
||||
* Your PR must include the glTF binary files (`.glb`) of your models.
|
||||
* Add a locale key `room.furnitures.YOUR_ITEM` at [`/locales/ja-JP.yml`](/locales/ja-JP.yml).
|
||||
* Add a furniture definition at [`/src/client/app/common/scripts/room/furnitures.json5`](/src/client/app/common/scripts/room/furnitures.json5).
|
||||
|
||||
If you have no experience on 3D modeling, we suggest to use the free 3DCG software [Blender](https://www.blender.org/).
|
||||
You can find information on glTF 2.0 at [glTF 2.0 — Blender Manual]( https://docs.blender.org/manual/en/dev/addons/io_scene_gltf2.html).
|
||||
|
||||
## FAQ
|
||||
### How to resolve conflictions occurred at yarn.lock?
|
||||
|
||||
Just execute `yarn` to fix it.
|
||||
@@ -93,6 +109,8 @@ Good:
|
||||
if (foo) bar;
|
||||
```
|
||||
|
||||
Make sure that the condition and the body statement are on the same line.
|
||||
|
||||
### Do not use `==` when it can simply be replaced with `===`.
|
||||
🥰
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM node:12.6-alpine AS base
|
||||
FROM node:12.9.1-alpine AS base
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
|
18
README.md
18
README.md
@@ -104,21 +104,17 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<!-- PATREON_START -->
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20010324/b8af4bd31ae34fbf8806cc0e6228e400/1.png?token-time=2145916800&token-hash=iyiocfousNIUwASmatsIDq8EOsmLUdrQNkWyktHlmJg%3D" alt="Nemo" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" 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>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=20010324">Nemo</a></td>
|
||||
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=557245">mkatze</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>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
@@ -126,56 +122,50 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
|
||||
<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>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
|
||||
<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>
|
||||
</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/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/619ab87cc08448439222631ebb26802f/1.gif?token-time=2145916800&token-hash=o27K7M02s1z-LkDUEO5Oa7cu-GviRXeOXxryi4o_6VU%3D" alt="Atsuko Tominaga" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/10789744/97175095d8f04c0f86225ff47cb98d40/1.jpeg?token-time=2145916800&token-hash=l4AoMR7Nj7K4yAHrkrk2hAoggPkbSPm12m1nmbe9Pb8%3D" alt="Naoki Hirayama" 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/user?u=16900731">Atsuko Tominaga</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
|
||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
||||
<td><a href="https://www.patreon.com/noellabo">noellabo</a></td>
|
||||
<td><a href="https://www.patreon.com/Corset">CG</a></td>
|
||||
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
|
||||
<td><a href="https://www.patreon.com/spinlock">Naoki Hirayama</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/23932002" alt="nenohi" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=23932002">nenohi</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Fri, 19 Jul 2019 15:41:09 UTC
|
||||
**Last updated:** Sun, 01 Sep 2019 22:11:05 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
@@ -25,10 +25,9 @@ server {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 http2;
|
||||
listen [::]:443 http2;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name example.tld;
|
||||
ssl on;
|
||||
ssl_session_cache shared:ssl_session_cache:10m;
|
||||
|
||||
# To use Let's Encrypt certificate
|
||||
|
@@ -47,7 +47,11 @@ gulp.task('build:copy:views', () =>
|
||||
gulp.src('./src/server/web/views/**/*').pipe(gulp.dest('./built/server/web/views'))
|
||||
);
|
||||
|
||||
gulp.task('build:copy', gulp.parallel('build:copy:views', () =>
|
||||
gulp.task('build:copy:fonts', () =>
|
||||
gulp.src('./node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/client/assets/fonts/'))
|
||||
);
|
||||
|
||||
gulp.task('build:copy', gulp.parallel('build:copy:views', 'build:copy:fonts', () =>
|
||||
gulp.src([
|
||||
'./src/const.json',
|
||||
'./src/server/web/views/**/*',
|
||||
|
@@ -34,6 +34,19 @@ common:
|
||||
signup: "Registrovat"
|
||||
signout: "Odhlásit"
|
||||
reload-to-apply-the-setting: "Pro uplatnění tohoto nastavení musíte znovu načíst tuto stránku. Chcete ji načíst teď?"
|
||||
delete-confirm: "Opravdu chcete smazat tento příspěvek?"
|
||||
signin-required: "Přihlašte se, prosím"
|
||||
notification-type: "Typy oznámení"
|
||||
notification-types:
|
||||
all: "Všechny"
|
||||
pollVote: "Hlasy"
|
||||
follow: "Sledovaní"
|
||||
receiveFollowRequest: "Žádost o sledování"
|
||||
reply: "Odpovědi"
|
||||
quote: "Citace"
|
||||
renote: "Renotovat"
|
||||
mention: "Zmínky"
|
||||
reaction: "Reakce"
|
||||
got-it: "Rozumím!"
|
||||
customization-tips:
|
||||
title: "Tipy pro přizpůsobení"
|
||||
@@ -99,6 +112,7 @@ common:
|
||||
hide-contents: "Schovat obsah"
|
||||
reply-placeholder: "Odpovědět na tento příspěvek"
|
||||
quote-placeholder: "Citovat tento příspěvek"
|
||||
quote-attached: "Přiložit citaci"
|
||||
submit: "Odeslat"
|
||||
reply: "Odpovědět"
|
||||
renote: "Renotovat"
|
||||
@@ -108,12 +122,15 @@ common:
|
||||
create-poll: "Vytvořit anketu"
|
||||
text-remain: "zbývá ještě {} znaků"
|
||||
recent-tags: "Nejnovější"
|
||||
click-to-tagging: "Klikni pro otágování"
|
||||
visibility: "Viditelnost"
|
||||
geolocation-alert: "Vaše zařízení nedalo k dispozici lokaci"
|
||||
error: "Chyba"
|
||||
enter-username: "Zadejte své uživatelské jméno"
|
||||
specified-recipient: "Pro"
|
||||
add-visible-user: "Přidat uživatele"
|
||||
username-prompt: "Zadejte své uživatelské jméno"
|
||||
enter-file-name: "Upravit název souboru"
|
||||
weekday-short:
|
||||
sunday: "Ne"
|
||||
monday: "Po"
|
||||
@@ -180,6 +197,7 @@ common:
|
||||
remember-note-visibility: "Zapamatovat viditelnost příspěvků"
|
||||
web-search-engine: "Webové vyhledávače"
|
||||
web-search-engine-desc: "Například: https://www.google.com/?#q={{query}}"
|
||||
paste: "Vložit"
|
||||
keep-cw: "Zachovat varování o obsahu"
|
||||
keep-cw-desc: "Při odpovědi na příspěvek bude varování o obsahu nastaveno stejně jako původní příspěvek."
|
||||
i-like-sushi: "Mam radši sushi (než puding)"
|
||||
@@ -217,7 +235,7 @@ common:
|
||||
deck-column-width-wide: "Široké"
|
||||
use-shadow: "Používat v rozhraní stíny"
|
||||
rounded-corners: "Zakulatit rohy v rozhraní"
|
||||
circle-icons: "Používat kulaté ikony"
|
||||
circle-icons: "Používat kulaté avatary"
|
||||
contrasted-acct: "Přidat uživatelskému účtu kontrast"
|
||||
wallpaper: "Obrázek na pozadí"
|
||||
choose-wallpaper: "Zvolit pozadí"
|
||||
@@ -265,6 +283,15 @@ common:
|
||||
sync: "Synchronizace"
|
||||
save: "Uložit"
|
||||
saved: "Uloženo"
|
||||
room: "Místnost"
|
||||
_room:
|
||||
graphicsQuality: "Kvalita grafiky"
|
||||
_graphicsQuality:
|
||||
ultra: "Nejvyšší"
|
||||
high: "Vysoká"
|
||||
medium: "Střední"
|
||||
low: "Nízká"
|
||||
cheep: "Nejnižší"
|
||||
search: "Hledání"
|
||||
delete: "Odstranit"
|
||||
loading: "Načítám..."
|
||||
@@ -357,6 +384,7 @@ common/views/components/games/reversi/reversi.vue:
|
||||
cancel: "Zrušit"
|
||||
common/views/components/games/reversi/reversi.game.vue:
|
||||
surrender: "Vzdát se"
|
||||
surrendered: "Vzdaním se"
|
||||
looped-map: "Zacyklená mapa"
|
||||
common/views/components/games/reversi/reversi.index.vue:
|
||||
title: "Misskey Reversi"
|
||||
@@ -390,6 +418,7 @@ common/views/components/connect-failed.vue:
|
||||
title: "Nelze se připojit k serveru"
|
||||
description: "Nastal problém s Vaším připojením k internetu, nebo server není dostupný nebo zrovna probíhá údržba. Prosím {zkuste to znova} za pár minut."
|
||||
thanks: "Děkujeme že jste použili Misskey."
|
||||
troubleshoot: "Odstranění problémů"
|
||||
common/views/components/connect-failed.troubleshooter.vue:
|
||||
title: "Poradce při potížích"
|
||||
network: "Síťové připojení"
|
||||
@@ -398,6 +427,8 @@ common/views/components/connect-failed.troubleshooter.vue:
|
||||
checking-internet: "Ověřuji připojení k internetu."
|
||||
server: "Připojení k serveru"
|
||||
checking-server: "Spojuji se se serverem"
|
||||
finding: "Vyšetřování problému"
|
||||
no-network: "Žádné připojení k síti"
|
||||
no-network-desc: "Ujistěte se že jste připojeni k Internetu."
|
||||
no-internet: "Nejste připojeni k internetu"
|
||||
no-internet-desc: "Jste připojen k síti, ale zdá se že stále chybí připojení k Internetu. Prosím zkontrolujte Vaše připojení k Internetu."
|
||||
@@ -489,6 +520,7 @@ common/views/components/note-menu.vue:
|
||||
unpin: "Odepnout"
|
||||
delete: "Odstranit"
|
||||
delete-confirm: "Opravdu chcete smazat tento příspěvek?"
|
||||
delete-and-edit: "Smazat a upravit"
|
||||
remote: "Ukázat originální poznámku"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "Zmínění"
|
||||
@@ -921,6 +953,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
lists: "Seznamy"
|
||||
groups: "Skupiny"
|
||||
admin: "Administrace"
|
||||
room: "Místnost"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "Hry"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@@ -1264,6 +1297,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "Pro '{q}' nebyly nalezeny žádné příspěvky."
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Vybrat soubory"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Oznámení"
|
||||
mobile/views/pages/user/home.vue:
|
||||
activity: "Aktivita"
|
||||
frequently-replied-users: "Častá zmínění"
|
||||
@@ -1332,3 +1367,10 @@ pages:
|
||||
arg1: "Seznamy"
|
||||
types:
|
||||
array: "Seznamy"
|
||||
room:
|
||||
translate: "Přesunout"
|
||||
save: "Uložit"
|
||||
saved: "Uloženo"
|
||||
furnitures:
|
||||
moon: "Měsíc"
|
||||
bin: "Koš"
|
||||
|
@@ -35,6 +35,13 @@ common:
|
||||
signout: "Log ud"
|
||||
reload-to-apply-the-setting: "Denne indstilling slår først igennem, når du har genindlæst siden. Vil du genindlæse siden nu?"
|
||||
fetching-as-ap-object: "Tilladelse til sammenkobling"
|
||||
delete-confirm: "Er du helt sikker på, at du vil slette denne post?"
|
||||
notification-types:
|
||||
all: "Alle"
|
||||
follow: "Følger"
|
||||
reply: "Svar"
|
||||
renote: "Gen-postering"
|
||||
reaction: "Reaktion"
|
||||
got-it: "Det er OK"
|
||||
customization-tips:
|
||||
title: "Tips om tilpasning"
|
||||
@@ -206,7 +213,7 @@ common:
|
||||
deck-column-width-wide: "Bred"
|
||||
use-shadow: "Vis skygger"
|
||||
rounded-corners: "Vis afrundede hjørner"
|
||||
circle-icons: "Anvend cykliske ikoner"
|
||||
circle-icons: "Anvend cykliske avatar"
|
||||
contrasted-acct: "Tilføj kontrast til brugerkontoen"
|
||||
wallpaper: "Baggrundsbillede"
|
||||
choose-wallpaper: "Vælg en baggrund"
|
||||
@@ -673,7 +680,7 @@ common/views/components/profile-editor.vue:
|
||||
you-can-include-hashtags: "Du må gerne bruge hashtags i din profil beskrivelse"
|
||||
language: "Sprog"
|
||||
birthday: "Fødselsdag"
|
||||
avatar: "Ikon"
|
||||
avatar: "Avatar"
|
||||
banner: "Banner"
|
||||
is-cat: "Denne konto er en Kat"
|
||||
is-bot: "Denne konto er en Bot"
|
||||
@@ -1606,6 +1613,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "Ingen poster fundet for \"{q}\""
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Vælg fil(er)"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifikationer"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Logget ind som {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1901,3 +1910,10 @@ pages:
|
||||
enviromentVariables: "Miljø variabel"
|
||||
pageVariables: "Side element"
|
||||
argVariables: "Input slot"
|
||||
room:
|
||||
translate: "Flyt"
|
||||
save: "Gem"
|
||||
saved: "Gemt"
|
||||
furnitures:
|
||||
moon: "Måne"
|
||||
bin: "Skraldespand"
|
||||
|
@@ -35,6 +35,10 @@ common:
|
||||
signout: "Ausloggen"
|
||||
reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?"
|
||||
fetching-as-ap-object: "Hole Daten…"
|
||||
delete-confirm: "Diesen Beitrag löschen?"
|
||||
notification-types:
|
||||
reply: "Antworten"
|
||||
renote: "Anmerkung"
|
||||
got-it: "Verstanden!"
|
||||
customization-tips:
|
||||
title: "Anpassung-Tipps"
|
||||
@@ -203,7 +207,7 @@ common:
|
||||
deck-column-width-wide: "Sehr breit"
|
||||
use-shadow: "Nutze Schatten"
|
||||
rounded-corners: "Abgerundete Ecken"
|
||||
circle-icons: "Kreisförmige Icons"
|
||||
circle-icons: "Kreisförmige Avatar"
|
||||
contrasted-acct: "Nutzernamen kontrastreicher darstellen"
|
||||
wallpaper: "Hintergrund"
|
||||
choose-wallpaper: "Hintergrund auswählen"
|
||||
@@ -601,6 +605,7 @@ common/views/widgets/memo.vue:
|
||||
save: "Speichern"
|
||||
desktop:
|
||||
banner: "Banner"
|
||||
avatar: "Avatar"
|
||||
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Schwarz ... komplett"
|
||||
@@ -876,6 +881,8 @@ mobile/views/pages/note.vue:
|
||||
next: "Nächster Kommentar"
|
||||
mobile/views/pages/search.vue:
|
||||
search: "Suchen"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Benachrichtigungen"
|
||||
mobile/views/pages/user/home.vue:
|
||||
activity: "Aktivität"
|
||||
keywords: "Schlagwörter"
|
||||
@@ -940,3 +947,9 @@ pages:
|
||||
arg1: "Listen"
|
||||
types:
|
||||
array: "Listen"
|
||||
room:
|
||||
save: "Speichern"
|
||||
saved: "Gespeichert"
|
||||
furnitures:
|
||||
moon: "Mond"
|
||||
bin: "Papierkorb"
|
||||
|
@@ -30,12 +30,25 @@ common:
|
||||
customize-home: "Customize home layout"
|
||||
featured-notes: "Featured notes"
|
||||
dark-mode: "Dark Mode"
|
||||
signin: "Log In"
|
||||
signin: "Login"
|
||||
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"
|
||||
fetching-as-ap-object: "Inquiring to fediverse"
|
||||
unfollow-confirm: "Do you want to unfollow {name}?"
|
||||
delete-confirm: "Are you sure you want to delete this post?"
|
||||
signin-required: "Please login"
|
||||
notification-type: "Notification Type"
|
||||
notification-types:
|
||||
all: "All"
|
||||
pollVote: "Votes"
|
||||
follow: "Following"
|
||||
receiveFollowRequest: "Follow requests"
|
||||
reply: "Reply"
|
||||
quote: "Quote"
|
||||
renote: "Renote"
|
||||
mention: "Mentions"
|
||||
reaction: "Reaction"
|
||||
got-it: "Got it!"
|
||||
customization-tips:
|
||||
title: "Customization tips"
|
||||
@@ -120,6 +133,7 @@ common:
|
||||
geolocation-alert: "Your device does not provide location services"
|
||||
error: "Error"
|
||||
enter-username: "Please enter username"
|
||||
specified-recipient: "Recipient"
|
||||
add-visible-user: "Add a user"
|
||||
cw-placeholder: "Comments for the post (optional)"
|
||||
username-prompt: "Please enter username"
|
||||
@@ -202,6 +216,7 @@ common:
|
||||
use-avatar-reversi-stones: "Use avatar as a stone in reversi"
|
||||
disable-animated-mfm: "Disable animated texts in a post"
|
||||
disable-showing-animated-images: "Do not play animated images"
|
||||
enable-quick-notification-view: "Enable Quick Notification View"
|
||||
suggest-recent-hashtags: "Show recent popular hashtags on the post form"
|
||||
always-show-nsfw: "Always show NSFW contents"
|
||||
always-mark-nsfw: "Always mark posts with media attachments as NSFW"
|
||||
@@ -232,7 +247,7 @@ common:
|
||||
deck-column-width-wide: "Wide"
|
||||
use-shadow: "Use shadows in the UI"
|
||||
rounded-corners: "Round the corners of the UI"
|
||||
circle-icons: "Use circular icons"
|
||||
circle-icons: "Use circular avatar icon"
|
||||
contrasted-acct: "Add contrast to user account"
|
||||
wallpaper: "Background image"
|
||||
choose-wallpaper: "Choose a background"
|
||||
@@ -282,6 +297,16 @@ common:
|
||||
saved: "Saved"
|
||||
home-profile: "Home profile"
|
||||
deck-profile: "Deck profile"
|
||||
room: "Room"
|
||||
_room:
|
||||
graphicsQuality: "Graphics Quality"
|
||||
_graphicsQuality:
|
||||
ultra: "Ultra"
|
||||
high: "High"
|
||||
medium: "Medium"
|
||||
low: "Low"
|
||||
cheep: "Cheep"
|
||||
useOrthographicCamera: "Use Orthographic Camera"
|
||||
search: "Search"
|
||||
delete: "Delete"
|
||||
loading: "Loading"
|
||||
@@ -366,6 +391,9 @@ common/views/pages/explore.vue:
|
||||
federated: "From the fediverse"
|
||||
explore: "Explore {host}"
|
||||
users-info: "Currently, {users} users are registered here"
|
||||
common/views/components/reactions-viewer.details.vue:
|
||||
few-users: "{users} reacted with {reaction}"
|
||||
many-users: "{users}, and {omitted} more reacted with {reaction}"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "Enable playback"
|
||||
disable-player: "Close the player"
|
||||
@@ -531,6 +559,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "Unpin"
|
||||
delete: "Delete"
|
||||
delete-confirm: "Are you sure you want to delete this post?"
|
||||
delete-and-edit: "Delete and Edit"
|
||||
delete-and-edit-confirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it."
|
||||
remote: "Show original note"
|
||||
pin-limit-exceeded: "You can't pin any more posts."
|
||||
common/views/components/user-menu.vue:
|
||||
@@ -618,7 +648,7 @@ common/views/components/signin.vue:
|
||||
signin-with-twitter: "Log in with Twitter"
|
||||
signin-with-github: "Sign in with GitHub"
|
||||
signin-with-discord: "Sign in with Discord"
|
||||
login-failed: "Logging in has failed. Make sure you have entered the correct username and password."
|
||||
login-failed: "Unable to log in. The username or password you entered is incorrect."
|
||||
tap-key: "Click on the Security Key to log in"
|
||||
enter-2fa-code: "Enter your verification code"
|
||||
common/views/components/signup.vue:
|
||||
@@ -726,8 +756,8 @@ common/views/components/profile-editor.vue:
|
||||
uploading: "Uploading"
|
||||
upload-failed: "Failed to upload"
|
||||
unable-to-process: "The operation could not be completed."
|
||||
avatar-not-an-image: "The file specified as an avatar is not an image"
|
||||
banner-not-an-image: "The file specified as a banner is not an image"
|
||||
avatar-not-an-image: "The file you specified as an avatar is not an image"
|
||||
banner-not-an-image: "The file you specified as a banner is not an image"
|
||||
email: "Email settings"
|
||||
email-address: "Email Address"
|
||||
email-verified: "Your email has been verified."
|
||||
@@ -901,7 +931,7 @@ desktop/views/components/drive.file.vue:
|
||||
copy-url: "Copy URL"
|
||||
download: "Download"
|
||||
else-files: "Other"
|
||||
set-as-avatar: "Set as an avatar"
|
||||
set-as-avatar: "Set as avatar"
|
||||
set-as-banner: "Set as a banner"
|
||||
open-in-app: "Open in app"
|
||||
add-app: "Add app"
|
||||
@@ -1119,6 +1149,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "Groups"
|
||||
follow-requests: "Follow requests"
|
||||
admin: "Admin"
|
||||
room: "Room"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "Games"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@@ -1698,6 +1729,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "No posts were found for '{q}'"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Choose files"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifications"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Signed in as {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1788,6 +1821,7 @@ pages:
|
||||
read-page: "Viewing the source"
|
||||
page-created: "Created the page!"
|
||||
page-updated: "Updated the page"
|
||||
name-already-exists: "The specified page name already exists"
|
||||
are-you-sure-delete: "Do you want to delete this page?"
|
||||
page-deleted: "The page has been deleted"
|
||||
edit-this-page: "Edit this page"
|
||||
@@ -2056,3 +2090,64 @@ pages:
|
||||
enviromentVariables: "Environment variable"
|
||||
pageVariables: "Page element"
|
||||
argVariables: "Input slot"
|
||||
room:
|
||||
add-furniture: "Place furniture"
|
||||
translate: "Move"
|
||||
rotate: "Rotate"
|
||||
exit: "Deselect"
|
||||
remove: "Remove"
|
||||
save: "Save"
|
||||
saved: "Saved"
|
||||
clear: "Remove All"
|
||||
clear-confirm: "Are you sure to remove all furnitures in your room?"
|
||||
leave-confirm: "There are unsaved changes. Do you really want to leave?"
|
||||
chooseImage: "Select an image"
|
||||
room-type: "Room type"
|
||||
carpet-color: "Color of carpet"
|
||||
rooms:
|
||||
default: "Default"
|
||||
washitsu: "Japanese-style"
|
||||
furnitures:
|
||||
milk: "Milk carton"
|
||||
bed: "Bed"
|
||||
low-table: "Low Table"
|
||||
desk: "Desk"
|
||||
chair: "Chair"
|
||||
chair2: "Chair 2"
|
||||
fan: "Fan"
|
||||
pc: "Computer"
|
||||
plant: "Houseplant"
|
||||
plant2: "Houseplant 2"
|
||||
eraser: "Eraser"
|
||||
pencil: "Pencil"
|
||||
pudding: "Pudding"
|
||||
cardboard-box: "Cardboard Box"
|
||||
cardboard-box2: "Cardboard Box 2"
|
||||
cardboard-box3: "Cardboard Box 3"
|
||||
book: "Book"
|
||||
book2: "Book 2"
|
||||
piano: "Piano"
|
||||
facial-tissue: "Facial tissue"
|
||||
server: "Servers"
|
||||
moon: "Moon"
|
||||
corkboard: "Cork board"
|
||||
mousepad: "Mousepad"
|
||||
monitor: "Monitor"
|
||||
keyboard: "Keyboard"
|
||||
carpet-stripe: "Carpet (stripe)"
|
||||
mat: "Mat"
|
||||
color-box: "Bookshelf"
|
||||
wall-clock: "Wall clock"
|
||||
photoframe: "Picture frame"
|
||||
cube: "Cube"
|
||||
tv: "TV"
|
||||
pinguin: "Penguin"
|
||||
rubik-cube: "Rubik's Cube"
|
||||
poster-h: "Poster (Horizontal)"
|
||||
poster-v: "Poster (Vertical)"
|
||||
sofa: "Sofa"
|
||||
spiral: "Spiral Staircase"
|
||||
bin: "Waste bin"
|
||||
cup-noodle: "Cup noodle"
|
||||
holo-display: "Holographic display"
|
||||
energy-drink: "Energy drink"
|
||||
|
@@ -31,6 +31,11 @@ common:
|
||||
signin: "Iniciar sesión"
|
||||
signup: "¡Regístrate!"
|
||||
signout: "Cerrar sesión"
|
||||
delete-confirm: "¿Seguro que quieres borrar la publicación?"
|
||||
notification-types:
|
||||
all: "Todo"
|
||||
reply: "Responder"
|
||||
renote: "Volver a publicar"
|
||||
got-it: "¡Listo!"
|
||||
customization-tips:
|
||||
title: "Consejos de personalización"
|
||||
@@ -172,7 +177,7 @@ common:
|
||||
deck-column-width-wide: "Ancho"
|
||||
use-shadow: "Usar sombras en la Interfaz de Usuario"
|
||||
rounded-corners: "Esquinas redondeadas en la Interfaz de Usuario"
|
||||
circle-icons: "Usar iconos circulares"
|
||||
circle-icons: "Usar avatar circulares"
|
||||
contrasted-acct: "Añadir contraste al nombre de usuario"
|
||||
wallpaper: "Fondo de pantalla"
|
||||
choose-wallpaper: "Escoge un fondo de pantalla"
|
||||
@@ -1089,6 +1094,8 @@ mobile/views/pages/games/reversi.vue:
|
||||
reversi: "Reversi"
|
||||
mobile/views/pages/search.vue:
|
||||
search: "Buscar"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notificaciones"
|
||||
mobile/views/pages/user/home.vue:
|
||||
activity: "Actividad"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
@@ -1128,3 +1135,9 @@ pages:
|
||||
arg1: "Listas"
|
||||
types:
|
||||
array: "Listas"
|
||||
room:
|
||||
save: "Guardar"
|
||||
saved: "Guardado"
|
||||
furnitures:
|
||||
moon: "Luna"
|
||||
bin: "Papelera"
|
||||
|
@@ -35,6 +35,19 @@ common:
|
||||
signout: "Se déconnecter"
|
||||
reload-to-apply-the-setting: "Le rechargement de la page est nécessaire pour appliquer ces paramètres. Désirez-vous la recharger maintenant ?"
|
||||
unfollow-confirm: "Désirez-vous vous désabonner de {name} ?"
|
||||
delete-confirm: "Supprimer cette publication ?"
|
||||
signin-required: "Veuillez vous connecter"
|
||||
notification-type: "Type de notification"
|
||||
notification-types:
|
||||
all: "Tout"
|
||||
pollVote: "Sondage"
|
||||
follow: "Abonnements"
|
||||
receiveFollowRequest: "Demandes d’abonnements"
|
||||
reply: "Répondre"
|
||||
quote: "Cité par"
|
||||
renote: "Republier"
|
||||
mention: "Mentions"
|
||||
reaction: "Réaction"
|
||||
got-it: "J’ai compris !"
|
||||
customization-tips:
|
||||
title: "Conseils de personnalisation"
|
||||
@@ -223,7 +236,7 @@ common:
|
||||
deck-column-width-wide: "Large"
|
||||
use-shadow: "Utiliser les ombres dans l'interface utilisateur"
|
||||
rounded-corners: "Coins arrondis de l'interface utilisateur"
|
||||
circle-icons: "Utiliser des icônes circulaires"
|
||||
circle-icons: "Utiliser des avatar circulaires"
|
||||
contrasted-acct: "Ajouter du contraste au nom de l’utilisateur"
|
||||
wallpaper: "Image du fond d'écran"
|
||||
choose-wallpaper: "Sélectionner un fond d'écran"
|
||||
@@ -271,6 +284,18 @@ common:
|
||||
sync: "Synchroniser"
|
||||
save: "Enregistrer"
|
||||
saved: "enregistré"
|
||||
home-profile: "Profil principal"
|
||||
deck-profile: "Profil deck"
|
||||
room: "Pièce"
|
||||
_room:
|
||||
graphicsQuality: "Qualité des graphismes"
|
||||
_graphicsQuality:
|
||||
ultra: "Très élevée"
|
||||
high: "Élevée"
|
||||
medium: "Moyenne"
|
||||
low: "Basse"
|
||||
cheep: "Minimale"
|
||||
useOrthographicCamera: "Utiliser une caméra orthographique"
|
||||
search: "Recherche"
|
||||
delete: "Supprimer"
|
||||
loading: "Chargement en cours …"
|
||||
@@ -392,6 +417,7 @@ common/views/components/games/reversi/reversi.room.vue:
|
||||
black-or-white: "Noirs/Blancs"
|
||||
black-is: "{} Noirs"
|
||||
rules: "Règles"
|
||||
is-llotheo: "Celui ou celle qui a le moins de pièces gagne (Llotheo)"
|
||||
looped-map: "Carte en boucle"
|
||||
can-put-everywhere: "Peut poser partout"
|
||||
settings-of-the-bot: "Configuration du bot"
|
||||
@@ -519,6 +545,7 @@ common/views/components/note-menu.vue:
|
||||
unpin: "Désépingler"
|
||||
delete: "Supprimer"
|
||||
delete-confirm: "Supprimer cette publication ?"
|
||||
delete-and-edit: "Effacer et éditer"
|
||||
remote: "Afficher la note originale"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "Mention"
|
||||
@@ -587,6 +614,7 @@ common/views/components/emoji-picker.vue:
|
||||
flags: "Drapeaux"
|
||||
common/views/components/settings/app-type.vue:
|
||||
title: "Mode"
|
||||
intro: "Vous pouvez choisir, si vous voulez utiliser la disposition de bureau ou mobile."
|
||||
choices:
|
||||
auto: "Choisir la disposition automatiquement"
|
||||
desktop: "Toujours utiliser la disposition de bureau"
|
||||
@@ -709,6 +737,8 @@ common/views/components/profile-editor.vue:
|
||||
uploading: "En cours d’envoi …"
|
||||
upload-failed: "Échec de l'envoi"
|
||||
unable-to-process: "L'opération n'a pas pu être complétée"
|
||||
avatar-not-an-image: "Le fichier sélectionné pour votre avatar n'est pas une image"
|
||||
banner-not-an-image: "Le fichier sélectionné pour votre bannière n'est pas une image"
|
||||
email: "Paramètres de messagerie"
|
||||
email-address: "Adresse de courrier électronique"
|
||||
email-verified: "L’adresse du courrier électronique a été vérifiée."
|
||||
@@ -894,6 +924,8 @@ desktop/views/components/drive.folder.vue:
|
||||
unable-to-process: "L'opération n'a pas pu être complétée"
|
||||
circular-reference-detected: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer."
|
||||
unhandled-error: "Erreur inconnue"
|
||||
unable-to-delete: "Ne peut pas être supprimé"
|
||||
has-child-files-or-folders: "Ce dossier n'est pas vide, il ne peut pas être supprimé"
|
||||
contextmenu:
|
||||
move-to-this-folder: "Déplacer dans ce dossier"
|
||||
show-in-new-window: "Ouvrir dans une nouvelle fenêtre"
|
||||
@@ -1007,6 +1039,7 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "Sauvegarde des paramètres avec succès !"
|
||||
failed: "L’opération a échoué. Veuillez vous assurer que le jeton a été saisi correctement."
|
||||
info: "À partir de maintenant, à chaque fois que vous vous connectez entrez votre mot de passe ainsi que le jeton généré sur votre appareil."
|
||||
totp-header: "Application d'authentification"
|
||||
security-key-header: "Clé de sécurité"
|
||||
last-used: "Dernière utilisation :"
|
||||
activate-key: "Cliquez pour activer la clé de sécurité"
|
||||
@@ -1038,6 +1071,7 @@ common/views/components/drive-settings.vue:
|
||||
max: "Maximale"
|
||||
in-use: "utilisé"
|
||||
stats: "Statistiques"
|
||||
default-upload-folder: "Emplacement par défaut du dossier de transfert"
|
||||
default-upload-folder-name: "Dossier·s"
|
||||
change-default-upload-folder: "Changer de dossier"
|
||||
common/views/components/mute-and-block.vue:
|
||||
@@ -1093,6 +1127,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "Groupes"
|
||||
follow-requests: "Demandes d’abonnement"
|
||||
admin: "Admin"
|
||||
room: "Pièce"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "Jeux"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@@ -1146,11 +1181,13 @@ admin/views/queue.vue:
|
||||
deliver: "Délivrées"
|
||||
inbox: "Reçues"
|
||||
db: "Base de données"
|
||||
objectStorage: "Stockage d'objets"
|
||||
state: "État"
|
||||
states:
|
||||
active: "en cours"
|
||||
delayed: "Programmé"
|
||||
waiting: "En file d'attente"
|
||||
result-is-truncated: "Le résultat est tronqué"
|
||||
other-queues: "Autres files d’attente"
|
||||
admin/views/logs.vue:
|
||||
logs: "Journaux"
|
||||
@@ -1183,12 +1220,14 @@ admin/views/instance.vue:
|
||||
languages-desc: "Vous pouvez en définir plus d’une, séparées par des espaces."
|
||||
tos-url: "URL des conditions d'utilisation"
|
||||
repository-url: "URL du dépôt"
|
||||
feedback-url: "URL pour les commentaires"
|
||||
maintainer-config: "Informations de l’administrateur"
|
||||
maintainer-name: "Nom de l’administrateur"
|
||||
maintainer-email: "Contact administratif"
|
||||
advanced-config: "Autres réglages"
|
||||
note-and-tl: "Notes et fils"
|
||||
drive-config: "Paramètres du lecteur"
|
||||
use-object-storage: "Utiliser le stockage d'objets"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-prefix: "Préfixe"
|
||||
object-storage-endpoint: "Point de terminaison"
|
||||
@@ -1366,6 +1405,7 @@ admin/views/moderators.vue:
|
||||
title: "Journaux"
|
||||
moderator: "Modérateurs"
|
||||
type: "Actions"
|
||||
at: "Date de modification"
|
||||
info: "Informations"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
@@ -1643,6 +1683,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "Aucune publication trouvée pour « {q} »."
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Choisissez un fichier"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifications"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Connecté·e en tant que {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1970,3 +2012,60 @@ pages:
|
||||
emptySlot: "Slot vide"
|
||||
enviromentVariables: "Variables d'environnement"
|
||||
pageVariables: "Élément de page"
|
||||
room:
|
||||
add-furniture: "Placer des meubles"
|
||||
translate: "Déplacer"
|
||||
rotate: "Tourner"
|
||||
remove: "Enlever"
|
||||
save: "Enregistrer"
|
||||
saved: "enregistré"
|
||||
clear: "Tout enlever"
|
||||
clear-confirm: "Désirez-vous enlever tout les meubles de votre chambre ?"
|
||||
leave-confirm: "Vous avez des modifications non-sauvegardées. Voulez-vous vraiment quitter ?"
|
||||
chooseImage: "Sélectionnez une image"
|
||||
room-type: "Type de chambre"
|
||||
carpet-color: "Couleur du tapis"
|
||||
rooms:
|
||||
default: "Par défaut"
|
||||
washitsu: "Style japonnais"
|
||||
furnitures:
|
||||
milk: "Lait en carton"
|
||||
bed: "Lit"
|
||||
low-table: "Table basse"
|
||||
desk: "Bureau"
|
||||
chair: "Chaise"
|
||||
chair2: "Chaise 2"
|
||||
fan: "Ventilateur"
|
||||
pc: "Ordinateur"
|
||||
plant: "Plante d’intérieur"
|
||||
plant2: "Plante d’intérieur 2"
|
||||
eraser: "Gomme"
|
||||
pencil: "Crayon"
|
||||
cardboard-box: "Boîte en carton"
|
||||
cardboard-box2: "Boîte en carton 2"
|
||||
cardboard-box3: "Boîte en carton 3"
|
||||
book: "Livre"
|
||||
book2: "Livre 2"
|
||||
piano: "Piano"
|
||||
server: "Serveurs"
|
||||
moon: "Lune"
|
||||
corkboard: "Tableau en liège"
|
||||
mousepad: "Tapis de souris"
|
||||
monitor: "Écran"
|
||||
keyboard: "Clavier"
|
||||
carpet-stripe: "Tapis (zébré)"
|
||||
color-box: "Étagère"
|
||||
wall-clock: "Horloge murale"
|
||||
photoframe: "Cadre photo"
|
||||
cube: "Cube"
|
||||
tv: "Téléviseur"
|
||||
pinguin: "Pingouin"
|
||||
rubik-cube: "Cube de Rubik"
|
||||
poster-h: "Affiche (horizontale)"
|
||||
poster-v: "Affiche (verticale)"
|
||||
sofa: "Canapé"
|
||||
spiral: "Escaliers en spirale"
|
||||
bin: "Corbeille"
|
||||
cup-noodle: "Bol de nouilles"
|
||||
holo-display: "Affichage holographique"
|
||||
energy-drink: "Boisson énergétique"
|
||||
|
@@ -37,6 +37,19 @@ common:
|
||||
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
|
||||
fetching-as-ap-object: "連合に照会中"
|
||||
unfollow-confirm: "{name}さんをフォロー解除しますか?"
|
||||
delete-confirm: "この投稿を削除しますか?"
|
||||
signin-required: "ログインしてください"
|
||||
notification-type: "通知の種類"
|
||||
notification-types:
|
||||
all: "すべて"
|
||||
pollVote: "投票"
|
||||
follow: "フォロー"
|
||||
receiveFollowRequest: "フォローリクエスト"
|
||||
reply: "返信"
|
||||
quote: "引用"
|
||||
renote: "Renote"
|
||||
mention: "言及"
|
||||
reaction: "リアクション"
|
||||
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
@@ -126,6 +139,7 @@ common:
|
||||
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
||||
error: "エラー"
|
||||
enter-username: "ユーザー名を入力してください"
|
||||
specified-recipient: "宛先"
|
||||
add-visible-user: "ユーザーを追加"
|
||||
cw-placeholder: "内容への注釈 (オプション)"
|
||||
username-prompt: "ユーザー名を入力してください"
|
||||
@@ -214,6 +228,7 @@ common:
|
||||
use-avatar-reversi-stones: "リバーシの石にアバターを使う"
|
||||
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
|
||||
disable-showing-animated-images: "アニメーション画像を再生しない"
|
||||
enable-quick-notification-view: "通知のクイックビューを有効にする"
|
||||
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
|
||||
always-show-nsfw: "常に閲覧注意のメディアを表示する"
|
||||
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
|
||||
@@ -244,7 +259,7 @@ common:
|
||||
deck-column-width-wide: "広"
|
||||
use-shadow: "UIに影を使用"
|
||||
rounded-corners: "UIの角を丸める"
|
||||
circle-icons: "円形のアイコンを使用"
|
||||
circle-icons: "円形のアバターを使用"
|
||||
contrasted-acct: "ユーザー名にコントラストを付ける"
|
||||
wallpaper: "壁紙"
|
||||
choose-wallpaper: "壁紙を選択"
|
||||
@@ -294,6 +309,16 @@ common:
|
||||
saved: "保存しました"
|
||||
home-profile: "ホームのプロファイル"
|
||||
deck-profile: "デッキのプロファイル"
|
||||
room: "ルーム"
|
||||
_room:
|
||||
graphicsQuality: "グラフィックの品質"
|
||||
_graphicsQuality:
|
||||
ultra: "最高"
|
||||
high: "高"
|
||||
medium: "中"
|
||||
low: "低"
|
||||
cheep: "最低"
|
||||
useOrthographicCamera: "平行投影カメラを使用"
|
||||
|
||||
search: "検索"
|
||||
delete: "削除"
|
||||
@@ -388,6 +413,10 @@ common/views/pages/explore.vue:
|
||||
explore: "{host}を探索"
|
||||
users-info: "現在{users}ユーザーが登録されています"
|
||||
|
||||
common/views/components/reactions-viewer.details.vue:
|
||||
few-users: "{users}が{reaction}をリアクション"
|
||||
many-users: "{users}と他{omitted}人が{reaction}をリアクション"
|
||||
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "プレイヤーを開く"
|
||||
disable-player: "プレイヤーを閉じる"
|
||||
@@ -569,6 +598,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "ピン留め解除"
|
||||
delete: "削除"
|
||||
delete-confirm: "この投稿を削除しますか?"
|
||||
delete-and-edit: "削除して編集"
|
||||
delete-and-edit-confirm: "この投稿を削除してもう一度編集しますか?この投稿へのリアクション、Renote、返信も全て削除されます。"
|
||||
remote: "投稿元で見る"
|
||||
pin-limit-exceeded: "これ以上ピン留めできません。"
|
||||
|
||||
@@ -769,7 +800,7 @@ common/views/components/profile-editor.vue:
|
||||
you-can-include-hashtags: "ハッシュタグを含めることができます。"
|
||||
language: "言語"
|
||||
birthday: "誕生日"
|
||||
avatar: "アイコン"
|
||||
avatar: "アバター"
|
||||
banner: "バナー"
|
||||
is-cat: "このアカウントはCatです"
|
||||
is-bot: "このアカウントはBotです"
|
||||
@@ -783,7 +814,7 @@ common/views/components/profile-editor.vue:
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
unable-to-process: "操作を完了できません"
|
||||
avatar-not-an-image: "アイコンとして指定したファイルは画像ではありません"
|
||||
avatar-not-an-image: "アバターとして指定したファイルは画像ではありません"
|
||||
banner-not-an-image: "バナーとして指定したファイルは画像ではありません"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
@@ -973,7 +1004,7 @@ desktop/views/components/drive-window.vue:
|
||||
used: "使用中"
|
||||
|
||||
desktop/views/components/drive.file.vue:
|
||||
avatar: "アイコン"
|
||||
avatar: "アバター"
|
||||
banner: "バナー"
|
||||
nsfw: "閲覧注意"
|
||||
contextmenu:
|
||||
@@ -983,7 +1014,7 @@ desktop/views/components/drive.file.vue:
|
||||
copy-url: "URLをコピー"
|
||||
download: "ダウンロード"
|
||||
else-files: "その他"
|
||||
set-as-avatar: "アイコンに設定"
|
||||
set-as-avatar: "アバターに設定"
|
||||
set-as-banner: "バナーに設定"
|
||||
open-in-app: "アプリで開く"
|
||||
add-app: "アプリを追加"
|
||||
@@ -1234,6 +1265,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "グループ"
|
||||
follow-requests: "フォロー申請"
|
||||
admin: "管理"
|
||||
room: "ルーム"
|
||||
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "ゲーム"
|
||||
@@ -1883,6 +1915,9 @@ mobile/views/pages/search.vue:
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "ファイルを選択"
|
||||
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "{}としてサインイン中"
|
||||
|
||||
@@ -1984,6 +2019,9 @@ pages:
|
||||
read-page: "ソースを表示中"
|
||||
page-created: "ページを作成しました"
|
||||
page-updated: "ページを更新しました"
|
||||
name-already-exists: "指定されたページURLは既に存在しています"
|
||||
title-invalid-name: "不正なページURLです"
|
||||
text-invalid-name: "空白でないか確認してください"
|
||||
are-you-sure-delete: "このページを削除しますか?"
|
||||
page-deleted: "ページを削除しました"
|
||||
edit-this-page: "このページを編集"
|
||||
@@ -2262,3 +2300,65 @@ pages:
|
||||
enviromentVariables: "環境変数"
|
||||
pageVariables: "ページ要素"
|
||||
argVariables: "入力スロット"
|
||||
|
||||
room:
|
||||
add-furniture: "家具を置く"
|
||||
translate: "移動"
|
||||
rotate: "回転"
|
||||
exit: "戻る"
|
||||
remove: "しまう"
|
||||
save: "保存"
|
||||
saved: "保存しました"
|
||||
clear: "片付け"
|
||||
clear-confirm: "全ての家具をしまいますか?"
|
||||
leave-confirm: "未保存の変更があります、移動しますか?"
|
||||
chooseImage: "画像を選択"
|
||||
room-type: "部屋のタイプ"
|
||||
carpet-color: "床の色"
|
||||
rooms:
|
||||
default: "デフォルト"
|
||||
washitsu: "和室"
|
||||
furnitures:
|
||||
milk: "牛乳パック"
|
||||
bed: "ベッド"
|
||||
low-table: "ローテーブル"
|
||||
desk: "デスク"
|
||||
chair: "チェア"
|
||||
chair2: "チェア2"
|
||||
fan: "換気扇"
|
||||
pc: "パソコン"
|
||||
plant: "観葉植物"
|
||||
plant2: "観葉植物2"
|
||||
eraser: "消しゴム"
|
||||
pencil: "鉛筆"
|
||||
pudding: "プリン"
|
||||
cardboard-box: "段ボール箱"
|
||||
cardboard-box2: "段ボール箱2"
|
||||
cardboard-box3: "段ボール箱3"
|
||||
book: "本"
|
||||
book2: "本2"
|
||||
piano: "ピアノ"
|
||||
facial-tissue: "ティッシュボックス"
|
||||
server: "サーバー"
|
||||
moon: "月"
|
||||
corkboard: "コルクボード"
|
||||
mousepad: "マウスパッド"
|
||||
monitor: "モニター"
|
||||
keyboard: "キーボード"
|
||||
carpet-stripe: "カーペット(縞)"
|
||||
mat: "マット"
|
||||
color-box: "カラーボックス"
|
||||
wall-clock: "壁掛け時計"
|
||||
photoframe: "額縁"
|
||||
cube: "キューブ"
|
||||
tv: "テレビ"
|
||||
pinguin: "ピンギン"
|
||||
rubik-cube: "ルービックキューブ"
|
||||
poster-h: "ポスター(横長)"
|
||||
poster-v: "ポスター(縦長)"
|
||||
sofa: "ソファ"
|
||||
spiral: "螺旋階段"
|
||||
bin: "ゴミ箱"
|
||||
cup-noodle: "カップ麺"
|
||||
holo-display: "ホログラフィックディスプレイ"
|
||||
energy-drink: "エナジードリンク"
|
||||
|
@@ -27,6 +27,13 @@ common:
|
||||
load-more: "もっとあらへんのか!"
|
||||
enter-password: "パスワードを入れてや"
|
||||
2fa: "二段階認証"
|
||||
delete-confirm: "この投稿を削除してもええか?"
|
||||
notification-types:
|
||||
all: "すべて"
|
||||
follow: "フォロー"
|
||||
reply: "返す"
|
||||
renote: "Renote"
|
||||
reaction: "リアクション"
|
||||
got-it: "ほい"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@@ -460,7 +467,7 @@ common/views/components/profile-editor.vue:
|
||||
description: "自己紹介"
|
||||
language: "言語"
|
||||
birthday: "誕生日"
|
||||
avatar: "アイコン"
|
||||
avatar: "アバター"
|
||||
banner: "バナー"
|
||||
is-cat: "このアカウントはCatやで"
|
||||
is-bot: "このアカウントはBotやで"
|
||||
@@ -598,7 +605,7 @@ desktop/views/components/crop-window.vue:
|
||||
desktop/views/components/drive-window.vue:
|
||||
used: "使うとる"
|
||||
desktop/views/components/drive.file.vue:
|
||||
avatar: "アイコン"
|
||||
avatar: "アバター"
|
||||
banner: "バナー"
|
||||
nsfw: "見たらあかんで"
|
||||
contextmenu:
|
||||
@@ -608,7 +615,7 @@ desktop/views/components/drive.file.vue:
|
||||
copy-url: "URLをコピー"
|
||||
download: "ダウンロード"
|
||||
else-files: "その他"
|
||||
set-as-avatar: "アイコンにする"
|
||||
set-as-avatar: "アバターにする"
|
||||
set-as-banner: "バナーにする"
|
||||
open-in-app: "アプリで開く"
|
||||
add-app: "アプリ増やす"
|
||||
@@ -1176,6 +1183,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "ワイは「{q}」なんて投稿知らんわ、無いんちゃう?知らんけど。"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "ファイル選んでや"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "あんたは橋の下で拾った{}や!"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1275,3 +1284,10 @@ pages:
|
||||
arg1: "リスト"
|
||||
types:
|
||||
array: "リスト"
|
||||
room:
|
||||
translate: "移動"
|
||||
save: "保存"
|
||||
saved: "保存したで!"
|
||||
furnitures:
|
||||
moon: "月"
|
||||
bin: "ゴミ箱"
|
||||
|
@@ -36,6 +36,19 @@ common:
|
||||
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
|
||||
fetching-as-ap-object: "연합에서 조회 중"
|
||||
unfollow-confirm: "{name} 님을 팔로우 해제하시겠습니까?"
|
||||
delete-confirm: "이 글을 삭제하시겠습니까?"
|
||||
signin-required: "로그인 해주세요"
|
||||
notification-type: "알림의 종류"
|
||||
notification-types:
|
||||
all: "모두"
|
||||
pollVote: "투표"
|
||||
follow: "팔로잉"
|
||||
receiveFollowRequest: "팔로우 요청"
|
||||
reply: "답글 달기"
|
||||
quote: "인용"
|
||||
renote: "리노트"
|
||||
mention: "멘션"
|
||||
reaction: "리액션"
|
||||
got-it: "알겠습니다"
|
||||
customization-tips:
|
||||
title: "커스터마이징 도움말"
|
||||
@@ -88,7 +101,7 @@ common:
|
||||
"read:mutes": "뮤트 보기"
|
||||
"write:mutes": "뮤트 수정"
|
||||
"write:notes": "글 작성, 삭제"
|
||||
"read:notifications": "글 보기"
|
||||
"read:notifications": "알림 보기"
|
||||
"write:notifications": "알림 수정"
|
||||
"read:reactions": "리액션 보기"
|
||||
"write:reactions": "리액션 수정"
|
||||
@@ -120,6 +133,7 @@ common:
|
||||
geolocation-alert: "사용 중이신 장치에서는 위치 정보를 사용할 수 없습니다"
|
||||
error: "오류"
|
||||
enter-username: "사용자명을 입력해주세요"
|
||||
specified-recipient: "수신인"
|
||||
add-visible-user: "사용자 추가"
|
||||
cw-placeholder: "내용에 대한 주석 (옵션)"
|
||||
username-prompt: "사용자명을 입력해주세요"
|
||||
@@ -202,6 +216,7 @@ common:
|
||||
use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용"
|
||||
disable-animated-mfm: "글의 문자 애니메이션을 비활성화"
|
||||
disable-showing-animated-images: "움직이는 이미지를 자동으로 재생하지 않음"
|
||||
enable-quick-notification-view: "알림의 빠른 보기를 사용합니다"
|
||||
suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시"
|
||||
always-show-nsfw: "항상 열람주의 미디어를 표시"
|
||||
always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
|
||||
@@ -232,7 +247,7 @@ common:
|
||||
deck-column-width-wide: "넓음"
|
||||
use-shadow: "UI에 그림자 효과 적용"
|
||||
rounded-corners: "UI의 모서리를 둥글게 설정"
|
||||
circle-icons: "원형 아이콘 사용"
|
||||
circle-icons: "원형 아바타를 사용"
|
||||
contrasted-acct: "사용자명에 대비 추가"
|
||||
wallpaper: "배경"
|
||||
choose-wallpaper: "배경 설정"
|
||||
@@ -282,6 +297,16 @@ common:
|
||||
saved: "저장하였습니다"
|
||||
home-profile: "홈 프로필"
|
||||
deck-profile: "덱 프로필"
|
||||
room: "룸"
|
||||
_room:
|
||||
graphicsQuality: "그래픽 품질"
|
||||
_graphicsQuality:
|
||||
ultra: "최고"
|
||||
high: "높음"
|
||||
medium: "보통"
|
||||
low: "낮음"
|
||||
cheep: "최저"
|
||||
useOrthographicCamera: "평행 투시 카메라를 사용"
|
||||
search: "검색"
|
||||
delete: "삭제"
|
||||
loading: "로드 중"
|
||||
@@ -366,6 +391,9 @@ common/views/pages/explore.vue:
|
||||
federated: "연합"
|
||||
explore: "{host}을(를) 탐색"
|
||||
users-info: "현재 {users} 사용자가 등록되어 있습니다"
|
||||
common/views/components/reactions-viewer.details.vue:
|
||||
few-users: "{users}님이 {reaction} 리액션"
|
||||
many-users: "{users}님 외 {omitted}명이 {reaction} 리액션"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "플레이어 열기"
|
||||
disable-player: "플레이어 닫기"
|
||||
@@ -531,6 +559,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "프로필에서 고정 해제"
|
||||
delete: "삭제"
|
||||
delete-confirm: "이 글을 삭제하시겠습니까?"
|
||||
delete-and-edit: "삭제 후 편집"
|
||||
delete-and-edit-confirm: "이 글을 삭제한 뒤 다시 편집하시겠습니까? 이 글에 대한 리액션, 리노트, 답글 또한 모두 삭제됩니다."
|
||||
remote: "글 원본 보기"
|
||||
pin-limit-exceeded: "더 이상 고정할 수 없습니다."
|
||||
common/views/components/user-menu.vue:
|
||||
@@ -1119,6 +1149,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "그룹"
|
||||
follow-requests: "팔로우 요청"
|
||||
admin: "관리"
|
||||
room: "룸"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "게임"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@@ -1698,6 +1729,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "\"{q}\" 와 일치하는 글을 찾을 수 없습니다."
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "파일 선택"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "알림"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "{}(으)로 로그인"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1788,6 +1821,7 @@ pages:
|
||||
read-page: "소스 표시중"
|
||||
page-created: "페이지를 만들었습니다"
|
||||
page-updated: "페이지를 수정했습니다"
|
||||
name-already-exists: "지정한 페이지 URL은 이미 존재합니다"
|
||||
are-you-sure-delete: "이 페이지를 삭제하시겠습니까?"
|
||||
page-deleted: "페이지가 삭제되었습니다"
|
||||
edit-this-page: "이 페이지를 편집"
|
||||
@@ -2056,3 +2090,64 @@ pages:
|
||||
enviromentVariables: "환경 변수"
|
||||
pageVariables: "페이지 요소"
|
||||
argVariables: "입력 슬롯"
|
||||
room:
|
||||
add-furniture: "가구를 배치"
|
||||
translate: "이동"
|
||||
rotate: "회전"
|
||||
exit: "선택 해제"
|
||||
remove: "치우기"
|
||||
save: "저장"
|
||||
saved: "저장하였습니다"
|
||||
clear: "모두 치우기"
|
||||
clear-confirm: "정말 방 안의 모든 가구를 치우시겠습니까?"
|
||||
leave-confirm: "저장되지 않은 변경 사항이 있습니다. 정말 떠나시겠습니까?"
|
||||
chooseImage: "이미지 선택"
|
||||
room-type: "룸 종류"
|
||||
carpet-color: "바닥 색상"
|
||||
rooms:
|
||||
default: "기본"
|
||||
washitsu: "일본식"
|
||||
furnitures:
|
||||
milk: "우유 팩"
|
||||
bed: "침대"
|
||||
low-table: "낮은 테이블"
|
||||
desk: "책상"
|
||||
chair: "의자"
|
||||
chair2: "의자 2"
|
||||
fan: "환기구"
|
||||
pc: "컴퓨터"
|
||||
plant: "관엽식물"
|
||||
plant2: "관엽식물 2"
|
||||
eraser: "지우개"
|
||||
pencil: "연필"
|
||||
pudding: "푸딩"
|
||||
cardboard-box: "골판지 상자"
|
||||
cardboard-box2: "골판지 상자 2"
|
||||
cardboard-box3: "골판지 상자 3"
|
||||
book: "책"
|
||||
book2: "책 2"
|
||||
piano: "피아노"
|
||||
facial-tissue: "휴지 상자"
|
||||
server: "서버"
|
||||
moon: "달"
|
||||
corkboard: "게시판"
|
||||
mousepad: "마우스 패드"
|
||||
monitor: "모니터"
|
||||
keyboard: "키보드"
|
||||
carpet-stripe: "카페트 (줄무늬)"
|
||||
mat: "매트"
|
||||
color-box: "책장"
|
||||
wall-clock: "벽걸이 시계"
|
||||
photoframe: "액자"
|
||||
cube: "큐브"
|
||||
tv: "TV"
|
||||
pinguin: "펭귄"
|
||||
rubik-cube: "루빅스 큐브"
|
||||
poster-h: "포스터 (가로)"
|
||||
poster-v: "포스터 (세로)"
|
||||
sofa: "소파"
|
||||
spiral: "나선형 계단"
|
||||
bin: "휴지통"
|
||||
cup-noodle: "컵라면"
|
||||
holo-display: "홀로그램"
|
||||
energy-drink: "에너지 드링크"
|
||||
|
@@ -8,6 +8,11 @@ common:
|
||||
reaction: "Reactie"
|
||||
close: "Sluiten"
|
||||
enter-password: "Voer het wachtwoord in"
|
||||
notification-types:
|
||||
all: "Alle"
|
||||
follow: "Volgend"
|
||||
reply: "Beantwoorden"
|
||||
reaction: "Reactie"
|
||||
time:
|
||||
unknown: "onbekend"
|
||||
future: "toekomstig"
|
||||
@@ -227,6 +232,7 @@ common/views/pages/follow.vue:
|
||||
follow: "Volgend"
|
||||
desktop:
|
||||
banner: "Omslagfoto"
|
||||
avatar: "Gebruikersafbeelding"
|
||||
unable-to-process: "De operatie kan niet worden voltooid."
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Zwart ... totaal"
|
||||
@@ -569,6 +575,8 @@ mobile/views/pages/search.vue:
|
||||
search: "Zoeken"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Kies een bestand"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Meldingen"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Ingelogd als {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -631,3 +639,7 @@ pages:
|
||||
arg1: "Lijsten"
|
||||
types:
|
||||
array: "Lijsten"
|
||||
room:
|
||||
translate: "Verplaatsen"
|
||||
furnitures:
|
||||
moon: "Maan"
|
||||
|
@@ -11,6 +11,10 @@ common:
|
||||
rich-contents: "Innlegg"
|
||||
drive: "Disk"
|
||||
close: "Lukk"
|
||||
notification-types:
|
||||
all: "Alle"
|
||||
follow: "Følger"
|
||||
reply: "Svar"
|
||||
got-it: "Skjønner!"
|
||||
notification:
|
||||
file-uploaded: "Filen ble lastet opp!"
|
||||
@@ -467,6 +471,8 @@ mobile/views/pages/games/reversi.vue:
|
||||
reversi: "Reversi"
|
||||
mobile/views/pages/search.vue:
|
||||
search: "Søk"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifikasjon"
|
||||
mobile/views/pages/user.vue:
|
||||
following: "Følger"
|
||||
followers: "Følgere"
|
||||
@@ -514,3 +520,9 @@ pages:
|
||||
arg1: "Lister"
|
||||
types:
|
||||
array: "Lister"
|
||||
room:
|
||||
translate: "Flytt"
|
||||
save: "Lagre"
|
||||
furnitures:
|
||||
moon: "Måne"
|
||||
bin: "Papirkurv"
|
||||
|
@@ -28,6 +28,18 @@ common:
|
||||
signin: "Zaloguj się"
|
||||
signup: "Rejestracja"
|
||||
signout: "Wyloguj się"
|
||||
delete-confirm: "Czy na pewno chcesz usunąć ten wpis?"
|
||||
notification-type: "Typy powiadomień"
|
||||
notification-types:
|
||||
all: "Wszyscy"
|
||||
pollVote: "Głosy"
|
||||
follow: "Śledzeni"
|
||||
receiveFollowRequest: "Prośby o śledzenie"
|
||||
reply: "Odpowiedzi"
|
||||
quote: "Cytat"
|
||||
renote: "Udostępnij"
|
||||
mention: "Wzmianki"
|
||||
reaction: "Reakcje"
|
||||
got-it: "Rozumiem!"
|
||||
customization-tips:
|
||||
title: "Wskazówki o dostosowywaniu"
|
||||
@@ -86,8 +98,10 @@ common:
|
||||
visibility: "Widoczność"
|
||||
error: "Błąd"
|
||||
enter-username: "Wprowadź nazwę użytkownika"
|
||||
specified-recipient: "Adresat"
|
||||
add-visible-user: "Dodaj użytkownika"
|
||||
username-prompt: "Wprowadź nazwę użytkownika"
|
||||
enter-file-name: "Wprowadź nazwę pliku"
|
||||
weekday-short:
|
||||
sunday: "N"
|
||||
monday: "Pn"
|
||||
@@ -150,6 +164,7 @@ common:
|
||||
note-visibility: "Widoczność wpisów"
|
||||
remember-note-visibility: "Zapamiętaj widoczność wpisów"
|
||||
web-search-engine: "Wyszukiwarka internetowa"
|
||||
paste: "Wklej"
|
||||
line-width: "Szerokości linii"
|
||||
line-width-thin: "Cienka"
|
||||
line-width-normal: "Normalna"
|
||||
@@ -1137,6 +1152,8 @@ mobile/views/pages/search.vue:
|
||||
search: "Szukaj"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Wybierz plik"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Powiadomienia"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Zalogowany jako {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1242,3 +1259,10 @@ pages:
|
||||
arg1: "Listy"
|
||||
types:
|
||||
array: "Listy"
|
||||
room:
|
||||
translate: "Przenieś"
|
||||
save: "Zapisz"
|
||||
saved: "Zapisano"
|
||||
furnitures:
|
||||
moon: "Księżyc"
|
||||
bin: "Kosz"
|
||||
|
@@ -18,6 +18,8 @@ common:
|
||||
application-authorization: "Aplicativos autorizados"
|
||||
close: "Fechar"
|
||||
do-not-copy-paste: "Por favor, não digite ou copie o código aqui. A conta pode ser comprometida."
|
||||
notification-types:
|
||||
follow: "Seguindo"
|
||||
got-it: "Entendi!"
|
||||
customization-tips:
|
||||
title: "Dicas de personalização"
|
||||
@@ -282,3 +284,7 @@ pages:
|
||||
blocks:
|
||||
image: "Imagens"
|
||||
post: "Formulário de publicação"
|
||||
room:
|
||||
furnitures:
|
||||
moon: "Lua"
|
||||
bin: "Lixo"
|
||||
|
@@ -168,3 +168,7 @@ pages:
|
||||
random: "Случайно"
|
||||
blocks:
|
||||
random: "Случайно"
|
||||
room:
|
||||
furnitures:
|
||||
moon: "Луна"
|
||||
bin: "Мусорное ведро"
|
||||
|
@@ -36,6 +36,19 @@ common:
|
||||
reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?"
|
||||
fetching-as-ap-object: "联合查询"
|
||||
unfollow-confirm: "取消对{name}的关注?"
|
||||
delete-confirm: "确定删除这个投稿吗?"
|
||||
signin-required: "请先登录"
|
||||
notification-type: "通知类型"
|
||||
notification-types:
|
||||
all: "所有"
|
||||
pollVote: "投票"
|
||||
follow: "关注中"
|
||||
receiveFollowRequest: "关注请求"
|
||||
reply: "回复"
|
||||
quote: "引用"
|
||||
renote: "转推"
|
||||
mention: "提及"
|
||||
reaction: "回应"
|
||||
got-it: "知道了"
|
||||
customization-tips:
|
||||
title: "自定义提示"
|
||||
@@ -120,6 +133,7 @@ common:
|
||||
geolocation-alert: "您的设备不支持定位服务"
|
||||
error: "错误"
|
||||
enter-username: "输入用户名"
|
||||
specified-recipient: "收件人"
|
||||
add-visible-user: "添加用户"
|
||||
cw-placeholder: "评论帖子(可选)"
|
||||
username-prompt: "输入用户名"
|
||||
@@ -202,6 +216,7 @@ common:
|
||||
use-avatar-reversi-stones: "用头像作为黑白棋的棋子"
|
||||
disable-animated-mfm: "在帖子中禁用动画文本"
|
||||
disable-showing-animated-images: "不播放动画"
|
||||
enable-quick-notification-view: "启用通知快速查看"
|
||||
suggest-recent-hashtags: "在帖子表单上显示最近流行的哈希标签"
|
||||
always-show-nsfw: "总是显示 NSFW 的内容"
|
||||
always-mark-nsfw: "总是用 NSFW 来标记附件"
|
||||
@@ -232,7 +247,7 @@ common:
|
||||
deck-column-width-wide: "宽"
|
||||
use-shadow: "在UI中使用阴影效果"
|
||||
rounded-corners: "UI界面圆角效果"
|
||||
circle-icons: "使用圆形图标"
|
||||
circle-icons: "使用圆形头像"
|
||||
contrasted-acct: "增加用户名的对比度"
|
||||
wallpaper: "壁纸"
|
||||
choose-wallpaper: "选择壁纸"
|
||||
@@ -282,6 +297,16 @@ common:
|
||||
saved: "已保存"
|
||||
home-profile: "定制首页数据"
|
||||
deck-profile: "定制Deck数据"
|
||||
room: "房间"
|
||||
_room:
|
||||
graphicsQuality: "图形质量"
|
||||
_graphicsQuality:
|
||||
ultra: "最高"
|
||||
high: "高"
|
||||
medium: "中"
|
||||
low: "低"
|
||||
cheep: "最低"
|
||||
useOrthographicCamera: "使用正交相机"
|
||||
search: "搜索"
|
||||
delete: "删除"
|
||||
loading: "正在加载中"
|
||||
@@ -366,6 +391,9 @@ common/views/pages/explore.vue:
|
||||
federated: "联合"
|
||||
explore: "查找{host}"
|
||||
users-info: "当前有{users}个注册用户"
|
||||
common/views/components/reactions-viewer.details.vue:
|
||||
few-users: "{users}作出了{reaction}的回应"
|
||||
many-users: "{users}和其他{omitted}人做出了{reaction}的回应"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "打开播放器"
|
||||
disable-player: "关闭播放器"
|
||||
@@ -531,6 +559,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "取消置顶"
|
||||
delete: "删除"
|
||||
delete-confirm: "确定删除这个投稿吗?"
|
||||
delete-and-edit: "删除和编辑"
|
||||
delete-and-edit-confirm: "要删除此帖并再次编辑吗?对此帖的所有回应,转推和回复也将被删除。"
|
||||
remote: "显示原始投稿"
|
||||
pin-limit-exceeded: "无法置顶更多了。"
|
||||
common/views/components/user-menu.vue:
|
||||
@@ -726,6 +756,8 @@ common/views/components/profile-editor.vue:
|
||||
uploading: "正在上传"
|
||||
upload-failed: "上传失败"
|
||||
unable-to-process: "无法完成操作"
|
||||
avatar-not-an-image: "选择的头像文件不是图片格式"
|
||||
banner-not-an-image: "选择的横幅背景不是图片格式"
|
||||
email: "邮件设置"
|
||||
email-address: "电子邮件地址"
|
||||
email-verified: "电子邮件地址已验证"
|
||||
@@ -745,6 +777,8 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "危险选项"
|
||||
delete-account: "删除帐户"
|
||||
account-deleted: "帐户已被删除。 数据会在一段时间之后清除。"
|
||||
profile-metadata: "个人资料补充信息"
|
||||
metadata-label: "标签"
|
||||
metadata-content: "内容"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "用户"
|
||||
@@ -897,7 +931,7 @@ desktop/views/components/drive.file.vue:
|
||||
copy-url: "复制链接"
|
||||
download: "下载"
|
||||
else-files: "其他"
|
||||
set-as-avatar: "设置为头像"
|
||||
set-as-avatar: "设为头像"
|
||||
set-as-banner: "设置为背景"
|
||||
open-in-app: "在应用程序中打开"
|
||||
add-app: "添加应用"
|
||||
@@ -1115,6 +1149,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "群组"
|
||||
follow-requests: "关注申请"
|
||||
admin: "管理"
|
||||
room: "房间"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "游戏"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@@ -1694,6 +1729,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "没有找到有关于“{q}”的帖子"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "选择文件"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "以{}登录"
|
||||
mobile/views/pages/user.vue:
|
||||
@@ -1784,6 +1821,7 @@ pages:
|
||||
read-page: "查看源"
|
||||
page-created: "页面已创建"
|
||||
page-updated: "页面已更新"
|
||||
name-already-exists: "该页面URL已存在"
|
||||
are-you-sure-delete: "是否删除此页面?"
|
||||
page-deleted: "该页面已被删除。"
|
||||
edit-this-page: "编辑此页面"
|
||||
@@ -2052,3 +2090,64 @@ pages:
|
||||
enviromentVariables: "环境变量"
|
||||
pageVariables: "页面元素"
|
||||
argVariables: "输入槽函数"
|
||||
room:
|
||||
add-furniture: "放置家具"
|
||||
translate: "移动"
|
||||
rotate: "旋转"
|
||||
exit: "返回"
|
||||
remove: "移除"
|
||||
save: "保存"
|
||||
saved: "已保存"
|
||||
clear: "清理"
|
||||
clear-confirm: "是否清除所有家具?"
|
||||
leave-confirm: "有尚未保存的修改。是否离开?"
|
||||
chooseImage: "选择图片"
|
||||
room-type: "房间类型"
|
||||
carpet-color: "地板颜色"
|
||||
rooms:
|
||||
default: "默认"
|
||||
washitsu: "和式房间"
|
||||
furnitures:
|
||||
milk: "牛奶纸箱"
|
||||
bed: "床"
|
||||
low-table: "矮桌"
|
||||
desk: "书桌"
|
||||
chair: "椅子"
|
||||
chair2: "椅子2"
|
||||
fan: "换气扇"
|
||||
pc: "电脑"
|
||||
plant: "观叶植物"
|
||||
plant2: "观叶植物2"
|
||||
eraser: "橡皮擦"
|
||||
pencil: "铅笔"
|
||||
pudding: "布丁"
|
||||
cardboard-box: "纸板箱"
|
||||
cardboard-box2: "纸板箱2"
|
||||
cardboard-box3: "纸板箱3"
|
||||
book: "书"
|
||||
book2: "书2"
|
||||
piano: "钢琴"
|
||||
facial-tissue: "纸巾盒"
|
||||
server: "服务器"
|
||||
moon: "月球"
|
||||
corkboard: "软木板"
|
||||
mousepad: "鼠标垫"
|
||||
monitor: "显示器"
|
||||
keyboard: "键盘"
|
||||
carpet-stripe: "地毯(条纹)"
|
||||
mat: "垫子"
|
||||
color-box: "收纳柜"
|
||||
wall-clock: "挂钟"
|
||||
photoframe: "相框"
|
||||
cube: "立方体"
|
||||
tv: "电视"
|
||||
pinguin: "企鹅君"
|
||||
rubik-cube: "魔方"
|
||||
poster-h: "海报(横向)"
|
||||
poster-v: "海报(纵向)"
|
||||
sofa: "沙发"
|
||||
spiral: "螺旋楼梯"
|
||||
bin: "垃圾箱"
|
||||
cup-noodle: "杯面"
|
||||
holo-display: "全息显示器"
|
||||
energy-drink: "能量饮料"
|
||||
|
@@ -88,3 +88,6 @@ admin/views/charts.vue:
|
||||
drive: "雲端硬碟"
|
||||
pages:
|
||||
like: "贊"
|
||||
room:
|
||||
furnitures:
|
||||
moon: "月"
|
||||
|
13
migration/1563757595828-UsedUsername.ts
Normal file
13
migration/1563757595828-UsedUsername.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class UsedUsername1563757595828 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "used_username" ("username" character varying(128) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_78fd79d2d24c6ac2f4cc9a31a5d" PRIMARY KEY ("username"))`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`DROP TABLE "used_username"`);
|
||||
}
|
||||
|
||||
}
|
13
migration/1565634203341-room.ts
Normal file
13
migration/1565634203341-room.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class room1565634203341 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`);
|
||||
}
|
||||
|
||||
}
|
85
package.json
85
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.26.1",
|
||||
"version": "11.31.4",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -30,7 +30,7 @@
|
||||
"lodash": "^4.17.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "7.1.0",
|
||||
"@elastic/elasticsearch": "7.3.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.19",
|
||||
"@fortawesome/free-brands-svg-icons": "5.9.0",
|
||||
"@fortawesome/free-regular-svg-icons": "5.9.0",
|
||||
@@ -66,7 +66,6 @@
|
||||
"@types/koa-views": "2.0.3",
|
||||
"@types/koa__cors": "2.2.3",
|
||||
"@types/lolex": "3.1.1",
|
||||
"@types/minio": "7.0.2",
|
||||
"@types/mocha": "5.2.7",
|
||||
"@types/node": "12.0.10",
|
||||
"@types/nodemailer": "6.2.0",
|
||||
@@ -99,40 +98,42 @@
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"@typescript-eslint/parser": "1.11.0",
|
||||
"animejs": "3.0.1",
|
||||
"apexcharts": "3.8.2",
|
||||
"agentkeepalive": "4.0.2",
|
||||
"animejs": "3.1.0",
|
||||
"apexcharts": "3.8.5",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.520.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "2.0.0-rc.13",
|
||||
"bull": "3.10.0",
|
||||
"cafy": "15.1.1",
|
||||
"cbor": "4.1.5",
|
||||
"cbor": "4.3.0",
|
||||
"chai": "4.2.0",
|
||||
"chalk": "2.4.2",
|
||||
"cli-highlight": "2.1.1",
|
||||
"commander": "2.20.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "3.0.0",
|
||||
"css-loader": "3.2.0",
|
||||
"cssnano": "4.1.10",
|
||||
"dateformat": "3.0.3",
|
||||
"deep-equal": "1.0.1",
|
||||
"diskusage": "1.1.3",
|
||||
"double-ended-queue": "2.1.0-0",
|
||||
"emojilib": "2.4.0",
|
||||
"eslint": "6.0.1",
|
||||
"eslint": "6.1.0",
|
||||
"eslint-plugin-vue": "5.2.3",
|
||||
"eventemitter3": "4.0.0",
|
||||
"feed": "3.0.0",
|
||||
"file-type": "12.0.1",
|
||||
"file-type": "12.2.0",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"fuckadblock": "3.2.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
"gulp-imagemin": "6.0.0",
|
||||
"gulp-imagemin": "6.1.0",
|
||||
"gulp-mocha": "6.0.0",
|
||||
"gulp-rename": "1.4.0",
|
||||
"gulp-replace": "1.0.0",
|
||||
@@ -145,6 +146,7 @@
|
||||
"hard-source-webpack-plugin": "0.13.1",
|
||||
"html-minifier": "4.0.0",
|
||||
"http-signature": "1.2.0",
|
||||
"https-proxy-agent": "2.2.2",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"is-root": "2.1.0",
|
||||
"is-svg": "4.2.0",
|
||||
@@ -153,13 +155,13 @@
|
||||
"json5": "2.1.0",
|
||||
"json5-loader": "3.0.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"katex": "0.10.2",
|
||||
"koa": "2.7.0",
|
||||
"katex": "0.11.0",
|
||||
"koa": "2.8.1",
|
||||
"koa-bodyparser": "4.2.1",
|
||||
"koa-compress": "3.0.0",
|
||||
"koa-favicon": "2.0.1",
|
||||
"koa-json-body": "5.3.0",
|
||||
"koa-logger": "3.2.0",
|
||||
"koa-logger": "3.2.1",
|
||||
"koa-mount": "4.0.0",
|
||||
"koa-multer": "1.0.2",
|
||||
"koa-router": "7.4.0",
|
||||
@@ -168,38 +170,35 @@
|
||||
"koa-views": "6.2.0",
|
||||
"langmap": "0.0.16",
|
||||
"loader-utils": "1.2.3",
|
||||
"lolex": "4.1.0",
|
||||
"lolex": "4.2.0",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"minio": "7.0.10",
|
||||
"mocha": "6.1.4",
|
||||
"mocha": "6.2.0",
|
||||
"moji": "0.5.1",
|
||||
"moment": "2.24.0",
|
||||
"ms": "2.1.2",
|
||||
"nested-property": "1.0.1",
|
||||
"node-fetch": "2.6.0",
|
||||
"nodemailer": "6.2.1",
|
||||
"nodemailer": "6.3.0",
|
||||
"nprogress": "0.2.0",
|
||||
"object-assign-deep": "0.4.0",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "5.1.0",
|
||||
"parsimmon": "1.12.1",
|
||||
"pg": "7.11.0",
|
||||
"parsimmon": "1.13.0",
|
||||
"pg": "7.12.1",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"prismjs": "1.16.0",
|
||||
"prismjs": "1.17.1",
|
||||
"progress-bar-webpack-plugin": "1.12.1",
|
||||
"promise-any": "0.2.0",
|
||||
"promise-limit": "2.7.0",
|
||||
"promise-sequential": "1.1.1",
|
||||
"pug": "2.0.4",
|
||||
"punycode": "2.1.1",
|
||||
"pureimage": "0.1.6",
|
||||
"qrcode": "1.4.0",
|
||||
"qrcode": "1.4.1",
|
||||
"random-seed": "0.3.0",
|
||||
"randomcolor": "0.5.4",
|
||||
"ratelimiter": "3.3.0",
|
||||
"ratelimiter": "3.3.1",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "4.1.10",
|
||||
"reconnecting-websocket": "4.2.0",
|
||||
"redis": "2.8.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rename": "1.0.4",
|
||||
@@ -210,20 +209,22 @@
|
||||
"rimraf": "2.6.3",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"seedrandom": "3.0.1",
|
||||
"sharp": "0.22.1",
|
||||
"seedrandom": "3.0.3",
|
||||
"sharp": "0.23.0",
|
||||
"showdown": "1.9.0",
|
||||
"showdown-highlightjs-extension": "0.1.2",
|
||||
"speakeasy": "2.0.0",
|
||||
"stringz": "2.0.0",
|
||||
"style-loader": "0.23.1",
|
||||
"stylus": "0.54.5",
|
||||
"stylus": "0.54.7",
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.3.0",
|
||||
"systeminformation": "4.14.3",
|
||||
"summaly": "2.3.1",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "4.14.8",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.3.0",
|
||||
"terser-webpack-plugin": "1.4.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.108.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.1.0",
|
||||
"ts-loader": "5.3.3",
|
||||
@@ -231,37 +232,37 @@
|
||||
"tslint": "5.18.0",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typeorm": "0.2.18",
|
||||
"typescript": "3.5.2",
|
||||
"typescript": "3.5.3",
|
||||
"uglify-es": "3.3.9",
|
||||
"ulid": "2.3.0",
|
||||
"url-loader": "2.0.1",
|
||||
"uuid": "3.3.2",
|
||||
"url-loader": "2.1.0",
|
||||
"uuid": "3.3.3",
|
||||
"v-animate-css": "0.0.3",
|
||||
"v-debounce": "0.1.2",
|
||||
"vue": "2.6.10",
|
||||
"vue-color": "2.7.0",
|
||||
"vue-content-loading": "1.6.0",
|
||||
"vue-cropperjs": "4.0.0",
|
||||
"vue-i18n": "8.12.0",
|
||||
"vue-i18n": "8.14.0",
|
||||
"vue-js-modal": "1.3.31",
|
||||
"vue-json-pretty": "1.6.0",
|
||||
"vue-loader": "15.7.0",
|
||||
"vue-json-pretty": "1.6.1",
|
||||
"vue-loader": "15.7.1",
|
||||
"vue-marquee-text-component": "1.1.1",
|
||||
"vue-prism-component": "1.1.1",
|
||||
"vue-router": "3.0.7",
|
||||
"vue-router": "3.1.2",
|
||||
"vue-sequential-entrance": "1.1.3",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.2.16",
|
||||
"vue-svg-inline-loader": "1.2.17",
|
||||
"vue-template-compiler": "2.6.10",
|
||||
"vuedraggable": "2.23.0",
|
||||
"vuewordcloud": "18.7.11",
|
||||
"vuex": "3.1.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.5",
|
||||
"webpack": "4.35.3",
|
||||
"webpack-cli": "3.3.5",
|
||||
"webpack": "4.39.3",
|
||||
"webpack-cli": "3.3.7",
|
||||
"websocket": "1.0.29",
|
||||
"ws": "7.1.0",
|
||||
"ws": "7.1.2",
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
20
src/@types/lookup-dns-cache.d.ts
vendored
20
src/@types/lookup-dns-cache.d.ts
vendored
@@ -1,17 +1,9 @@
|
||||
declare module 'lookup-dns-cache' {
|
||||
type IPv4 = 4;
|
||||
import { LookupOneOptions, LookupAllOptions, LookupOptions, LookupAddress } from 'dns';
|
||||
|
||||
type IPv6 = 6;
|
||||
|
||||
type Family = IPv4 | IPv6 | undefined;
|
||||
|
||||
interface IRunOptions {
|
||||
family?: Family;
|
||||
all?: boolean;
|
||||
}
|
||||
|
||||
type RunCallback = (error: Error | null, address?: string | string[], family?: Family) => void;
|
||||
|
||||
export function lookup(hostname: string, options: IRunOptions | Family, callback: RunCallback): {} | undefined;
|
||||
export function lookup(hostname: string, callback: RunCallback): {} | undefined;
|
||||
function lookup(hostname: string, family: number, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
|
||||
function lookup(hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
|
||||
function lookup(hostname: string, options: LookupAllOptions, callback: (err: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void): void;
|
||||
function lookup(hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family: number) => void): void;
|
||||
function lookup(hostname: string, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
|
||||
}
|
||||
|
7
src/@types/promise-any.d.ts
vendored
7
src/@types/promise-any.d.ts
vendored
@@ -1,7 +0,0 @@
|
||||
declare module 'promise-any' {
|
||||
function promiseAny<T>(iterable: Iterable<T | PromiseLike<T>>): Promise<T>;
|
||||
|
||||
namespace promiseAny {} // Hack
|
||||
|
||||
export = promiseAny;
|
||||
}
|
@@ -159,7 +159,7 @@ async function init(): Promise<Config> {
|
||||
return config;
|
||||
}
|
||||
|
||||
async function spawnWorkers(limit: number = Infinity) {
|
||||
async function spawnWorkers(limit: number = 1) {
|
||||
const workers = Math.min(limit, os.cpus().length);
|
||||
bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
|
||||
await Promise.all([...Array(workers)].map(spawnWorker));
|
||||
|
@@ -19,7 +19,8 @@ init(launch => {
|
||||
mode: 'history',
|
||||
base: '/admin/',
|
||||
routes: [
|
||||
{ path: '/', component: Index },
|
||||
{ path: '/:page', component: Index },
|
||||
{ path: '/', redirect: '/dashboard' },
|
||||
{ path: '*', component: NotFound }
|
||||
]
|
||||
});
|
||||
|
@@ -18,18 +18,18 @@
|
||||
<p class="name"><mk-user-name :user="$store.state.i"/></p>
|
||||
</div>
|
||||
<ul>
|
||||
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
|
||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||
<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
|
||||
<li @click="nav('logs')" :class="{ active: page == 'logs' }"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</li>
|
||||
<li @click="nav('db')" :class="{ active: page == 'db' }"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</li>
|
||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||
<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
|
||||
<li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</li>
|
||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
||||
<li @click="nav('abuse')" :class="{ active: page == 'abuse' }"><fa :icon="faExclamationCircle" fixed-width/>{{ $t('abuse') }}</li>
|
||||
<li><router-link to="/dashboard" active-class="active"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</router-link></li>
|
||||
<li><router-link to="/instance" active-class="active"><fa icon="cog" fixed-width/>{{ $t('instance') }}</router-link></li>
|
||||
<li><router-link to="/queue" active-class="active"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</router-link></li>
|
||||
<li><router-link to="/logs" active-class="active"><fa :icon="faStream" fixed-width/>{{ $t('logs') }}</router-link></li>
|
||||
<li><router-link to="/db" active-class="active"><fa :icon="faDatabase" fixed-width/>{{ $t('db') }}</router-link></li>
|
||||
<li><router-link to="/moderators" active-class="active"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</router-link></li>
|
||||
<li><router-link to="/users" active-class="active"><fa icon="users" fixed-width/>{{ $t('users') }}</router-link></li>
|
||||
<li><router-link to="/drive" active-class="active"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</router-link></li>
|
||||
<li><router-link to="/federation" active-class="active"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</router-link></li>
|
||||
<li><router-link to="/emoji" active-class="active"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</router-link></li>
|
||||
<li><router-link to="/announcements" active-class="active"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</router-link></li>
|
||||
<li><router-link to="/abuse" active-class="active"><fa :icon="faExclamationCircle" fixed-width/>{{ $t('abuse') }}</router-link></li>
|
||||
</ul>
|
||||
<div class="back-to-misskey">
|
||||
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
|
||||
@@ -102,7 +102,6 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: 'dashboard',
|
||||
version,
|
||||
isMobile,
|
||||
navOpend: !isMobile,
|
||||
@@ -116,9 +115,9 @@ export default Vue.extend({
|
||||
faDatabase,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
nav(page: string) {
|
||||
this.page = page;
|
||||
computed: {
|
||||
page() {
|
||||
return this.$route.params.page;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -240,11 +239,10 @@ export default Vue.extend({
|
||||
list-style none
|
||||
font-size 15px
|
||||
|
||||
> li
|
||||
> li > a
|
||||
display block
|
||||
padding 10px 16px
|
||||
margin 0
|
||||
cursor pointer
|
||||
user-select none
|
||||
color #eee
|
||||
transition margin-left 0.2s ease
|
||||
|
@@ -3,6 +3,8 @@ import { sum, unique } from '../../../../prelude/array';
|
||||
import shouldMuteNote from './should-mute-note';
|
||||
import MkNoteMenu from '../views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../views/components/reaction-picker.vue';
|
||||
import pleaseLogin from './please-login';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
function focus(el, fn) {
|
||||
const target = fn(el);
|
||||
@@ -20,10 +22,13 @@ type Opts = {
|
||||
};
|
||||
|
||||
export default (opts: Opts = {}) => ({
|
||||
i18n: i18n(),
|
||||
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
hideThisNote: false
|
||||
hideThisNote: false,
|
||||
openingMenu: false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -108,6 +113,7 @@ export default (opts: Opts = {}) => ({
|
||||
|
||||
methods: {
|
||||
reply(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.$root.$post({
|
||||
reply: this.appearNote,
|
||||
animation: !viaKeyboard,
|
||||
@@ -118,6 +124,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
renote(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.$root.$post({
|
||||
renote: this.appearNote,
|
||||
animation: !viaKeyboard,
|
||||
@@ -134,6 +141,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
react(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.blur();
|
||||
this.$root.new(MkReactionPicker, {
|
||||
source: this.$refs.reactButton,
|
||||
@@ -159,6 +167,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
favorite() {
|
||||
pleaseLogin(this.$root);
|
||||
this.$root.api('notes/favorites/create', {
|
||||
noteId: this.appearNote.id
|
||||
}).then(() => {
|
||||
@@ -170,17 +179,30 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
del() {
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.appearNote.id
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('@.delete-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.appearNote.id
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
menu(viaKeyboard = false) {
|
||||
if (this.openingMenu) return;
|
||||
this.openingMenu = true;
|
||||
this.$root.new(MkNoteMenu, {
|
||||
source: this.$refs.menuButton,
|
||||
note: this.appearNote,
|
||||
animation: !viaKeyboard
|
||||
}).$once('closed', this.focus);
|
||||
}).$once('closed', () => {
|
||||
this.openingMenu = false;
|
||||
this.focus();
|
||||
});
|
||||
},
|
||||
|
||||
toggleShowContent() {
|
||||
|
@@ -44,13 +44,21 @@ export default (opts) => ({
|
||||
return window.scrollY <= 8;
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', this.onWindowScroll, { passive: true });
|
||||
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||
} else if (opts.isContainer) {
|
||||
this.isScrollTop = () => {
|
||||
return this.$el.scrollTop <= 8;
|
||||
};
|
||||
|
||||
this.$el.addEventListener('scroll', this.onScroll, { passive: true });
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (opts.captureWindowScroll) {
|
||||
window.removeEventListener('scroll', this.onWindowScroll);
|
||||
window.removeEventListener('scroll', this.onScroll);
|
||||
} else if (opts.isContainer) {
|
||||
this.$el.removeEventListener('scroll', this.onScroll);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -67,6 +75,7 @@ export default (opts) => ({
|
||||
|
||||
async init() {
|
||||
this.fetching = true;
|
||||
if (opts.beforeInit) opts.beforeInit(this);
|
||||
let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;
|
||||
if (params && params.then) params = await params;
|
||||
await this.$root.api(this.pagination.endpoint, {
|
||||
@@ -151,7 +160,7 @@ export default (opts) => ({
|
||||
this.queue = [];
|
||||
},
|
||||
|
||||
onWindowScroll() {
|
||||
onScroll() {
|
||||
if (this.isScrollTop()) {
|
||||
this.onTop();
|
||||
}
|
||||
@@ -162,8 +171,10 @@ export default (opts) => ({
|
||||
// http://d.hatena.ne.jp/favril/20091105/1257403319
|
||||
if (this.$el.offsetHeight == 0) return;
|
||||
|
||||
const current = window.scrollY + window.innerHeight;
|
||||
if (current > document.body.offsetHeight - 8) this.onBottom();
|
||||
const bottomPosition = opts.isContainer ? this.$el.scrollHeight : document.body.offsetHeight;
|
||||
|
||||
const currentBottomPosition = opts.isContainer ? this.$el.scrollTop + this.$el.clientHeight : window.scrollY + window.innerHeight;
|
||||
if (currentBottomPosition > (bottomPosition - 8)) this.onBottom();
|
||||
}
|
||||
},
|
||||
|
||||
|
10
src/client/app/common/scripts/please-login.ts
Normal file
10
src/client/app/common/scripts/please-login.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export default ($root: any) => {
|
||||
if ($root.$store.getters.isSignedIn) return;
|
||||
|
||||
$root.dialog({
|
||||
title: $root.$t('@.signin-required'),
|
||||
text: null
|
||||
});
|
||||
|
||||
throw new Error('signin required');
|
||||
};
|
@@ -35,6 +35,10 @@ export default (opts) => ({
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
initialNote: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
instant: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
@@ -149,6 +153,10 @@ export default (opts) => ({
|
||||
// デフォルト公開範囲
|
||||
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility);
|
||||
|
||||
if (this.reply && this.reply.localOnly) {
|
||||
this.localOnly = true;
|
||||
}
|
||||
|
||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||
if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {
|
||||
this.visibility = this.reply.visibility;
|
||||
@@ -158,13 +166,13 @@ export default (opts) => ({
|
||||
}).then(users => {
|
||||
this.visibleUsers.push(...users);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.reply && this.reply.userId !== this.$store.state.i.id) {
|
||||
this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
|
||||
this.visibleUsers.push(user);
|
||||
});
|
||||
if (this.reply.userId !== this.$store.state.i.id) {
|
||||
this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
|
||||
this.visibleUsers.push(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keep cw when reply
|
||||
@@ -196,6 +204,29 @@ export default (opts) => ({
|
||||
}
|
||||
}
|
||||
|
||||
// 削除して編集
|
||||
if (this.initialNote) {
|
||||
const init = this.initialNote;
|
||||
this.text = init.text ? init.text : '';
|
||||
this.files = init.files;
|
||||
this.cw = init.cw;
|
||||
this.useCw = init.cw != null;
|
||||
if (init.poll) {
|
||||
this.poll = true;
|
||||
this.$nextTick(() => {
|
||||
(this.$refs.poll as any).set({
|
||||
choices: init.poll.choices.map(c => c.text),
|
||||
multiple: init.poll.multiple
|
||||
});
|
||||
});
|
||||
}
|
||||
// hack 位置情報投稿が動くようになったら適用する
|
||||
this.geo = null;
|
||||
this.visibility = init.visibility;
|
||||
this.localOnly = init.localOnly;
|
||||
this.quoteId = init.renote ? init.renote.id : null;
|
||||
}
|
||||
|
||||
this.$nextTick(() => this.watch());
|
||||
});
|
||||
},
|
||||
@@ -292,7 +323,7 @@ export default (opts) => ({
|
||||
setVisibility() {
|
||||
const w = this.$root.new(MkVisibilityChooser, {
|
||||
source: this.$refs.visibilityButton,
|
||||
currentVisibility: this.visibility
|
||||
currentVisibility: this.localOnly ? `local-${this.visibility}` : this.visibility
|
||||
});
|
||||
w.$once('chosen', v => {
|
||||
this.applyVisibility(v);
|
||||
|
21
src/client/app/common/scripts/room/furniture.ts
Normal file
21
src/client/app/common/scripts/room/furniture.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export type RoomInfo = {
|
||||
roomType: string;
|
||||
carpetColor: string;
|
||||
furnitures: Furniture[];
|
||||
};
|
||||
|
||||
export type Furniture = {
|
||||
id: string; // 同じ家具が複数ある場合にそれぞれを識別するためのIDであり、家具IDではない
|
||||
type: string; // こっちが家具ID(chairとか)
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
rotation: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
props?: Record<string, any>;
|
||||
};
|
397
src/client/app/common/scripts/room/furnitures.json5
Normal file
397
src/client/app/common/scripts/room/furnitures.json5
Normal file
@@ -0,0 +1,397 @@
|
||||
// 家具メタデータ
|
||||
|
||||
// 家具にはユーザーが設定できるプロパティを設定可能です:
|
||||
//
|
||||
// props: {
|
||||
// <propname>: <proptype>
|
||||
// }
|
||||
//
|
||||
// proptype一覧:
|
||||
// * image ... 画像選択ダイアログを出し、その画像のURLが格納されます
|
||||
// * color ... 色選択コントロールを出し、選択された色が格納されます
|
||||
|
||||
// 家具にカスタムテクスチャを適用できるようにするには、textureプロパティに以下の追加の情報を含めます:
|
||||
// 便宜上そのUVのどの部分にカスタムテクスチャを貼り合わせるかのエリアをテクスチャエリアと呼びます。
|
||||
// UVは1024*1024だと仮定します。
|
||||
//
|
||||
// <key>: {
|
||||
// prop: <プロパティ名>,
|
||||
// uv: {
|
||||
// x: <テクスチャエリアX座標>,
|
||||
// y: <テクスチャエリアY座標>,
|
||||
// width: <テクスチャエリアの幅>,
|
||||
// height: <テクスチャエリアの高さ>,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// <key>には、カスタムテクスチャを適用したいメッシュ名を指定します
|
||||
// <プロパティ名>には、カスタムテクスチャとして使用する画像を格納するプロパティ(前述)名を指定します
|
||||
|
||||
// 家具にカスタムカラーを適用できるようにするには、colorプロパティに以下の追加の情報を含めます:
|
||||
//
|
||||
// <key>: <プロパティ名>
|
||||
//
|
||||
// <key>には、カスタムカラーを適用したいマテリアル名を指定します
|
||||
// <プロパティ名>には、カスタムカラーとして使用する色を格納するプロパティ(前述)名を指定します
|
||||
|
||||
[
|
||||
{
|
||||
id: "milk",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "bed",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "low-table",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Table: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "desk",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Board: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "chair",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Chair: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "chair2",
|
||||
place: "floor",
|
||||
props: {
|
||||
color1: 'color',
|
||||
color2: 'color'
|
||||
},
|
||||
color: {
|
||||
Cushion: 'color1',
|
||||
Leg: 'color2'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "fan",
|
||||
place: "wall"
|
||||
},
|
||||
{
|
||||
id: "pc",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "plant",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "plant2",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "eraser",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "pencil",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "pudding",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "cardboard-box",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "cardboard-box2",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "cardboard-box3",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "book",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Cover: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "book2",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "piano",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "facial-tissue",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "server",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "moon",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "corkboard",
|
||||
place: "wall"
|
||||
},
|
||||
{
|
||||
id: "mousepad",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Pad: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "monitor",
|
||||
place: "floor",
|
||||
props: {
|
||||
screen: 'image'
|
||||
},
|
||||
texture: {
|
||||
Screen: {
|
||||
prop: 'screen',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 434,
|
||||
width: 1024,
|
||||
height: 588,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "tv",
|
||||
place: "floor",
|
||||
props: {
|
||||
screen: 'image'
|
||||
},
|
||||
texture: {
|
||||
Screen: {
|
||||
prop: 'screen',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 434,
|
||||
width: 1024,
|
||||
height: 588,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "keyboard",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "carpet-stripe",
|
||||
place: "floor",
|
||||
props: {
|
||||
color1: 'color',
|
||||
color2: 'color'
|
||||
},
|
||||
color: {
|
||||
CarpetAreaA: 'color1',
|
||||
CarpetAreaB: 'color2'
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "mat",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Mat: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "color-box",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
main: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "wall-clock",
|
||||
place: "wall"
|
||||
},
|
||||
{
|
||||
id: "cube",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Cube: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "photoframe",
|
||||
place: "wall",
|
||||
props: {
|
||||
photo: 'image',
|
||||
color: 'color'
|
||||
},
|
||||
texture: {
|
||||
Photo: {
|
||||
prop: 'photo',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 342,
|
||||
width: 1024,
|
||||
height: 683,
|
||||
},
|
||||
},
|
||||
},
|
||||
color: {
|
||||
Frame: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "pinguin",
|
||||
place: "floor",
|
||||
props: {
|
||||
body: 'color',
|
||||
belly: 'color'
|
||||
},
|
||||
color: {
|
||||
Body: 'body',
|
||||
Belly: 'belly',
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "rubik-cube",
|
||||
place: "floor",
|
||||
},
|
||||
{
|
||||
id: "poster-h",
|
||||
place: "wall",
|
||||
props: {
|
||||
picture: 'image'
|
||||
},
|
||||
texture: {
|
||||
Poster: {
|
||||
prop: 'picture',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 277,
|
||||
width: 1024,
|
||||
height: 745,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "poster-v",
|
||||
place: "wall",
|
||||
props: {
|
||||
picture: 'image'
|
||||
},
|
||||
texture: {
|
||||
Poster: {
|
||||
prop: 'picture',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 745,
|
||||
height: 1024,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "sofa",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Sofa: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "spiral",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Step: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "bin",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Bin: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "cup-noodle",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "holo-display",
|
||||
place: "floor",
|
||||
props: {
|
||||
image: 'image'
|
||||
},
|
||||
texture: {
|
||||
Image_Front: {
|
||||
prop: 'image',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
},
|
||||
},
|
||||
Image_Back: {
|
||||
prop: 'image',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'energy-drink',
|
||||
place: "floor",
|
||||
}
|
||||
]
|
776
src/client/app/common/scripts/room/room.ts
Normal file
776
src/client/app/common/scripts/room/room.ts
Normal file
@@ -0,0 +1,776 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
|
||||
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
||||
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
|
||||
import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass.js';
|
||||
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
|
||||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
||||
import { Furniture, RoomInfo } from './furniture';
|
||||
import { query as urlQuery } from '../../../../../prelude/url';
|
||||
const furnitureDefs = require('./furnitures.json5');
|
||||
|
||||
THREE.ImageUtils.crossOrigin = '';
|
||||
|
||||
type Options = {
|
||||
graphicsQuality: Room['graphicsQuality'];
|
||||
onChangeSelect: Room['onChangeSelect'];
|
||||
useOrthographicCamera: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* MisskeyRoom Core Engine
|
||||
*/
|
||||
export class Room {
|
||||
private clock: THREE.Clock;
|
||||
private scene: THREE.Scene;
|
||||
private renderer: THREE.WebGLRenderer;
|
||||
private camera: THREE.PerspectiveCamera | THREE.OrthographicCamera;
|
||||
private controls: OrbitControls;
|
||||
private composer: EffectComposer;
|
||||
private mixers: THREE.AnimationMixer[] = [];
|
||||
private furnitureControl: TransformControls;
|
||||
private roomInfo: RoomInfo;
|
||||
private graphicsQuality: 'cheep' | 'low' | 'medium' | 'high' | 'ultra';
|
||||
private roomObj: THREE.Object3D;
|
||||
private objects: THREE.Object3D[] = [];
|
||||
private selectedObject: THREE.Object3D = null;
|
||||
private onChangeSelect: Function;
|
||||
private isTransformMode = false;
|
||||
private renderFrameRequestId: number;
|
||||
|
||||
private get canvas(): HTMLCanvasElement {
|
||||
return this.renderer.domElement;
|
||||
}
|
||||
|
||||
private get furnitures(): Furniture[] {
|
||||
return this.roomInfo.furnitures;
|
||||
}
|
||||
|
||||
private set furnitures(furnitures: Furniture[]) {
|
||||
this.roomInfo.furnitures = furnitures;
|
||||
}
|
||||
|
||||
private get enableShadow() {
|
||||
return this.graphicsQuality != 'cheep';
|
||||
}
|
||||
|
||||
private get usePostFXs() {
|
||||
return this.graphicsQuality !== 'cheep' && this.graphicsQuality !== 'low';
|
||||
}
|
||||
|
||||
private get shadowQuality() {
|
||||
return (
|
||||
this.graphicsQuality === 'ultra' ? 16384 :
|
||||
this.graphicsQuality === 'high' ? 8192 :
|
||||
this.graphicsQuality === 'medium' ? 4096 :
|
||||
this.graphicsQuality === 'low' ? 1024 :
|
||||
0); // cheep
|
||||
}
|
||||
|
||||
constructor(user, isMyRoom, roomInfo: RoomInfo, container, options: Options) {
|
||||
this.roomInfo = roomInfo;
|
||||
this.graphicsQuality = options.graphicsQuality;
|
||||
this.onChangeSelect = options.onChangeSelect;
|
||||
|
||||
this.clock = new THREE.Clock(true);
|
||||
|
||||
//#region Init a scene
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
|
||||
//#region Init a renderer
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: false,
|
||||
stencil: false,
|
||||
alpha: false,
|
||||
powerPreference:
|
||||
this.graphicsQuality === 'ultra' ? 'high-performance' :
|
||||
this.graphicsQuality === 'high' ? 'high-performance' :
|
||||
this.graphicsQuality === 'medium' ? 'default' :
|
||||
this.graphicsQuality === 'low' ? 'low-power' :
|
||||
'low-power' // cheep
|
||||
});
|
||||
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
this.renderer.setSize(width, height);
|
||||
this.renderer.autoClear = false;
|
||||
this.renderer.setClearColor(new THREE.Color(0x051f2d));
|
||||
this.renderer.shadowMap.enabled = this.enableShadow;
|
||||
this.renderer.shadowMap.type =
|
||||
this.graphicsQuality === 'ultra' ? THREE.PCFSoftShadowMap :
|
||||
this.graphicsQuality === 'high' ? THREE.PCFSoftShadowMap :
|
||||
this.graphicsQuality === 'medium' ? THREE.PCFShadowMap :
|
||||
this.graphicsQuality === 'low' ? THREE.BasicShadowMap :
|
||||
THREE.BasicShadowMap; // cheep
|
||||
|
||||
container.appendChild(this.canvas);
|
||||
//#endregion
|
||||
|
||||
//#region Init a camera
|
||||
this.camera = options.useOrthographicCamera
|
||||
? new THREE.OrthographicCamera(
|
||||
width / - 2, width / 2, height / 2, height / - 2, -10, 10)
|
||||
: new THREE.PerspectiveCamera(45, width / height);
|
||||
|
||||
if (options.useOrthographicCamera) {
|
||||
this.camera.position.x = 2;
|
||||
this.camera.position.y = 2;
|
||||
this.camera.position.z = 2;
|
||||
this.camera.zoom = 100;
|
||||
this.camera.updateProjectionMatrix();
|
||||
} else {
|
||||
this.camera.position.x = 5;
|
||||
this.camera.position.y = 2;
|
||||
this.camera.position.z = 5;
|
||||
}
|
||||
|
||||
this.scene.add(this.camera);
|
||||
//#endregion
|
||||
|
||||
//#region AmbientLight
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
|
||||
this.scene.add(ambientLight);
|
||||
//#endregion
|
||||
|
||||
if (this.graphicsQuality !== 'cheep') {
|
||||
//#region Room light
|
||||
const roomLight = new THREE.SpotLight(0xffffff, 0.1);
|
||||
|
||||
roomLight.position.set(0, 8, 0);
|
||||
roomLight.castShadow = this.enableShadow;
|
||||
roomLight.shadow.bias = -0.0001;
|
||||
roomLight.shadow.mapSize.width = this.shadowQuality;
|
||||
roomLight.shadow.mapSize.height = this.shadowQuality;
|
||||
roomLight.shadow.camera.near = 0.1;
|
||||
roomLight.shadow.camera.far = 9;
|
||||
roomLight.shadow.camera.fov = 45;
|
||||
|
||||
this.scene.add(roomLight);
|
||||
//#endregion
|
||||
}
|
||||
|
||||
//#region Out light
|
||||
const outLight1 = new THREE.SpotLight(0xffffff, 0.4);
|
||||
outLight1.position.set(9, 3, -2);
|
||||
outLight1.castShadow = this.enableShadow;
|
||||
outLight1.shadow.bias = -0.001; // アクネ、アーチファクト対策 その代わりピーターパンが発生する可能性がある
|
||||
outLight1.shadow.mapSize.width = this.shadowQuality;
|
||||
outLight1.shadow.mapSize.height = this.shadowQuality;
|
||||
outLight1.shadow.camera.near = 6;
|
||||
outLight1.shadow.camera.far = 15;
|
||||
outLight1.shadow.camera.fov = 45;
|
||||
this.scene.add(outLight1);
|
||||
|
||||
const outLight2 = new THREE.SpotLight(0xffffff, 0.2);
|
||||
outLight2.position.set(-2, 3, 9);
|
||||
outLight2.castShadow = false;
|
||||
outLight2.shadow.bias = -0.001; // アクネ、アーチファクト対策 その代わりピーターパンが発生する可能性がある
|
||||
outLight2.shadow.camera.near = 6;
|
||||
outLight2.shadow.camera.far = 15;
|
||||
outLight2.shadow.camera.fov = 45;
|
||||
this.scene.add(outLight2);
|
||||
//#endregion
|
||||
|
||||
//#region Init a controller
|
||||
this.controls = new OrbitControls(this.camera, this.canvas);
|
||||
|
||||
this.controls.target.set(0, 1, 0);
|
||||
this.controls.enableZoom = true;
|
||||
this.controls.enablePan = isMyRoom;
|
||||
this.controls.minPolarAngle = 0;
|
||||
this.controls.maxPolarAngle = Math.PI / 2;
|
||||
this.controls.minAzimuthAngle = 0;
|
||||
this.controls.maxAzimuthAngle = Math.PI / 2;
|
||||
this.controls.enableDamping = true;
|
||||
this.controls.dampingFactor = 0.2;
|
||||
this.controls.mouseButtons.LEFT = 1;
|
||||
this.controls.mouseButtons.MIDDLE = 2;
|
||||
this.controls.mouseButtons.RIGHT = 0;
|
||||
//#endregion
|
||||
|
||||
//#region POST FXs
|
||||
if (!this.usePostFXs) {
|
||||
this.composer = null;
|
||||
} else {
|
||||
const renderTarget = new THREE.WebGLRenderTarget(width, height, {
|
||||
minFilter: THREE.LinearFilter,
|
||||
magFilter: THREE.LinearFilter,
|
||||
format: THREE.RGBFormat,
|
||||
stencilBuffer: false,
|
||||
});
|
||||
|
||||
const fxaa = new ShaderPass(FXAAShader);
|
||||
fxaa.uniforms['resolution'].value = new THREE.Vector2(1 / width, 1 / height);
|
||||
fxaa.renderToScreen = true;
|
||||
|
||||
this.composer = new EffectComposer(this.renderer, renderTarget);
|
||||
this.composer.addPass(new RenderPass(this.scene, this.camera));
|
||||
if (this.graphicsQuality === 'ultra') {
|
||||
this.composer.addPass(new BloomPass(0.25, 30, 128.0, 512));
|
||||
}
|
||||
this.composer.addPass(fxaa);
|
||||
}
|
||||
//#endregion
|
||||
//#endregion
|
||||
|
||||
//#region Label
|
||||
//#region Avatar
|
||||
const avatarUrl = `/proxy/?${urlQuery({ url: user.avatarUrl })}`;
|
||||
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
textureLoader.crossOrigin = 'anonymous';
|
||||
|
||||
const iconTexture = textureLoader.load(avatarUrl);
|
||||
iconTexture.wrapS = THREE.RepeatWrapping;
|
||||
iconTexture.wrapT = THREE.RepeatWrapping;
|
||||
iconTexture.anisotropy = 16;
|
||||
|
||||
const avatarMaterial = new THREE.MeshBasicMaterial({
|
||||
map: iconTexture,
|
||||
side: THREE.DoubleSide,
|
||||
alphaTest: 0.5
|
||||
});
|
||||
|
||||
const iconGeometry = new THREE.PlaneGeometry(1, 1);
|
||||
|
||||
const avatarObject = new THREE.Mesh(iconGeometry, avatarMaterial);
|
||||
avatarObject.position.set(-3, 2.5, 2);
|
||||
avatarObject.rotation.y = Math.PI / 2;
|
||||
avatarObject.castShadow = false;
|
||||
|
||||
this.scene.add(avatarObject);
|
||||
//#endregion
|
||||
|
||||
//#region Username
|
||||
const name = user.username;
|
||||
|
||||
new THREE.FontLoader().load('/assets/fonts/helvetiker_regular.typeface.json', font => {
|
||||
const nameGeometry = new THREE.TextGeometry(name, {
|
||||
size: 0.5,
|
||||
height: 0,
|
||||
curveSegments: 8,
|
||||
font: font,
|
||||
bevelThickness: 0,
|
||||
bevelSize: 0,
|
||||
bevelEnabled: false
|
||||
});
|
||||
|
||||
const nameMaterial = new THREE.MeshLambertMaterial({
|
||||
color: 0xffffff
|
||||
});
|
||||
|
||||
const nameObject = new THREE.Mesh(nameGeometry, nameMaterial);
|
||||
nameObject.position.set(-3, 2.25, 1.25);
|
||||
nameObject.rotation.y = Math.PI / 2;
|
||||
nameObject.castShadow = false;
|
||||
|
||||
this.scene.add(nameObject);
|
||||
});
|
||||
//#endregion
|
||||
//#endregion
|
||||
|
||||
//#region Interaction
|
||||
if (isMyRoom) {
|
||||
this.furnitureControl = new TransformControls(this.camera, this.canvas);
|
||||
this.scene.add(this.furnitureControl);
|
||||
|
||||
// Hover highlight
|
||||
this.canvas.onmousemove = this.onmousemove;
|
||||
|
||||
// Click
|
||||
this.canvas.onmousedown = this.onmousedown;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Init room
|
||||
this.loadRoom();
|
||||
//#endregion
|
||||
|
||||
//#region Load furnitures
|
||||
for (const furniture of this.furnitures) {
|
||||
this.loadFurniture(furniture).then(obj => {
|
||||
this.scene.add(obj.scene);
|
||||
this.objects.push(obj.scene);
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// Start render
|
||||
if (this.usePostFXs) {
|
||||
this.renderWithPostFXs();
|
||||
} else {
|
||||
this.renderWithoutPostFXs();
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private renderWithoutPostFXs() {
|
||||
this.renderFrameRequestId =
|
||||
window.requestAnimationFrame(this.renderWithoutPostFXs);
|
||||
|
||||
// Update animations
|
||||
const clock = this.clock.getDelta();
|
||||
for (const mixer of this.mixers) {
|
||||
mixer.update(clock);
|
||||
}
|
||||
|
||||
this.controls.update();
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
@autobind
|
||||
private renderWithPostFXs() {
|
||||
this.renderFrameRequestId =
|
||||
window.requestAnimationFrame(this.renderWithPostFXs);
|
||||
|
||||
// Update animations
|
||||
const clock = this.clock.getDelta();
|
||||
for (const mixer of this.mixers) {
|
||||
mixer.update(clock);
|
||||
}
|
||||
|
||||
this.controls.update();
|
||||
this.renderer.clear();
|
||||
this.composer.render();
|
||||
}
|
||||
|
||||
@autobind
|
||||
private loadRoom() {
|
||||
const type = this.roomInfo.roomType;
|
||||
new GLTFLoader().load(`/assets/room/rooms/${type}/${type}.glb`, gltf => {
|
||||
gltf.scene.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
|
||||
child.receiveShadow = this.enableShadow;
|
||||
|
||||
child.material = new THREE.MeshLambertMaterial({
|
||||
color: (child.material as THREE.MeshStandardMaterial).color,
|
||||
map: (child.material as THREE.MeshStandardMaterial).map,
|
||||
name: (child.material as THREE.MeshStandardMaterial).name,
|
||||
});
|
||||
|
||||
// 異方性フィルタリング
|
||||
if ((child.material as THREE.MeshLambertMaterial).map && this.graphicsQuality !== 'cheep') {
|
||||
(child.material as THREE.MeshLambertMaterial).map.minFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshLambertMaterial).map.magFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshLambertMaterial).map.anisotropy = 8;
|
||||
}
|
||||
});
|
||||
|
||||
gltf.scene.position.set(0, 0, 0);
|
||||
|
||||
this.scene.add(gltf.scene);
|
||||
this.roomObj = gltf.scene;
|
||||
if (this.roomInfo.roomType === 'default') {
|
||||
this.applyCarpetColor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private loadFurniture(furniture: Furniture) {
|
||||
const def = furnitureDefs.find(d => d.id === furniture.type);
|
||||
return new Promise<GLTF>((res, rej) => {
|
||||
const loader = new GLTFLoader();
|
||||
loader.load(`/assets/room/furnitures/${furniture.type}/${furniture.type}.glb`, gltf => {
|
||||
const model = gltf.scene;
|
||||
|
||||
// Load animation
|
||||
if (gltf.animations.length > 0) {
|
||||
const mixer = new THREE.AnimationMixer(model);
|
||||
this.mixers.push(mixer);
|
||||
for (const clip of gltf.animations) {
|
||||
mixer.clipAction(clip).play();
|
||||
}
|
||||
}
|
||||
|
||||
model.name = furniture.id;
|
||||
model.position.x = furniture.position.x;
|
||||
model.position.y = furniture.position.y;
|
||||
model.position.z = furniture.position.z;
|
||||
model.rotation.x = furniture.rotation.x;
|
||||
model.rotation.y = furniture.rotation.y;
|
||||
model.rotation.z = furniture.rotation.z;
|
||||
|
||||
model.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
child.castShadow = this.enableShadow;
|
||||
child.receiveShadow = this.enableShadow;
|
||||
(child.material as THREE.MeshStandardMaterial).metalness = 0;
|
||||
|
||||
// 異方性フィルタリング
|
||||
if ((child.material as THREE.MeshStandardMaterial).map && this.graphicsQuality !== 'cheep') {
|
||||
(child.material as THREE.MeshStandardMaterial).map.minFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshStandardMaterial).map.magFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshStandardMaterial).map.anisotropy = 8;
|
||||
}
|
||||
});
|
||||
|
||||
if (def.color) { // カスタムカラー
|
||||
this.applyCustomColor(model);
|
||||
}
|
||||
|
||||
if (def.texture) { // カスタムテクスチャ
|
||||
this.applyCustomTexture(model);
|
||||
}
|
||||
|
||||
res(gltf);
|
||||
}, null, rej);
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private applyCarpetColor() {
|
||||
this.roomObj.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
if (child.material &&
|
||||
(child.material as THREE.MeshStandardMaterial).name &&
|
||||
(child.material as THREE.MeshStandardMaterial).name === 'Carpet'
|
||||
) {
|
||||
const colorHex = parseInt(this.roomInfo.carpetColor.substr(1), 16);
|
||||
(child.material as THREE.MeshStandardMaterial).color.setHex(colorHex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private applyCustomColor(model: THREE.Object3D) {
|
||||
const furniture = this.furnitures.find(furniture => furniture.id === model.name);
|
||||
const def = furnitureDefs.find(d => d.id === furniture.type);
|
||||
if (def.color == null) return;
|
||||
model.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
for (const t of Object.keys(def.color)) {
|
||||
if (!child.material ||
|
||||
!(child.material as THREE.MeshStandardMaterial).name ||
|
||||
(child.material as THREE.MeshStandardMaterial).name !== t
|
||||
) continue;
|
||||
|
||||
const prop = def.color[t];
|
||||
const val = furniture.props ? furniture.props[prop] : undefined;
|
||||
|
||||
if (val == null) continue;
|
||||
|
||||
const colorHex = parseInt(val.substr(1), 16);
|
||||
(child.material as THREE.MeshStandardMaterial).color.setHex(colorHex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private applyCustomTexture(model: THREE.Object3D) {
|
||||
const furniture = this.furnitures.find(furniture => furniture.id === model.name);
|
||||
const def = furnitureDefs.find(d => d.id === furniture.type);
|
||||
if (def.texture == null) return;
|
||||
|
||||
model.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
for (const t of Object.keys(def.texture)) {
|
||||
if (child.name !== t) continue;
|
||||
|
||||
const prop = def.texture[t].prop;
|
||||
const val = furniture.props ? furniture.props[prop] : undefined;
|
||||
|
||||
if (val == null) continue;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.height = 1024;
|
||||
canvas.width = 1024;
|
||||
|
||||
child.material = new THREE.MeshLambertMaterial({
|
||||
emissive: 0x111111,
|
||||
side: THREE.DoubleSide,
|
||||
alphaTest: 0.5,
|
||||
});
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.onload = () => {
|
||||
const uvInfo = def.texture[t].uv;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img,
|
||||
0, 0, img.width, img.height,
|
||||
uvInfo.x, uvInfo.y, uvInfo.width, uvInfo.height);
|
||||
|
||||
const texture = new THREE.Texture(canvas);
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
texture.anisotropy = 16;
|
||||
texture.flipY = false;
|
||||
|
||||
(child.material as THREE.MeshLambertMaterial).map = texture;
|
||||
(child.material as THREE.MeshLambertMaterial).needsUpdate = true;
|
||||
(child.material as THREE.MeshLambertMaterial).map.needsUpdate = true;
|
||||
};
|
||||
img.src = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private onmousemove(ev: MouseEvent) {
|
||||
if (this.isTransformMode) return;
|
||||
|
||||
const rect = (ev.target as HTMLElement).getBoundingClientRect();
|
||||
const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.canvas.width) * 2 - 1;
|
||||
const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.canvas.height) * 2 + 1;
|
||||
const pos = new THREE.Vector2(x, y);
|
||||
|
||||
this.camera.updateMatrixWorld();
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(pos, this.camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(this.objects, true);
|
||||
|
||||
for (const object of this.objects) {
|
||||
if (this.isSelectedObject(object)) continue;
|
||||
object.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0x000000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersected = this.getRoot(intersects[0].object);
|
||||
if (this.isSelectedObject(intersected)) return;
|
||||
intersected.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0x191919);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private onmousedown(ev: MouseEvent) {
|
||||
if (this.isTransformMode) return;
|
||||
if (ev.target !== this.canvas || ev.button !== 0) return;
|
||||
|
||||
const rect = (ev.target as HTMLElement).getBoundingClientRect();
|
||||
const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.canvas.width) * 2 - 1;
|
||||
const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.canvas.height) * 2 + 1;
|
||||
const pos = new THREE.Vector2(x, y);
|
||||
|
||||
this.camera.updateMatrixWorld();
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(pos, this.camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(this.objects, true);
|
||||
|
||||
for (const object of this.objects) {
|
||||
object.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0x000000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const selectedObj = this.getRoot(intersects[0].object);
|
||||
this.selectFurniture(selectedObj);
|
||||
} else {
|
||||
this.selectedObject = null;
|
||||
this.onChangeSelect(null);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private getRoot(obj: THREE.Object3D): THREE.Object3D {
|
||||
let found = false;
|
||||
let x = obj.parent;
|
||||
while (!found) {
|
||||
if (x.parent.parent == null) {
|
||||
found = true;
|
||||
} else {
|
||||
x = x.parent;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private isSelectedObject(obj: THREE.Object3D): boolean {
|
||||
if (this.selectedObject == null) {
|
||||
return false;
|
||||
} else {
|
||||
return obj.name === this.selectedObject.name;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private selectFurniture(obj: THREE.Object3D) {
|
||||
this.selectedObject = obj;
|
||||
this.onChangeSelect(obj);
|
||||
obj.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0xff0000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 家具の移動/回転モードにします
|
||||
* @param type 移動か回転か
|
||||
*/
|
||||
@autobind
|
||||
public enterTransformMode(type: 'translate' | 'rotate') {
|
||||
this.isTransformMode = true;
|
||||
this.furnitureControl.setMode(type);
|
||||
this.furnitureControl.attach(this.selectedObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 家具の移動/回転モードを終了します
|
||||
*/
|
||||
@autobind
|
||||
public exitTransformMode() {
|
||||
this.isTransformMode = false;
|
||||
this.furnitureControl.detach();
|
||||
}
|
||||
|
||||
/**
|
||||
* 家具プロパティを更新します
|
||||
* @param key プロパティ名
|
||||
* @param value 値
|
||||
*/
|
||||
@autobind
|
||||
public updateProp(key: string, value: any) {
|
||||
const furniture = this.furnitures.find(furniture => furniture.id === this.selectedObject.name);
|
||||
if (furniture.props == null) furniture.props = {};
|
||||
furniture.props[key] = value;
|
||||
this.applyCustomColor(this.selectedObject);
|
||||
this.applyCustomTexture(this.selectedObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部屋に家具を追加します
|
||||
* @param type 家具の種類
|
||||
*/
|
||||
@autobind
|
||||
public addFurniture(type: string) {
|
||||
const furniture = {
|
||||
id: uuid(),
|
||||
type: type,
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
rotation: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
};
|
||||
|
||||
this.furnitures.push(furniture);
|
||||
|
||||
this.loadFurniture(furniture).then(obj => {
|
||||
this.scene.add(obj.scene);
|
||||
this.objects.push(obj.scene);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 現在選択されている家具を部屋から削除します
|
||||
*/
|
||||
@autobind
|
||||
public removeFurniture() {
|
||||
this.exitTransformMode();
|
||||
const obj = this.selectedObject;
|
||||
this.scene.remove(obj);
|
||||
this.objects = this.objects.filter(object => object.name !== obj.name);
|
||||
this.furnitures = this.furnitures.filter(furniture => furniture.id !== obj.name);
|
||||
this.selectedObject = null;
|
||||
this.onChangeSelect(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全ての家具を部屋から削除します
|
||||
*/
|
||||
@autobind
|
||||
public removeAllFurnitures() {
|
||||
this.exitTransformMode();
|
||||
for (const obj of this.objects) {
|
||||
this.scene.remove(obj);
|
||||
}
|
||||
this.objects = [];
|
||||
this.furnitures = [];
|
||||
this.selectedObject = null;
|
||||
this.onChangeSelect(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部屋の床の色を変更します
|
||||
* @param color 色
|
||||
*/
|
||||
@autobind
|
||||
public updateCarpetColor(color: string) {
|
||||
this.roomInfo.carpetColor = color;
|
||||
this.applyCarpetColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 部屋の種類を変更します
|
||||
* @param type 種類
|
||||
*/
|
||||
@autobind
|
||||
public changeRoomType(type: string) {
|
||||
this.roomInfo.roomType = type;
|
||||
this.scene.remove(this.roomObj);
|
||||
this.loadRoom();
|
||||
}
|
||||
|
||||
/**
|
||||
* 部屋データを取得します
|
||||
*/
|
||||
@autobind
|
||||
public getRoomInfo() {
|
||||
for (const obj of this.objects) {
|
||||
const furniture = this.furnitures.find(f => f.id === obj.name);
|
||||
furniture.position.x = obj.position.x;
|
||||
furniture.position.y = obj.position.y;
|
||||
furniture.position.z = obj.position.z;
|
||||
furniture.rotation.x = obj.rotation.x;
|
||||
furniture.rotation.y = obj.rotation.y;
|
||||
furniture.rotation.z = obj.rotation.z;
|
||||
}
|
||||
|
||||
return this.roomInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 選択されている家具を取得します
|
||||
*/
|
||||
@autobind
|
||||
public getSelectedObject() {
|
||||
return this.selectedObject;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public findFurnitureById(id: string) {
|
||||
return this.furnitures.find(furniture => furniture.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* レンダリングを終了します
|
||||
*/
|
||||
@autobind
|
||||
public destroy() {
|
||||
// Stop render loop
|
||||
window.cancelAnimationFrame(this.renderFrameRequestId);
|
||||
|
||||
this.controls.dispose();
|
||||
this.scene.dispose();
|
||||
}
|
||||
}
|
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<prism :inline="inline" :language="lang || 'js'">{{ code }}</prism>
|
||||
<x-prism :inline="inline" :language="prismLang">{{ code }}</x-prism>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import 'prismjs';
|
||||
import 'prismjs/themes/prism-okaidia.css';
|
||||
import Prism from 'vue-prism-component';
|
||||
import XPrism from 'vue-prism-component';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
Prism
|
||||
XPrism
|
||||
},
|
||||
props: {
|
||||
code: {
|
||||
@@ -25,6 +25,12 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
prismLang() {
|
||||
return Prism.languages[this.lang] ? this.lang : 'js';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -98,7 +98,7 @@ export default Vue.extend({
|
||||
return {
|
||||
inputValue: this.input && this.input.default ? this.input.default : null,
|
||||
userInputValue: null,
|
||||
selectedValue: this.select ? this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
|
||||
selectedValue: this.select ? this.select.default ? this.select.default : this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
|
||||
canOk: true,
|
||||
faTimesCircle, faQuestionCircle
|
||||
};
|
||||
|
@@ -57,7 +57,8 @@ export default Vue.extend({
|
||||
},
|
||||
fit: {
|
||||
type: String,
|
||||
required: true
|
||||
required: false,
|
||||
default: 'cover'
|
||||
},
|
||||
detail: {
|
||||
type: Boolean,
|
||||
|
@@ -35,7 +35,8 @@ export default Vue.extend({
|
||||
mounted() {
|
||||
//#region for Safari bug
|
||||
if (this.$refs.grid) {
|
||||
this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px` : '128px';
|
||||
this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px`
|
||||
: this.$store.state.device.inDeckMode ? '128px' : this.$root.isMobile ? '173px' : '287px';
|
||||
}
|
||||
//#endregion
|
||||
},
|
||||
|
@@ -139,6 +139,7 @@ export default Vue.extend({
|
||||
cursor pointer
|
||||
|
||||
> .content
|
||||
max-width 100%
|
||||
|
||||
> .is-deleted
|
||||
display block
|
||||
@@ -155,6 +156,7 @@ export default Vue.extend({
|
||||
padding 8px 16px
|
||||
overflow hidden
|
||||
overflow-wrap break-word
|
||||
word-break break-word
|
||||
font-size 1em
|
||||
color rgba(#000, 0.8)
|
||||
|
||||
|
@@ -22,66 +22,95 @@ export default Vue.extend({
|
||||
},
|
||||
computed: {
|
||||
items(): any[] {
|
||||
return [{
|
||||
icon: 'at',
|
||||
text: this.$t('mention'),
|
||||
action: this.mention
|
||||
}, null, {
|
||||
icon: 'info-circle',
|
||||
text: this.$t('detail'),
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: faCopy,
|
||||
text: this.$t('copy-content'),
|
||||
action: this.copyContent
|
||||
}, {
|
||||
icon: 'link',
|
||||
text: this.$t('copy-link'),
|
||||
action: this.copyLink
|
||||
}, this.note.uri ? {
|
||||
icon: 'external-link-square-alt',
|
||||
text: this.$t('remote'),
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
} : undefined,
|
||||
null,
|
||||
this.isFavorited ? {
|
||||
icon: 'star',
|
||||
text: this.$t('unfavorite'),
|
||||
action: () => this.toggleFavorite(false)
|
||||
} : {
|
||||
icon: 'star',
|
||||
text: this.$t('favorite'),
|
||||
action: () => this.toggleFavorite(true)
|
||||
},
|
||||
this.note.userId != this.$store.state.i.id ? this.isWatching ? {
|
||||
icon: faEyeSlash,
|
||||
text: this.$t('unwatch'),
|
||||
action: () => this.toggleWatch(false)
|
||||
} : {
|
||||
icon: faEye,
|
||||
text: this.$t('watch'),
|
||||
action: () => this.toggleWatch(true)
|
||||
} : undefined,
|
||||
this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('unpin'),
|
||||
action: () => this.togglePin(false)
|
||||
} : {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('pin'),
|
||||
action: () => this.togglePin(true)
|
||||
} : undefined,
|
||||
...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [
|
||||
null, {
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: this.$t('delete'),
|
||||
action: this.del
|
||||
}]
|
||||
: []
|
||||
)]
|
||||
.filter(x => x !== undefined)
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
return [{
|
||||
icon: 'at',
|
||||
text: this.$t('mention'),
|
||||
action: this.mention
|
||||
}, null, {
|
||||
icon: 'info-circle',
|
||||
text: this.$t('detail'),
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: faCopy,
|
||||
text: this.$t('copy-content'),
|
||||
action: this.copyContent
|
||||
}, {
|
||||
icon: 'link',
|
||||
text: this.$t('copy-link'),
|
||||
action: this.copyLink
|
||||
}, this.note.uri ? {
|
||||
icon: 'external-link-square-alt',
|
||||
text: this.$t('remote'),
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
} : undefined,
|
||||
null,
|
||||
this.isFavorited ? {
|
||||
icon: 'star',
|
||||
text: this.$t('unfavorite'),
|
||||
action: () => this.toggleFavorite(false)
|
||||
} : {
|
||||
icon: 'star',
|
||||
text: this.$t('favorite'),
|
||||
action: () => this.toggleFavorite(true)
|
||||
},
|
||||
this.note.userId != this.$store.state.i.id ? this.isWatching ? {
|
||||
icon: faEyeSlash,
|
||||
text: this.$t('unwatch'),
|
||||
action: () => this.toggleWatch(false)
|
||||
} : {
|
||||
icon: faEye,
|
||||
text: this.$t('watch'),
|
||||
action: () => this.toggleWatch(true)
|
||||
} : undefined,
|
||||
this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('unpin'),
|
||||
action: () => this.togglePin(false)
|
||||
} : {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('pin'),
|
||||
action: () => this.togglePin(true)
|
||||
} : undefined,
|
||||
...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [
|
||||
null,
|
||||
this.note.userId == this.$store.state.i.id ? {
|
||||
icon: 'undo-alt',
|
||||
text: this.$t('delete-and-edit'),
|
||||
action: this.deleteAndEdit
|
||||
} : undefined,
|
||||
{
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: this.$t('delete'),
|
||||
action: this.del
|
||||
}]
|
||||
: []
|
||||
)]
|
||||
.filter(x => x !== undefined);
|
||||
} else {
|
||||
return [{
|
||||
icon: 'info-circle',
|
||||
text: this.$t('detail'),
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: faCopy,
|
||||
text: this.$t('copy-content'),
|
||||
action: this.copyContent
|
||||
}, {
|
||||
icon: 'link',
|
||||
text: this.$t('copy-link'),
|
||||
action: this.copyLink
|
||||
}, this.note.uri ? {
|
||||
icon: 'external-link-square-alt',
|
||||
text: this.$t('remote'),
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
} : undefined]
|
||||
.filter(x => x !== undefined);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -154,6 +183,25 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
deleteAndEdit() {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('delete-and-edit-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.note.id
|
||||
}).then(() => {
|
||||
this.destroyDom();
|
||||
});
|
||||
this.$post({
|
||||
initialNote: this.note,
|
||||
reply: this.note.reply,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleFavorite(favorite: boolean) {
|
||||
this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
|
||||
noteId: this.note.id
|
||||
@@ -179,6 +227,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
closed() {
|
||||
this.$emit('closed');
|
||||
this.$nextTick(() => {
|
||||
this.destroyDom();
|
||||
});
|
||||
|
@@ -52,9 +52,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as moment from 'moment';
|
||||
import i18n from '../../../i18n';
|
||||
import { erase } from '../../../../../prelude/array';
|
||||
import { addTimespan } from '../../../../../prelude/time';
|
||||
import { formatDateTimeString } from '../../../../../misc/format-time-string';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/poll-editor.vue'),
|
||||
data() {
|
||||
@@ -62,7 +64,7 @@ export default Vue.extend({
|
||||
choices: ['', ''],
|
||||
multiple: false,
|
||||
expiration: 'infinite',
|
||||
atDate: moment().add(1, 'day').toISOString().split('T')[0],
|
||||
atDate: formatDateTimeString(addTimespan(new Date(), 1, 'days'), 'yyyy-MM-dd'),
|
||||
atTime: '00:00',
|
||||
after: 0,
|
||||
unit: 'second'
|
||||
@@ -95,7 +97,7 @@ export default Vue.extend({
|
||||
|
||||
get() {
|
||||
const at = () => {
|
||||
return moment(`${this.atDate} ${this.atTime}`).valueOf();
|
||||
return new Date(`${this.atDate} ${this.atTime}`).getTime();
|
||||
};
|
||||
|
||||
const after = () => {
|
||||
|
@@ -36,7 +36,7 @@ export default Vue.extend({
|
||||
required: true
|
||||
},
|
||||
detachMediaFn: {
|
||||
type: Object,
|
||||
type: Function,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<transition name="zoom-in-top">
|
||||
<div class="buebdbiu" ref="popover" v-if="show">
|
||||
<i18n path="few-users" v-if="users.length <= 10">
|
||||
<span slot="users">
|
||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
||||
<mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
||||
<mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
|
||||
</b>
|
||||
</span>
|
||||
<mk-reaction-icon slot="reaction" :reaction="reaction" ref="icon" />
|
||||
</i18n>
|
||||
<i18n path="many-users" v-if="10 < users.length">
|
||||
<span slot="users">
|
||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
||||
<mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
||||
<mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
|
||||
</b>
|
||||
</span>
|
||||
<span slot="ommited">{{ count - 10 }}</span>
|
||||
<mk-reaction-icon slot="reaction" :reaction="reaction" ref="icon" />
|
||||
</i18n>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/reactions-viewer.details.vue'),
|
||||
props: {
|
||||
reaction: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
users: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
source: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.show = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
const popover = this.$refs.popover as any;
|
||||
|
||||
if (this.source == null) {
|
||||
this.destroyDom();
|
||||
return;
|
||||
}
|
||||
const rect = this.source.getBoundingClientRect();
|
||||
|
||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
||||
popover.style.left = (x - 28) + 'px';
|
||||
popover.style.top = (y + 16) + 'px';
|
||||
});
|
||||
}
|
||||
methods: {
|
||||
close() {
|
||||
this.show = false;
|
||||
setTimeout(this.destroyDom, 300);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.buebdbiu
|
||||
$bgcolor = var(--popupBg)
|
||||
z-index 10000
|
||||
display block
|
||||
position absolute
|
||||
max-width 240px
|
||||
font-size 0.8em
|
||||
padding 6px 8px
|
||||
background $bgcolor
|
||||
text-align center
|
||||
color var(--text)
|
||||
border-radius 4px
|
||||
box-shadow 0 var(--lineWidth) 4px rgba(#000, 0.25)
|
||||
pointer-events none
|
||||
transform-origin center -16px
|
||||
|
||||
&:before
|
||||
content ""
|
||||
pointer-events none
|
||||
display block
|
||||
position absolute
|
||||
top -28px
|
||||
left 12px
|
||||
border-top solid 14px transparent
|
||||
border-right solid 14px transparent
|
||||
border-bottom solid 14px rgba(#000, 0.1)
|
||||
border-left solid 14px transparent
|
||||
|
||||
&:after
|
||||
content ""
|
||||
pointer-events none
|
||||
display block
|
||||
position absolute
|
||||
top -27px
|
||||
left 12px
|
||||
border-top solid 14px transparent
|
||||
border-right solid 14px transparent
|
||||
border-bottom solid 14px $bgcolor
|
||||
border-left solid 14px transparent
|
||||
</style>
|
@@ -5,6 +5,9 @@
|
||||
@click="toggleReaction(reaction)"
|
||||
v-if="count > 0"
|
||||
v-particle="!isMe"
|
||||
@mouseover="onMouseover"
|
||||
@mouseleave="onMouseleave"
|
||||
ref="reaction"
|
||||
>
|
||||
<mk-reaction-icon :reaction="reaction" ref="icon"/>
|
||||
<span>{{ count }}</span>
|
||||
@@ -15,6 +18,7 @@
|
||||
import Vue from 'vue';
|
||||
import Icon from './reaction-icon.vue';
|
||||
import anime from 'animejs';
|
||||
import XDetails from './reactions-viewer.details.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
@@ -26,6 +30,10 @@ export default Vue.extend({
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
isInitial: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
note: {
|
||||
type: Object,
|
||||
required: true,
|
||||
@@ -36,14 +44,25 @@ export default Vue.extend({
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
details: null,
|
||||
detailsTimeoutId: null,
|
||||
isHovering: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isMe(): boolean {
|
||||
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.isInitial) this.anime();
|
||||
},
|
||||
watch: {
|
||||
count() {
|
||||
this.anime();
|
||||
count(newCount, oldCount) {
|
||||
if (oldCount < newCount) this.anime();
|
||||
if (this.details != null) this.openDetails();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@@ -70,11 +89,49 @@ export default Vue.extend({
|
||||
});
|
||||
}
|
||||
},
|
||||
onMouseover() {
|
||||
this.isHovering = true;
|
||||
this.detailsTimeoutId = setTimeout(this.openDetails, 300);
|
||||
},
|
||||
onMouseleave() {
|
||||
this.isHovering = false;
|
||||
clearTimeout(this.detailsTimeoutId);
|
||||
this.closeDetails();
|
||||
},
|
||||
openDetails() {
|
||||
if (this.$root.isMobile) return;
|
||||
this.$root.api('notes/reactions', {
|
||||
noteId: this.note.id,
|
||||
type: this.reaction,
|
||||
limit: 11
|
||||
}).then((reactions: any[]) => {
|
||||
const users = reactions
|
||||
.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
|
||||
.map(x => x.user);
|
||||
|
||||
this.closeDetails();
|
||||
if (!this.isHovering) return;
|
||||
this.details = this.$root.new(XDetails, {
|
||||
reaction: this.reaction,
|
||||
users,
|
||||
count: this.count,
|
||||
source: this.$refs.reaction
|
||||
});
|
||||
});
|
||||
},
|
||||
closeDetails() {
|
||||
if (this.details != null) {
|
||||
this.details.close();
|
||||
this.details = null;
|
||||
}
|
||||
},
|
||||
anime() {
|
||||
if (this.$store.state.device.reduceMotion) return;
|
||||
if (document.hidden) return;
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.icon == null) return;
|
||||
|
||||
const rect = this.$refs.icon.$el.getBoundingClientRect();
|
||||
|
||||
const x = rect.left;
|
||||
@@ -120,6 +177,14 @@ export default Vue.extend({
|
||||
border-radius 4px
|
||||
cursor pointer
|
||||
|
||||
&, *
|
||||
-webkit-touch-callout none
|
||||
-webkit-user-select none
|
||||
-khtml-user-select none
|
||||
-moz-user-select none
|
||||
-ms-user-select none
|
||||
user-select none
|
||||
|
||||
*
|
||||
user-select none
|
||||
pointer-events none
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mk-reactions-viewer" :class="{ isMe }">
|
||||
<x-reaction v-for="(count, reaction) in reactions" :reaction="reaction" :count="count" :note="note" :key="reaction"/>
|
||||
<x-reaction v-for="(count, reaction) in note.reactions" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note" :key="reaction"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,6 +12,11 @@ export default Vue.extend({
|
||||
components: {
|
||||
XReaction
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialReactions: new Set(Object.keys(this.note.reactions))
|
||||
};
|
||||
},
|
||||
props: {
|
||||
note: {
|
||||
type: Object,
|
||||
@@ -19,9 +24,6 @@ export default Vue.extend({
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
reactions(): any {
|
||||
return this.note.reactions;
|
||||
},
|
||||
isMe(): boolean {
|
||||
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
|
||||
},
|
||||
|
@@ -159,6 +159,14 @@ export default Vue.extend({
|
||||
username: null,
|
||||
location: null,
|
||||
description: null,
|
||||
fieldName0: null,
|
||||
fieldValue0: null,
|
||||
fieldName1: null,
|
||||
fieldValue1: null,
|
||||
fieldName2: null,
|
||||
fieldValue2: null,
|
||||
fieldName3: null,
|
||||
fieldValue3: null,
|
||||
lang: null,
|
||||
birthday: null,
|
||||
avatarId: null,
|
||||
@@ -210,16 +218,14 @@ export default Vue.extend({
|
||||
this.carefulBot = this.$store.state.i.carefulBot;
|
||||
this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed;
|
||||
|
||||
if (this.$store.state.i.fields) {
|
||||
this.fieldName0 = this.$store.state.i.fields[0].name;
|
||||
this.fieldValue0 = this.$store.state.i.fields[0].value;
|
||||
this.fieldName1 = this.$store.state.i.fields[1].name;
|
||||
this.fieldValue1 = this.$store.state.i.fields[1].value;
|
||||
this.fieldName2 = this.$store.state.i.fields[2].name;
|
||||
this.fieldValue2 = this.$store.state.i.fields[2].value;
|
||||
this.fieldName3 = this.$store.state.i.fields[3].name;
|
||||
this.fieldValue3 = this.$store.state.i.fields[3].value;
|
||||
}
|
||||
this.fieldName0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].name : null;
|
||||
this.fieldValue0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].value : null;
|
||||
this.fieldName1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].name : null;
|
||||
this.fieldValue1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].value : null;
|
||||
this.fieldName2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].name : null;
|
||||
this.fieldValue2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].value : null;
|
||||
this.fieldName3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].name : null;
|
||||
this.fieldValue3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].value : null;
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@@ -47,6 +47,7 @@
|
||||
<ui-switch v-model="disableAnimatedMfm">{{ $t('@._settings.disable-animated-mfm') }}</ui-switch>
|
||||
<ui-switch v-model="disableShowingAnimatedImages">{{ $t('@._settings.disable-showing-animated-images') }}</ui-switch>
|
||||
<ui-switch v-model="remainDeletedNote">{{ $t('@._settings.remain-deleted-note') }}</ui-switch>
|
||||
<ui-switch v-model="enableMobileQuickNotificationView">{{ $t('@._settings.enable-quick-notification-view') }}</ui-switch>
|
||||
</section>
|
||||
<section>
|
||||
<header>{{ $t('@._settings.line-width') }}</header>
|
||||
@@ -158,6 +159,19 @@
|
||||
<template #desc>{{ $t('@._settings.paste-dialog-desc') }}</template>
|
||||
</ui-switch>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('@._settings.room') }}</header>
|
||||
<ui-select v-model="roomGraphicsQuality">
|
||||
<template #label>{{ $t('@._settings._room.graphicsQuality') }}</template>
|
||||
<option value="ultra">{{ $t('@._settings._room._graphicsQuality.ultra') }}</option>
|
||||
<option value="high">{{ $t('@._settings._room._graphicsQuality.high') }}</option>
|
||||
<option value="medium">{{ $t('@._settings._room._graphicsQuality.medium') }}</option>
|
||||
<option value="low">{{ $t('@._settings._room._graphicsQuality.low') }}</option>
|
||||
<option value="cheep">{{ $t('@._settings._room._graphicsQuality.cheep') }}</option>
|
||||
</ui-select>
|
||||
<ui-switch v-model="roomUseOrthographicCamera">{{ $t('@._settings._room.useOrthographicCamera') }}</ui-switch>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
@@ -502,6 +516,16 @@ export default Vue.extend({
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'iLikeSushi', value }); }
|
||||
},
|
||||
|
||||
roomUseOrthographicCamera: {
|
||||
get() { return this.$store.state.device.roomUseOrthographicCamera; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'roomUseOrthographicCamera', value }); }
|
||||
},
|
||||
|
||||
roomGraphicsQuality: {
|
||||
get() { return this.$store.state.device.roomGraphicsQuality; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'roomGraphicsQuality', value }); }
|
||||
},
|
||||
|
||||
games_reversi_showBoardLabels: {
|
||||
get() { return this.$store.state.settings.gamesReversiShowBoardLabels; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'gamesReversiShowBoardLabels', value }); }
|
||||
@@ -532,6 +556,11 @@ export default Vue.extend({
|
||||
set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); }
|
||||
},
|
||||
|
||||
enableMobileQuickNotificationView: {
|
||||
get() { return this.$store.state.device.enableMobileQuickNotificationView; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'enableMobileQuickNotificationView', value }); }
|
||||
},
|
||||
|
||||
homeProfile: {
|
||||
get() { return this.$store.state.device.homeProfile; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'homeProfile', value }); }
|
||||
|
@@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('desktop/views/components/settings.tags.vue'),
|
||||
|
@@ -125,7 +125,7 @@ import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { lightTheme, darkTheme, builtinThemes, applyTheme, Theme } from '../../../../theme';
|
||||
import { Chrome } from 'vue-color';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import * as JSON5 from 'json5';
|
||||
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
|
||||
|
@@ -31,7 +31,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
|
@@ -64,8 +64,7 @@ export default Vue.extend({
|
||||
methods: {
|
||||
onMousedown(e: MouseEvent) {
|
||||
function distance(p, q) {
|
||||
const sqrt = Math.sqrt, pow = Math.pow;
|
||||
return sqrt(pow(p.x - q.x, 2) + pow(p.y - q.y, 2));
|
||||
return Math.hypot(p.x - q.x, p.y - q.y);
|
||||
}
|
||||
|
||||
function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY) {
|
||||
|
@@ -49,6 +49,7 @@ export default Vue.extend({
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.disabled) return;
|
||||
this.$emit('change', !this.checked);
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<mfm :text="user.name || user.username" :plain="true" :nowrap="true" :custom-emojis="user.emojis"/>
|
||||
<mfm :text="user.name || user.username" :plain="true" :nowrap="nowrap" :custom-emojis="user.emojis"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -10,7 +10,11 @@ export default Vue.extend({
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
nowrap: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -14,7 +14,7 @@
|
||||
<template v-if="active"><fa icon="angle-up"/></template>
|
||||
<template v-else><fa icon="angle-down"/></template>
|
||||
</button>
|
||||
<span><slot name="header"></slot></span>
|
||||
<span class="header"><slot name="header"></slot></span>
|
||||
<span class="count" v-if="count > 0">({{ count }})</span>
|
||||
<button v-if="!isTemporaryColumn" class="menu" ref="menu" @click.stop="showMenu"><fa icon="caret-down"/></button>
|
||||
<button v-else class="close" @click.stop="close"><fa icon="times"/></button>
|
||||
@@ -395,13 +395,22 @@ export default Vue.extend({
|
||||
&.indicate
|
||||
box-shadow 0 3px 0 0 var(--primary)
|
||||
|
||||
> span
|
||||
> .header
|
||||
display inline-block
|
||||
align-items center
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
white-space nowrap
|
||||
|
||||
[data-icon]
|
||||
margin-right 8px
|
||||
|
||||
> .count
|
||||
margin-left 4px
|
||||
opacity 0.5
|
||||
|
||||
> span:only-of-type
|
||||
width 100%
|
||||
|
||||
> .toggleActive
|
||||
> .menu
|
||||
|
@@ -54,8 +54,8 @@
|
||||
<div>
|
||||
<header>
|
||||
<fa icon="user-clock" class="icon"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<mk-user-name :user="notification.user" class="name"/>
|
||||
<router-link :to="notification.user | userPage" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
@@ -67,8 +67,8 @@
|
||||
<div>
|
||||
<header>
|
||||
<fa icon="chart-pie" class="icon"/>
|
||||
<router-link :to="notification.user | userPage">
|
||||
<mk-user-name :user="notification.user" class="name"/>
|
||||
<router-link :to="notification.user | userPage" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
@@ -167,6 +167,10 @@ export default Vue.extend({
|
||||
display inline-block
|
||||
margin-right 3px
|
||||
|
||||
&.reaction
|
||||
> div > header
|
||||
align-items normal
|
||||
|
||||
&.renote
|
||||
> div > header [data-icon]
|
||||
color #77B255
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<x-column :name="name" :column="column" :is-stacked="isStacked">
|
||||
<x-column :name="name" :column="column" :is-stacked="isStacked" :menu="menu">
|
||||
<template #header><fa :icon="['far', 'bell']"/>{{ name }}</template>
|
||||
|
||||
<x-notifications/>
|
||||
<x-notifications :type="column.notificationType === 'all' ? null : column.notificationType"/>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
@@ -30,11 +30,46 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
menu: null,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
name(): string {
|
||||
if (this.column.name) return this.column.name;
|
||||
return this.$t('@deck.notifications');
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.column.notificationType == null) {
|
||||
this.column.notificationType = 'all';
|
||||
this.$store.commit('updateDeckColumn', this.column);
|
||||
}
|
||||
|
||||
this.menu = [{
|
||||
icon: 'cog',
|
||||
text: this.$t('@.notification-type'),
|
||||
action: () => {
|
||||
this.$root.dialog({
|
||||
title: this.$t('@.notification-type'),
|
||||
type: null,
|
||||
select: {
|
||||
items: ['all', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'].map(x => ({
|
||||
value: x, text: this.$t('@.notification-types.' + x)
|
||||
}))
|
||||
default: this.column.notificationType,
|
||||
},
|
||||
showCancelButton: true
|
||||
}).then(({ canceled, result: type }) => {
|
||||
if (canceled) return;
|
||||
this.column.notificationType = type;
|
||||
this.$store.commit('updateDeckColumn', this.column);
|
||||
});
|
||||
}
|
||||
}];
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@@ -47,12 +47,22 @@ export default Vue.extend({
|
||||
}),
|
||||
],
|
||||
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
pagination: {
|
||||
endpoint: 'i/notifications',
|
||||
limit: 20,
|
||||
params: () => ({
|
||||
includeTypes: this.type ? [this.type] : undefined
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -69,6 +79,12 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
type() {
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow" mini/>
|
||||
<mk-avatar class="avatar" :user="user" :disable-preview="true" :key="user.id"/>
|
||||
<router-link class="name" :to="user | userPage()">
|
||||
<mk-user-name :user="user" :key="user.id"/>
|
||||
<mk-user-name :user="user" :key="user.id" :nowrap="false"/>
|
||||
</router-link>
|
||||
<span class="acct">@{{ user | acct }} <fa v-if="user.isLocked == true" class="locked" icon="lock" fixed-width/></span>
|
||||
<span class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</span>
|
||||
|
@@ -21,7 +21,7 @@ import i18n from '../../../i18n';
|
||||
import XColumnCore from './deck.column-core.vue';
|
||||
import Menu from '../../../common/views/components/menu.vue';
|
||||
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('deck'),
|
||||
|
@@ -54,7 +54,7 @@ import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XColumn from './deck.column.vue';
|
||||
import * as XDraggable from 'vuedraggable';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
@@ -143,7 +143,11 @@ export default Vue.extend({
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@@ -40,5 +40,9 @@ export default Vue.extend({
|
||||
icon: faStar
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@@ -40,5 +40,9 @@ export default Vue.extend({
|
||||
icon: faNewspaper
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@@ -26,7 +26,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { faPlus, faQuestion } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
@@ -76,7 +76,7 @@ export default Vue.extend({
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
this.value.children.push({ id, type });
|
||||
},
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { faPlus, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
@@ -88,7 +88,7 @@ export default Vue.extend({
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
this.value.children.push({ id, type });
|
||||
},
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ import i18n from '../../../../i18n';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
|
||||
import { isLiteralBlock, funcDefs, blockDefs } from '../../../../../../misc/aiscript/index';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
@@ -143,7 +143,7 @@ export default Vue.extend({
|
||||
this.warn = null;
|
||||
|
||||
if (this.value.type === 'fn') {
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
this.value.value = {};
|
||||
Vue.set(this.value.value, 'slots', []);
|
||||
Vue.set(this.value.value, 'expression', { id, type: null });
|
||||
@@ -156,7 +156,7 @@ export default Vue.extend({
|
||||
|
||||
const empties = [];
|
||||
for (let i = 0; i < fn.value.slots.length; i++) {
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
empties.push({ id, type: null });
|
||||
}
|
||||
Vue.set(this.value, 'args', empties);
|
||||
@@ -167,7 +167,7 @@ export default Vue.extend({
|
||||
|
||||
const empties = [];
|
||||
for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
empties.push({ id, type: null });
|
||||
}
|
||||
Vue.set(this.value, 'args', empties);
|
||||
|
@@ -99,7 +99,7 @@ import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-
|
||||
import i18n from '../../../../i18n';
|
||||
import XVariable from './page-editor.script-block.vue';
|
||||
import XBlocks from './page-editor.blocks.vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { blockDefs } from '../../../../../../misc/aiscript/index';
|
||||
import { ASTypeChecker } from '../../../../../../misc/aiscript/type-checker';
|
||||
import { url } from '../../../../config';
|
||||
@@ -201,7 +201,7 @@ export default Vue.extend({
|
||||
this.variables = this.page.variables;
|
||||
this.eyeCatchingImageId = this.page.eyeCatchingImageId;
|
||||
} else {
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
this.content = [{
|
||||
id,
|
||||
type: 'text',
|
||||
@@ -220,37 +220,48 @@ export default Vue.extend({
|
||||
|
||||
methods: {
|
||||
save() {
|
||||
const options = {
|
||||
title: this.title.trim(),
|
||||
name: this.name.trim(),
|
||||
summary: this.summary,
|
||||
font: this.font,
|
||||
hideTitleWhenPinned: this.hideTitleWhenPinned,
|
||||
alignCenter: this.alignCenter,
|
||||
content: this.content,
|
||||
variables: this.variables,
|
||||
eyeCatchingImageId: this.eyeCatchingImageId,
|
||||
};
|
||||
|
||||
const onError = err => {
|
||||
if (err.id == '3d81ceae-475f-4600-b2a8-2bc116157532') {
|
||||
if (err.info.param == 'name') {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('title-invalid-name'),
|
||||
text: this.$t('text-invalid-name')
|
||||
});
|
||||
}
|
||||
} else if (err.code == 'NAME_ALREADY_EXISTS') {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('name-already-exists')
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (this.pageId) {
|
||||
this.$root.api('pages/update', {
|
||||
pageId: this.pageId,
|
||||
title: this.title.trim(),
|
||||
name: this.name.trim(),
|
||||
summary: this.summary,
|
||||
font: this.font,
|
||||
hideTitleWhenPinned: this.hideTitleWhenPinned,
|
||||
alignCenter: this.alignCenter,
|
||||
content: this.content,
|
||||
variables: this.variables,
|
||||
eyeCatchingImageId: this.eyeCatchingImageId,
|
||||
}).then(page => {
|
||||
options.pageId = this.pageId;
|
||||
this.$root.api('pages/update', options)
|
||||
.then(page => {
|
||||
this.currentName = this.name.trim();
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('page-updated')
|
||||
});
|
||||
});
|
||||
}).catch(onError);
|
||||
} else {
|
||||
this.$root.api('pages/create', {
|
||||
title: this.title.trim(),
|
||||
name: this.name.trim(),
|
||||
summary: this.summary,
|
||||
font: this.font,
|
||||
hideTitleWhenPinned: this.hideTitleWhenPinned,
|
||||
alignCenter: this.alignCenter,
|
||||
content: this.content,
|
||||
variables: this.variables,
|
||||
eyeCatchingImageId: this.eyeCatchingImageId,
|
||||
}).then(page => {
|
||||
this.$root.api('pages/create', options)
|
||||
.then(page => {
|
||||
this.pageId = page.id;
|
||||
this.currentName = this.name.trim();
|
||||
this.$root.dialog({
|
||||
@@ -258,7 +269,7 @@ export default Vue.extend({
|
||||
text: this.$t('page-created')
|
||||
});
|
||||
this.$router.push(`/i/pages/edit/${this.pageId}`);
|
||||
});
|
||||
}).catch(onError);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -292,7 +303,7 @@ export default Vue.extend({
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
this.content.push({ id, type });
|
||||
},
|
||||
|
||||
@@ -316,7 +327,7 @@ export default Vue.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
const id = uuid.v4();
|
||||
const id = uuid();
|
||||
this.variables.push({ id, name, type: null });
|
||||
},
|
||||
|
||||
|
@@ -52,6 +52,9 @@ export default Vue.extend({
|
||||
icon: faStickyNote
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
},
|
||||
methods: {
|
||||
create() {
|
||||
this.$router.push(`/i/pages/new`);
|
||||
|
106
src/client/app/common/views/pages/room/preview.vue
Normal file
106
src/client/app/common/views/pages/room/preview.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<canvas width=224 height=128></canvas>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as THREE from 'three';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
selected: null,
|
||||
objectHeight: 0,
|
||||
orbitRadius: 5
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const canvas = this.$el;
|
||||
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas: canvas,
|
||||
antialias: true,
|
||||
alpha: false
|
||||
});
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(width, height);
|
||||
renderer.setClearColor(0x000000);
|
||||
renderer.autoClear = false;
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.cullFace = THREE.CullFaceBack;
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 100);
|
||||
camera.zoom = 10;
|
||||
camera.position.x = 0;
|
||||
camera.position.y = 2;
|
||||
camera.position.z = 0;
|
||||
camera.updateProjectionMatrix();
|
||||
scene.add(camera);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
|
||||
ambientLight.castShadow = false;
|
||||
scene.add(ambientLight);
|
||||
|
||||
const light = new THREE.PointLight(0xffffff, 1, 100);
|
||||
light.position.set(3, 3, 3);
|
||||
scene.add(light);
|
||||
|
||||
const grid = new THREE.GridHelper(5, 16, 0x444444, 0x222222);
|
||||
scene.add(grid);
|
||||
|
||||
const render = () => {
|
||||
const timer = Date.now() * 0.0004;
|
||||
requestAnimationFrame(render);
|
||||
|
||||
camera.position.y = Math.sin(Math.PI / 6) * this.orbitRadius; // Math.PI / 6 => 30deg
|
||||
camera.position.z = Math.cos(timer) * this.orbitRadius;
|
||||
camera.position.x = Math.sin(timer) * this.orbitRadius;
|
||||
camera.lookAt(new THREE.Vector3(0, this.objectHeight / 2, 0));
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
this.selected = selected => {
|
||||
const obj = selected.clone();
|
||||
|
||||
// Remove current object
|
||||
const current = scene.getObjectByName('obj');
|
||||
if (current != null) {
|
||||
scene.remove(current);
|
||||
}
|
||||
|
||||
// Add new object
|
||||
obj.name = 'obj';
|
||||
obj.position.x = 0;
|
||||
obj.position.y = 0;
|
||||
obj.position.z = 0;
|
||||
obj.rotation.x = 0;
|
||||
obj.rotation.y = 0;
|
||||
obj.rotation.z = 0;
|
||||
obj.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
child.material = child.material.clone();
|
||||
return child.material.emissive.setHex(0x000000);
|
||||
}
|
||||
});
|
||||
const objectBoundingBox = new THREE.Box3().setFromObject(obj);
|
||||
this.objectHeight = objectBoundingBox.max.y - objectBoundingBox.min.y;
|
||||
|
||||
const objectWidth = objectBoundingBox.max.x - objectBoundingBox.min.x;
|
||||
const objectDepth = objectBoundingBox.max.z - objectBoundingBox.min.z;
|
||||
|
||||
const horizontal = Math.hypot(objectWidth, objectDepth) / camera.aspect;
|
||||
this.orbitRadius = Math.max(horizontal, this.objectHeight) * camera.zoom * 0.625 / Math.tan(camera.fov * 0.5 * (Math.PI / 180));
|
||||
|
||||
scene.add(obj);
|
||||
};
|
||||
|
||||
render();
|
||||
},
|
||||
});
|
||||
</script>
|
310
src/client/app/common/views/pages/room/room.vue
Normal file
310
src/client/app/common/views/pages/room/room.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<div class="hveuntkp">
|
||||
<div class="controller" v-if="objectSelected">
|
||||
<section>
|
||||
<p class="name">{{ selectedFurnitureName }}</p>
|
||||
<x-preview ref="preview"/>
|
||||
<template v-if="selectedFurnitureInfo.props">
|
||||
<div v-for="k in Object.keys(selectedFurnitureInfo.props)" :key="k">
|
||||
<p>{{ k }}</p>
|
||||
<template v-if="selectedFurnitureInfo.props[k] === 'image'">
|
||||
<ui-button @click="chooseImage(k)">{{ $t('chooseImage') }}</ui-button>
|
||||
</template>
|
||||
<template v-else-if="selectedFurnitureInfo.props[k] === 'color'">
|
||||
<input type="color" :value="selectedFurnitureProps ? selectedFurnitureProps[k] : null" @change="updateColor(k, $event)"/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="translate()" :primary="isTranslateMode"><fa :icon="faArrowsAlt"/> {{ $t('translate') }}</ui-button>
|
||||
<ui-button @click="rotate()" :primary="isRotateMode"><fa :icon="faUndo"/> {{ $t('rotate') }}</ui-button>
|
||||
<ui-button v-if="isTranslateMode || isRotateMode" @click="exit()"><fa :icon="faBan"/> {{ $t('exit') }}</ui-button>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="remove()"><fa :icon="faTrashAlt"/> {{ $t('remove') }}</ui-button>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="menu" v-if="isMyRoom">
|
||||
<section>
|
||||
<ui-button @click="add()"><fa :icon="faBoxOpen"/> {{ $t('add-furniture') }}</ui-button>
|
||||
</section>
|
||||
<section>
|
||||
<ui-select :value="roomType" @input="updateRoomType($event)">
|
||||
<template #label>{{ $t('room-type') }}</template>
|
||||
<option value="default">{{ $t('rooms.default') }}</option>
|
||||
<option value="washitsu">{{ $t('rooms.washitsu') }}</option>
|
||||
</ui-select>
|
||||
<label v-if="roomType === 'default'">
|
||||
<span>{{ $t('carpet-color') }}</span>
|
||||
<input type="color" :value="carpetColor" @change="updateCarpetColor($event)"/>
|
||||
</label>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button :primary="changed" @click="save()"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
<ui-button @click="clear()"><fa :icon="faBroom"/> {{ $t('clear') }}</ui-button>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { Room } from '../../../scripts/room/room';
|
||||
import parseAcct from '../../../../../../misc/acct/parse';
|
||||
import XPreview from './preview.vue';
|
||||
const storeItems = require('../../../scripts/room/furnitures.json5');
|
||||
import { faBoxOpen, faUndo, faArrowsAlt, faBan, faBroom } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
import { query as urlQuery } from '../../../../../../prelude/url';
|
||||
|
||||
let room: Room;
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('room'),
|
||||
|
||||
components: {
|
||||
XPreview
|
||||
},
|
||||
|
||||
props: {
|
||||
acct: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
objectSelected: false,
|
||||
selectedFurnitureName: null,
|
||||
selectedFurnitureInfo: null,
|
||||
selectedFurnitureProps: null,
|
||||
roomType: null,
|
||||
carpetColor: null,
|
||||
isTranslateMode: false,
|
||||
isRotateMode: false,
|
||||
isMyRoom: false,
|
||||
changed: false,
|
||||
faBoxOpen, faSave, faTrashAlt, faUndo, faArrowsAlt, faBan, faBroom,
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
window.addEventListener('beforeunload', this.beforeunload);
|
||||
|
||||
const user = await this.$root.api('users/show', {
|
||||
...parseAcct(this.acct)
|
||||
});
|
||||
|
||||
this.isMyRoom = this.$store.getters.isSignedIn && this.$store.state.i.id === user.id;
|
||||
|
||||
const roomInfo = await this.$root.api('room/show', {
|
||||
userId: user.id
|
||||
});
|
||||
|
||||
this.roomType = roomInfo.roomType;
|
||||
this.carpetColor = roomInfo.carpetColor;
|
||||
|
||||
room = new Room(user, this.isMyRoom, roomInfo, this.$el, {
|
||||
graphicsQuality: this.$store.state.device.roomGraphicsQuality,
|
||||
onChangeSelect: obj => {
|
||||
this.objectSelected = obj != null;
|
||||
if (obj) {
|
||||
const f = room.findFurnitureById(obj.name);
|
||||
this.selectedFurnitureName = this.$t('furnitures.' + f.type);
|
||||
this.selectedFurnitureInfo = storeItems.find(x => x.id === f.type);
|
||||
this.selectedFurnitureProps = f.props
|
||||
? JSON.parse(JSON.stringify(f.props)) // Disable reactivity
|
||||
: null;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.preview.selected(obj);
|
||||
});
|
||||
}
|
||||
},
|
||||
useOrthographicCamera: this.$store.state.device.roomUseOrthographicCamera
|
||||
});
|
||||
},
|
||||
|
||||
beforeRouteLeave(to, from, next) {
|
||||
if (this.changed) {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('leave-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
next(false);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
room.destroy();
|
||||
window.removeEventListener('beforeunload', this.beforeunload);
|
||||
},
|
||||
|
||||
methods: {
|
||||
beforeunload(e: BeforeUnloadEvent) {
|
||||
if (this.changed) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
}
|
||||
},
|
||||
|
||||
async add() {
|
||||
const { canceled, result: id } = await this.$root.dialog({
|
||||
type: null,
|
||||
title: this.$t('add-furniture'),
|
||||
select: {
|
||||
items: storeItems.map(item => ({
|
||||
value: item.id, text: this.$t('furnitures.' + item.id)
|
||||
}))
|
||||
},
|
||||
showCancelButton: true
|
||||
});
|
||||
if (canceled) return;
|
||||
room.addFurniture(id);
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
remove() {
|
||||
this.isTranslateMode = false;
|
||||
this.isRotateMode = false;
|
||||
room.removeFurniture();
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
save() {
|
||||
this.$root.api('room/update', {
|
||||
room: room.getRoomInfo()
|
||||
}).then(() => {
|
||||
this.changed = false;
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('saved')
|
||||
});
|
||||
}).catch((e: any) => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e.message
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
clear() {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('clear-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
room.removeAllFurnitures();
|
||||
this.changed = true;
|
||||
});
|
||||
},
|
||||
|
||||
chooseImage(key) {
|
||||
this.$chooseDriveFile({
|
||||
multiple: false
|
||||
}).then(file => {
|
||||
room.updateProp(key, `/proxy/?${urlQuery({ url: file.thumbnailUrl })}`);
|
||||
this.$refs.preview.selected(room.getSelectedObject());
|
||||
this.changed = true;
|
||||
});
|
||||
},
|
||||
|
||||
updateColor(key, ev) {
|
||||
room.updateProp(key, ev.target.value);
|
||||
this.$refs.preview.selected(room.getSelectedObject());
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
updateCarpetColor(ev) {
|
||||
room.updateCarpetColor(ev.target.value);
|
||||
this.carpetColor = ev.target.value;
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
updateRoomType(type) {
|
||||
room.changeRoomType(type);
|
||||
this.roomType = type;
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
translate() {
|
||||
if (this.isTranslateMode) {
|
||||
this.exit();
|
||||
} else {
|
||||
this.isRotateMode = false;
|
||||
this.isTranslateMode = true;
|
||||
room.enterTransformMode('translate');
|
||||
}
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
rotate() {
|
||||
if (this.isRotateMode) {
|
||||
this.exit();
|
||||
} else {
|
||||
this.isTranslateMode = false;
|
||||
this.isRotateMode = true;
|
||||
room.enterTransformMode('rotate');
|
||||
}
|
||||
this.changed = true;
|
||||
},
|
||||
|
||||
exit() {
|
||||
this.isTranslateMode = false;
|
||||
this.isRotateMode = false;
|
||||
room.exitTransformMode();
|
||||
this.changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.hveuntkp
|
||||
> .controller
|
||||
> .menu
|
||||
position fixed
|
||||
z-index 1
|
||||
padding 16px
|
||||
background var(--face)
|
||||
color var(--text)
|
||||
|
||||
> section
|
||||
padding 16px 0
|
||||
|
||||
&:first-child
|
||||
padding-top 0
|
||||
|
||||
&:last-child
|
||||
padding-bottom 0
|
||||
|
||||
&:not(:last-child)
|
||||
border-bottom solid 1px var(--faceDivider)
|
||||
|
||||
> .controller
|
||||
top 16px
|
||||
left 16px
|
||||
width 256px
|
||||
|
||||
> section
|
||||
> .name
|
||||
margin 0
|
||||
|
||||
> .menu
|
||||
top 16px
|
||||
right 16px
|
||||
width 256px
|
||||
|
||||
</style>
|
@@ -62,6 +62,8 @@ export default Vue.extend({
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
|
||||
this.$root.api('users/groups/owned').then(groups => {
|
||||
this.ownedGroups = groups;
|
||||
});
|
||||
|
@@ -33,6 +33,8 @@ export default Vue.extend({
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
|
||||
this.$root.api('users/lists/list').then(lists => {
|
||||
this.fetching = false;
|
||||
this.lists = lists;
|
||||
|
@@ -25,7 +25,8 @@ export default define({
|
||||
data() {
|
||||
return {
|
||||
text: null,
|
||||
changed: false
|
||||
changed: false,
|
||||
timeoutId: null
|
||||
};
|
||||
},
|
||||
|
||||
@@ -45,6 +46,8 @@ export default define({
|
||||
|
||||
onChange() {
|
||||
this.changed = true;
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = setTimeout(this.saveMemo, 1000);
|
||||
},
|
||||
|
||||
saveMemo() {
|
||||
|
@@ -71,7 +71,7 @@
|
||||
<script lang="ts">
|
||||
import define from '../../../common/define-widget';
|
||||
import i18n from '../../../i18n';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default define({
|
||||
name: 'posts-monitor',
|
||||
|
@@ -63,7 +63,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['connection'],
|
||||
|
@@ -63,7 +63,10 @@ init(async (launch, os) => {
|
||||
this.$root.newAsync(() => import('./views/components/post-form-window.vue').then(m => m.default), {
|
||||
reply: o.reply,
|
||||
mention: o.mention,
|
||||
animation: o.animation == null ? true : o.animation
|
||||
animation: o.animation == null ? true : o.animation,
|
||||
initialText: o.initialText,
|
||||
instant: o.instant,
|
||||
initialNote: o.initialNote,
|
||||
}).then(vm => {
|
||||
if (o.cb) vm.$once('closed', o.cb);
|
||||
});
|
||||
@@ -182,8 +185,10 @@ init(async (launch, os) => {
|
||||
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
||||
{ path: '/i/drive', component: MkDrive },
|
||||
{ path: '/i/drive/folder/:folder', component: MkDrive },
|
||||
{ path: '/i/settings', component: MkSettings },
|
||||
{ path: '/i/settings', redirect: '/i/settings/profile' },
|
||||
{ path: '/i/settings/:page', component: MkSettings },
|
||||
{ path: '/selectdrive', component: MkSelectDrive },
|
||||
{ path: '/@:acct/room', props: true, component: () => import('../common/views/pages/room/room.vue').then(m => m.default) },
|
||||
{ path: '/share', component: MkShare },
|
||||
{ path: '/games/reversi/:game?', component: MkReversi },
|
||||
{ path: '/authorize-follow', component: MkFollow },
|
||||
|
@@ -11,17 +11,16 @@
|
||||
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div">
|
||||
<template v-for="(notification, i) in _notifications">
|
||||
<div class="notification" :class="notification.type" :key="notification.id">
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
|
||||
<template v-if="notification.type == 'reaction'">
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div class="text">
|
||||
<p>
|
||||
<mk-reaction-icon :reaction="notification.reaction"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id">
|
||||
<header>
|
||||
<mk-reaction-icon :reaction="notification.reaction" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
<router-link class="note-ref" :to="notification.note | notePage" :title="getNoteSummary(notification.note)">
|
||||
<fa icon="quote-left"/>
|
||||
<mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :custom-emojis="notification.note.emojis"/>
|
||||
@@ -33,11 +32,13 @@
|
||||
<template v-if="notification.type == 'renote'">
|
||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="retweet"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId">
|
||||
<header>
|
||||
<fa icon="retweet" class="icon"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId" class="name">
|
||||
<mk-user-name :user="notification.note.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
<router-link class="note-ref" :to="notification.note | notePage" :title="getNoteSummary(notification.note.renote)">
|
||||
<fa icon="quote-left"/>
|
||||
<mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="true" :custom-emojis="notification.note.renote.emojis"/>
|
||||
@@ -49,11 +50,13 @@
|
||||
<template v-if="notification.type == 'quote'">
|
||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="quote-left"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId">
|
||||
<header>
|
||||
<fa icon="quote-left" class="icon"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId" class="name">
|
||||
<mk-user-name :user="notification.note.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
<router-link class="note-preview" :to="notification.note | notePage" :title="getNoteSummary(notification.note)">
|
||||
<mfm :text="getNoteSummary(notification.note)" :plain="true" :custom-emojis="notification.note.emojis"/>
|
||||
</router-link>
|
||||
@@ -63,33 +66,39 @@
|
||||
<template v-if="notification.type == 'follow'">
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="user-plus"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id">
|
||||
<header>
|
||||
<fa icon="user-plus" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type == 'receiveFollowRequest'">
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="user-clock"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id">
|
||||
<header>
|
||||
<fa icon="user-clock" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type == 'reply'">
|
||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="reply"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId">
|
||||
<header>
|
||||
<fa icon="reply" class="icon"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId" class="name">
|
||||
<mk-user-name :user="notification.note.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
<router-link class="note-preview" :to="notification.note | notePage" :title="getNoteSummary(notification.note)">
|
||||
<mfm :text="getNoteSummary(notification.note)" :plain="true" :custom-emojis="notification.note.emojis"/>
|
||||
</router-link>
|
||||
@@ -99,11 +108,13 @@
|
||||
<template v-if="notification.type == 'mention'">
|
||||
<mk-avatar class="avatar" :user="notification.note.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="at"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId">
|
||||
<header>
|
||||
<fa icon="at" class="icon"/>
|
||||
<router-link :to="notification.note.user | userPage" v-user-preview="notification.note.userId" class="name">
|
||||
<mk-user-name :user="notification.note.user"/>
|
||||
</router-link>
|
||||
</p>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
<router-link class="note-preview" :to="notification.note | notePage" :title="getNoteSummary(notification.note)">
|
||||
<mfm :text="getNoteSummary(notification.note)" :plain="true" :custom-emojis="notification.note.emojis"/>
|
||||
</router-link>
|
||||
@@ -113,9 +124,13 @@
|
||||
<template v-if="notification.type == 'pollVote'">
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div class="text">
|
||||
<p><fa icon="chart-pie"/><router-link :to="notification.user | userPage" v-user-preview="notification.user.id">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link></p>
|
||||
<header>
|
||||
<fa icon="chart-pie" class="icon"/>
|
||||
<router-link :to="notification.user | userPage" v-user-preview="notification.user.id" class="name">
|
||||
<mk-user-name :user="notification.user"/>
|
||||
</router-link>
|
||||
<mk-time :time="notification.createdAt"/>
|
||||
</header>
|
||||
<router-link class="note-ref" :to="notification.note | notePage" :title="getNoteSummary(notification.note)">
|
||||
<fa icon="quote-left"/>
|
||||
<mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :custom-emojis="notification.note.emojis"/>
|
||||
@@ -150,9 +165,18 @@ export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
mixins: [
|
||||
paging({}),
|
||||
paging({
|
||||
isContainer: true
|
||||
}),
|
||||
],
|
||||
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
@@ -160,6 +184,9 @@ export default Vue.extend({
|
||||
pagination: {
|
||||
endpoint: 'i/notifications',
|
||||
limit: 10,
|
||||
params: () => ({
|
||||
includeTypes: this.type ? [this.type] : undefined
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -176,6 +203,12 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
type() {
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
this.connection.on('notification', this.onNotification);
|
||||
@@ -225,15 +258,6 @@ export default Vue.extend({
|
||||
&:last-child
|
||||
border-bottom none
|
||||
|
||||
> .mk-time
|
||||
display inline
|
||||
position absolute
|
||||
top 16px
|
||||
right 12px
|
||||
vertical-align top
|
||||
color var(--noteHeaderInfo)
|
||||
font-size small
|
||||
|
||||
&:after
|
||||
content ""
|
||||
display block
|
||||
@@ -254,12 +278,23 @@ export default Vue.extend({
|
||||
width calc(100% - 36px)
|
||||
padding-left 8px
|
||||
|
||||
p
|
||||
margin 0
|
||||
> header
|
||||
display flex
|
||||
align-items baseline
|
||||
white-space nowrap
|
||||
|
||||
[data-icon], .mk-reaction-icon
|
||||
> .icon
|
||||
margin-right 4px
|
||||
|
||||
> .name
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
|
||||
> .mk-time
|
||||
margin-left auto
|
||||
color var(--noteHeaderInfo)
|
||||
font-size 0.9em
|
||||
|
||||
.note-preview
|
||||
color var(--noteText)
|
||||
display inline-block
|
||||
@@ -280,20 +315,24 @@ export default Vue.extend({
|
||||
display inline-block
|
||||
margin-right 3px
|
||||
|
||||
&.reaction
|
||||
.text header
|
||||
align-items normal
|
||||
|
||||
&.renote, &.quote
|
||||
.text p [data-icon]
|
||||
.text header [data-icon]
|
||||
color #77B255
|
||||
|
||||
&.follow
|
||||
.text p [data-icon]
|
||||
.text header [data-icon]
|
||||
color #53c7ce
|
||||
|
||||
&.receiveFollowRequest
|
||||
.text p [data-icon]
|
||||
.text header [data-icon]
|
||||
color #888
|
||||
|
||||
&.reply, &.mention
|
||||
.text p [data-icon]
|
||||
.text header [data-icon]
|
||||
color #555
|
||||
|
||||
> .date
|
||||
|
@@ -5,8 +5,8 @@
|
||||
<span class="icon" v-if="geo"><fa icon="map-marker-alt"/></span>
|
||||
<span v-if="!reply">{{ $t('note') }}</span>
|
||||
<span v-if="reply">{{ $t('reply') }}</span>
|
||||
<span class="count" v-if="files.length != 0">{{ this.$t('attaches').replace('{}', files.length) }}</span>
|
||||
<span class="count" v-if="uploadings.length != 0">{{ this.$t('uploading-media').replace('{}', uploadings.length) }}<mk-ellipsis/></span>
|
||||
<span class="count" v-if="files.length != 0">{{ $t('attaches').replace('{}', files.length) }}</span>
|
||||
<span class="count" v-if="uploadings.length != 0">{{ $t('uploading-media').replace('{}', uploadings.length) }}<mk-ellipsis/></span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
<x-post-form ref="form"
|
||||
:reply="reply"
|
||||
:mention="mention"
|
||||
:initial-text="initialText"
|
||||
:initial-note="initialNote"
|
||||
:instant="instant"
|
||||
|
||||
@posted="onPosted"
|
||||
@change-uploadings="onChangeUploadings"
|
||||
@change-attached-files="onChangeFiles"
|
||||
@@ -50,7 +54,23 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
|
||||
initialText: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
|
||||
initialNote: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
|
||||
instant: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@@ -10,14 +10,18 @@
|
||||
<b>{{ $t('@.post-form.recent-tags') }}:</b>
|
||||
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('@.post-form.click-to-tagging')">#{{ tag }}</a>
|
||||
</div>
|
||||
<div class="with-quote" v-if="quoteId">{{ $t('@.post-form.quote-attached') }} <a @click="quoteId = null">[x]</a></div>
|
||||
<div v-if="visibility === 'specified'" class="visibleUsers">
|
||||
<span v-for="u in visibleUsers">
|
||||
<mk-user-name :user="u"/><a @click="removeVisibleUser(u)">[x]</a>
|
||||
</span>
|
||||
<a @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</a>
|
||||
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('@.post-form.quote-attached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
|
||||
<div v-if="visibility === 'specified'" class="to-specified">
|
||||
<fa icon="envelope"/> {{ $t('@.post-form.specified-recipient') }}
|
||||
<div class="visibleUsers">
|
||||
<span v-for="u in visibleUsers">
|
||||
<mk-user-name :user="u"/>
|
||||
<button @click="removeVisibleUser(u)"><fa icon="times"/></button>
|
||||
</span>
|
||||
<button @click="addVisibleUser">{{ $t('@.post-form.add-visible-user') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="local-only" v-if="localOnly === true">{{ $t('@.post-form.local-only-message') }}</div>
|
||||
<div class="local-only" v-if="localOnly === true"><fa icon="heart"/> {{ $t('@.post-form.local-only-message') }}</div>
|
||||
<input v-show="useCw" ref="cw" v-model="cw" :placeholder="$t('@.post-form.cw-placeholder')" v-autocomplete="{ model: 'cw' }">
|
||||
<div class="textarea">
|
||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||
@@ -207,13 +211,37 @@ export default Vue.extend({
|
||||
margin 0 0 8px 0
|
||||
color var(--primary)
|
||||
|
||||
> .visibleUsers
|
||||
margin-bottom 8px
|
||||
font-size 14px
|
||||
> button
|
||||
padding 4px 8px
|
||||
color var(--primaryAlpha04)
|
||||
|
||||
> span
|
||||
margin-right 16px
|
||||
color var(--primary)
|
||||
&:hover
|
||||
color var(--primaryAlpha06)
|
||||
|
||||
&:active
|
||||
color var(--primaryDarken30)
|
||||
|
||||
> .to-specified
|
||||
margin 0 0 8px 0
|
||||
color var(--primary)
|
||||
|
||||
> .visibleUsers
|
||||
display inline
|
||||
top -1px
|
||||
font-size 14px
|
||||
|
||||
> span
|
||||
margin-left 14px
|
||||
|
||||
> button
|
||||
padding 4px 8px
|
||||
color var(--primaryAlpha04)
|
||||
|
||||
&:hover
|
||||
color var(--primaryAlpha06)
|
||||
|
||||
&:active
|
||||
color var(--primaryDarken30)
|
||||
|
||||
> .local-only
|
||||
margin 0 0 8px 0
|
||||
|
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div class="mk-settings">
|
||||
<div class="nav" :class="{ inWindow }">
|
||||
<p :class="{ active: page == 'profile' }" @mousedown="page = 'profile'"><fa icon="user" fixed-width/>{{ $t('@._settings.profile') }}</p>
|
||||
<p :class="{ active: page == 'appearance' }" @mousedown="page = 'appearance'"><fa icon="palette" fixed-width/>{{ $t('@._settings.appearance') }}</p>
|
||||
<p :class="{ active: page == 'behavior' }" @mousedown="page = 'behavior'"><fa icon="desktop" fixed-width/>{{ $t('@._settings.behavior') }}</p>
|
||||
<p :class="{ active: page == 'notification' }" @mousedown="page = 'notification'"><fa :icon="['far', 'bell']" fixed-width/>{{ $t('@._settings.notification') }}</p>
|
||||
<p :class="{ active: page == 'drive' }" @mousedown="page = 'drive'"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</p>
|
||||
<p :class="{ active: page == 'hashtags' }" @mousedown="page = 'hashtags'"><fa icon="hashtag" fixed-width/>{{ $t('@._settings.tags') }}</p>
|
||||
<p :class="{ active: page == 'muteAndBlock' }" @mousedown="page = 'muteAndBlock'"><fa icon="ban" fixed-width/>{{ $t('@._settings.mute-and-block') }}</p>
|
||||
<p :class="{ active: page == 'apps' }" @mousedown="page = 'apps'"><fa icon="puzzle-piece" fixed-width/>{{ $t('@._settings.apps') }}</p>
|
||||
<p :class="{ active: page == 'security' }" @mousedown="page = 'security'"><fa icon="unlock-alt" fixed-width/>{{ $t('@._settings.security') }}</p>
|
||||
<p :class="{ active: page == 'api' }" @mousedown="page = 'api'"><fa icon="key" fixed-width/>API</p>
|
||||
<p :class="{ active: page == 'other' }" @mousedown="page = 'other'"><fa icon="cogs" fixed-width/>{{ $t('@._settings.other') }}</p>
|
||||
<router-link to="/i/settings/profile" active-class="active"><fa icon="user" fixed-width/>{{ $t('@._settings.profile') }}</router-link>
|
||||
<router-link to="/i/settings/appearance" active-class="active"><fa icon="palette" fixed-width/>{{ $t('@._settings.appearance') }}</router-link>
|
||||
<router-link to="/i/settings/behavior" active-class="active"><fa icon="desktop" fixed-width/>{{ $t('@._settings.behavior') }}</router-link>
|
||||
<router-link to="/i/settings/notification" active-class="active"><fa :icon="['far', 'bell']" fixed-width/>{{ $t('@._settings.notification') }}</router-link>
|
||||
<router-link to="/i/settings/drive" active-class="active"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</router-link>
|
||||
<router-link to="/i/settings/hashtags" active-class="active"><fa icon="hashtag" fixed-width/>{{ $t('@._settings.tags') }}</router-link>
|
||||
<router-link to="/i/settings/muteAndBlock" active-class="active"><fa icon="ban" fixed-width/>{{ $t('@._settings.mute-and-block') }}</router-link>
|
||||
<router-link to="/i/settings/apps" active-class="active"><fa icon="puzzle-piece" fixed-width/>{{ $t('@._settings.apps') }}</router-link>
|
||||
<router-link to="/i/settings/security" active-class="active"><fa icon="unlock-alt" fixed-width/>{{ $t('@._settings.security') }}</router-link>
|
||||
<router-link to="/i/settings/api" active-class="active"><fa icon="key" fixed-width/>API</router-link>
|
||||
<router-link to="/i/settings/other" active-class="active"><fa icon="cogs" fixed-width/>{{ $t('@._settings.other') }}</router-link>
|
||||
</div>
|
||||
<div class="pages">
|
||||
<x-settings :page="page"/>
|
||||
@@ -30,9 +30,9 @@ export default Vue.extend({
|
||||
XSettings,
|
||||
},
|
||||
props: {
|
||||
initialPage: {
|
||||
page: {
|
||||
type: String,
|
||||
required: false
|
||||
required: true,
|
||||
},
|
||||
inWindow: {
|
||||
type: Boolean,
|
||||
@@ -40,11 +40,6 @@ export default Vue.extend({
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: this.initialPage || 'profile',
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -63,7 +58,7 @@ export default Vue.extend({
|
||||
z-index 1
|
||||
font-size 15px
|
||||
|
||||
> p
|
||||
> a
|
||||
display block
|
||||
padding 10px 16px
|
||||
margin 0
|
||||
|
@@ -56,6 +56,13 @@
|
||||
<i><fa icon="angle-right"/></i>
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="`/@${ $store.state.i.username }/room`">
|
||||
<i><fa :icon="faDoorOpen" fixed-width/></i>
|
||||
<span>{{ $t('room') }}</span>
|
||||
<i><fa icon="angle-right"/></i>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -106,7 +113,7 @@ import i18n from '../../../i18n';
|
||||
// import MkSettingsWindow from './settings-window.vue';
|
||||
import MkDriveWindow from './drive-window.vue';
|
||||
import contains from '../../../common/scripts/contains';
|
||||
import { faHome, faColumns, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHome, faColumns, faUsers, faDoorOpen } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faMoon, faSun, faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
@@ -114,7 +121,7 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
faHome, faColumns, faMoon, faSun, faStickyNote, faUsers
|
||||
faHome, faColumns, faMoon, faSun, faStickyNote, faUsers, faDoorOpen
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<div class="banner" :style="u.bannerUrl ? `background-image: url(${u.bannerUrl})` : ''"></div>
|
||||
<mk-avatar class="avatar" :user="u" :disable-preview="true"/>
|
||||
<div class="title">
|
||||
<router-link class="name" :to="u | userPage"><mk-user-name :user="u"/></router-link>
|
||||
<router-link class="name" :to="u | userPage"><mk-user-name :user="u" :nowrap="false"/></router-link>
|
||||
<p class="username"><mk-acct :user="u"/></p>
|
||||
</div>
|
||||
<div class="description">
|
||||
|
@@ -79,7 +79,7 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import * as XDraggable from 'vuedraggable';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import XWelcome from '../pages/welcome.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
@@ -224,6 +224,8 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
addWidget() {
|
||||
if(this.widgetAdderSelected == null) return;
|
||||
|
||||
this.$store.commit('addHomeWidget', {
|
||||
name: this.widgetAdderSelected,
|
||||
id: uuid(),
|
||||
|
@@ -94,6 +94,8 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
|
||||
(this.$refs.tl as any).$once('loaded', () => {
|
||||
this.$emit('loaded');
|
||||
});
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<div class="fade"></div>
|
||||
<div class="title">
|
||||
<p class="name">
|
||||
<mk-user-name :user="user"/>
|
||||
<mk-user-name :user="user" :nowrap="false"/>
|
||||
</p>
|
||||
<div>
|
||||
<span class="username"><mk-acct :user="user" :detail="true" /></span>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<mk-ui>
|
||||
<main>
|
||||
<x-settings :in-window="false"/>
|
||||
<x-settings :in-window="false" :page="$route.params.page" />
|
||||
</main>
|
||||
</mk-ui>
|
||||
</template>
|
||||
@@ -13,6 +13,9 @@ export default Vue.extend({
|
||||
components: {
|
||||
XSettings: () => import('../components/settings.vue').then(m => m.default)
|
||||
},
|
||||
mounted() {
|
||||
document.title = this.$root.instanceName;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="mkw-notifications">
|
||||
<ui-container :show-header="!props.compact">
|
||||
<template #header><fa :icon="['far', 'bell']"/>{{ $t('title') }}</template>
|
||||
<!-- <button #func :title="$t('title')" @click="settings"><fa icon="cog"/></button> -->
|
||||
<template #header><fa :icon="['far', 'bell']"/>{{ props.type === 'all' ? $t('title') : $t('@.notification-types.' + props.type) }}</template>
|
||||
<template #func><button :title="$t('@.notification-type')" @click="settings"><fa icon="cog"/></button></template>
|
||||
|
||||
<mk-notifications :class="$style.notifications"/>
|
||||
<mk-notifications :class="$style.notifications" :type="props.type === 'all' ? null : props.type"/>
|
||||
</ui-container>
|
||||
</div>
|
||||
</template>
|
||||
@@ -16,13 +16,28 @@ import i18n from '../../../i18n';
|
||||
export default define({
|
||||
name: 'notifications',
|
||||
props: () => ({
|
||||
compact: false
|
||||
compact: false,
|
||||
type: 'all'
|
||||
})
|
||||
}).extend({
|
||||
i18n: i18n('desktop/views/widgets/notifications.vue'),
|
||||
methods: {
|
||||
settings() {
|
||||
alert('not implemented yet');
|
||||
this.$root.dialog({
|
||||
title: this.$t('@.notification-type'),
|
||||
type: null,
|
||||
select: {
|
||||
items: ['all', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'].map(x => ({
|
||||
value: x, text: this.$t('@.notification-types.' + x)
|
||||
}))
|
||||
default: this.props.type,
|
||||
},
|
||||
showCancelButton: true
|
||||
}).then(({ canceled, result: type }) => {
|
||||
if (canceled) return;
|
||||
this.props.type = type;
|
||||
this.save();
|
||||
});
|
||||
},
|
||||
func() {
|
||||
this.props.compact = !this.props.compact;
|
||||
|
@@ -125,7 +125,8 @@ import {
|
||||
faMapMarker,
|
||||
faRobot,
|
||||
faHourglassHalf,
|
||||
faGavel
|
||||
faGavel,
|
||||
faUndoAlt,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import {
|
||||
@@ -258,6 +259,7 @@ library.add(
|
||||
faRobot,
|
||||
faHourglassHalf,
|
||||
faGavel,
|
||||
faUndoAlt,
|
||||
|
||||
farBell,
|
||||
farEnvelope,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import Vue from 'vue';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import initStore from './store';
|
||||
import { apiUrl, version, locale } from './config';
|
||||
@@ -28,7 +28,12 @@ export default class MiOS extends EventEmitter {
|
||||
};
|
||||
|
||||
public get instanceName() {
|
||||
return this.meta ? (this.meta.data.name || 'Misskey') : 'Misskey';
|
||||
const siteName = document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement;
|
||||
if (siteName && siteName.content) {
|
||||
return siteName.content;
|
||||
}
|
||||
|
||||
return 'Misskey';
|
||||
}
|
||||
|
||||
private isMetaFetching = false;
|
||||
@@ -174,7 +179,7 @@ export default class MiOS extends EventEmitter {
|
||||
// Init service worker
|
||||
if (this.shouldRegisterSw) {
|
||||
this.getMeta().then(data => {
|
||||
this.registerSw(data.swPublickey);
|
||||
if (data.swPublickey) this.registerSw(data.swPublickey);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -291,7 +296,7 @@ export default class MiOS extends EventEmitter {
|
||||
* Register service worker
|
||||
*/
|
||||
@autobind
|
||||
private registerSw(swPublickey) {
|
||||
private registerSw(swPublickey: string) {
|
||||
// Check whether service worker and push manager supported
|
||||
const isSwSupported =
|
||||
('serviceWorker' in navigator) && ('PushManager' in window);
|
||||
|
@@ -14,6 +14,7 @@ import MkIndex from './views/pages/index.vue';
|
||||
import MkSignup from './views/pages/signup.vue';
|
||||
import MkSelectDrive from './views/pages/selectdrive.vue';
|
||||
import MkDrive from './views/pages/drive.vue';
|
||||
import MkNotifications from './views/pages/notifications.vue';
|
||||
import MkMessaging from './views/pages/messaging.vue';
|
||||
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
||||
import MkNote from './views/pages/note.vue';
|
||||
@@ -54,7 +55,10 @@ init((launch, os) => {
|
||||
const vm = this.$root.new(PostFormDialog, {
|
||||
reply: o.reply,
|
||||
mention: o.mention,
|
||||
renote: o.renote
|
||||
renote: o.renote,
|
||||
initialText: o.initialText,
|
||||
instant: o.instant,
|
||||
initialNote: o.initialNote,
|
||||
});
|
||||
vm.$once('cancel', recover);
|
||||
vm.$once('posted', recover);
|
||||
@@ -136,6 +140,7 @@ init((launch, os) => {
|
||||
]),
|
||||
{ path: '/signup', name: 'signup', component: MkSignup },
|
||||
{ path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) },
|
||||
{ path: '/i/settings/:page', redirect: '/i/settings' },
|
||||
{ path: '/i/favorites', name: 'favorites', component: UI, props: route => ({ component: () => import('../common/views/pages/favorites.vue').then(m => m.default), platform: 'mobile' }) },
|
||||
{ path: '/i/pages', name: 'pages', component: UI, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
|
||||
{ path: '/i/lists', name: 'user-lists', component: UI, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
|
||||
@@ -144,6 +149,7 @@ init((launch, os) => {
|
||||
{ path: '/i/groups/:group', component: UI, props: route => ({ component: () => import('../common/views/pages/user-group-editor.vue').then(m => m.default), groupId: route.params.group }) },
|
||||
{ path: '/i/follow-requests', name: 'follow-requests', component: UI, props: route => ({ component: () => import('../common/views/pages/follow-requests.vue').then(m => m.default) }) },
|
||||
{ path: '/i/widgets', name: 'widgets', component: () => import('./views/pages/widgets.vue').then(m => m.default) },
|
||||
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
|
||||
{ path: '/i/messaging', name: 'messaging', component: MkMessaging },
|
||||
{ path: '/i/messaging/group/:group', component: MkMessagingRoom },
|
||||
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
||||
@@ -166,6 +172,7 @@ init((launch, os) => {
|
||||
]},
|
||||
{ path: '/@:user/pages/:page', component: UI, props: route => ({ component: () => import('../common/views/pages/page.vue').then(m => m.default), pageName: route.params.page, username: route.params.user }) },
|
||||
{ path: '/@:user/pages/:pageName/view-source', component: UI, props: route => ({ component: () => import('../common/views/pages/page-editor/page-editor.vue').then(m => m.default), initUser: route.params.user, initPageName: route.params.pageName }) },
|
||||
{ path: '/@:acct/room', props: true, component: () => import('../common/views/pages/room/room.vue').then(m => m.default) },
|
||||
{ path: '/notes/:note', component: MkNote },
|
||||
{ path: '/authorize-follow', component: MkFollow },
|
||||
{ path: '*', component: MkNotFound }
|
||||
|
@@ -122,7 +122,7 @@ export default Vue.extend({
|
||||
this.$root.api('drive/files/delete', {
|
||||
fileId: this.file.id
|
||||
}).then(() => {
|
||||
this.browser.cd(this.file.folderId, true);
|
||||
this.browser.cd(this.file.folderId);
|
||||
});
|
||||
},
|
||||
|
||||
|
@@ -163,8 +163,6 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
cd(target, silent = false) {
|
||||
this.file = null;
|
||||
|
||||
if (target == null) {
|
||||
this.goRoot(silent);
|
||||
return;
|
||||
@@ -172,6 +170,7 @@ export default Vue.extend({
|
||||
target = target.id;
|
||||
}
|
||||
|
||||
this.file = null;
|
||||
this.fetching = true;
|
||||
|
||||
this.$root.api('drive/folders/show', {
|
||||
@@ -244,13 +243,14 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
goRoot(silent = false) {
|
||||
if (this.folder || this.file) {
|
||||
this.file = null;
|
||||
this.folder = null;
|
||||
this.hierarchyFolders = [];
|
||||
this.$emit('move-root', silent);
|
||||
this.fetch();
|
||||
}
|
||||
// すでにrootにいるなら何もしない
|
||||
if (this.folder == null && this.file == null) return;
|
||||
|
||||
this.file = null;
|
||||
this.folder = null;
|
||||
this.hierarchyFolders = [];
|
||||
this.$emit('move-root', silent);
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
fetch() {
|
||||
|
@@ -100,6 +100,27 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mk-notification
|
||||
|
||||
&.wide
|
||||
> .notification
|
||||
@media (min-width 350px)
|
||||
font-size 14px
|
||||
|
||||
@media (min-width 500px)
|
||||
font-size 16px
|
||||
|
||||
@media (min-width 600px)
|
||||
padding 24px 32px
|
||||
|
||||
> .avatar
|
||||
@media (min-width 500px)
|
||||
width 42px
|
||||
height 42px
|
||||
|
||||
> div
|
||||
@media (min-width 500px)
|
||||
width calc(100% - 42px)
|
||||
|
||||
> .notification
|
||||
padding 16px
|
||||
font-size 12px
|
||||
@@ -159,6 +180,10 @@ export default Vue.extend({
|
||||
display inline-block
|
||||
margin-right 3px
|
||||
|
||||
&.reaction
|
||||
> div > header
|
||||
align-items normal
|
||||
|
||||
&.renote
|
||||
> div > header [data-icon]
|
||||
color #77B255
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user