Compare commits

...

28 Commits

Author SHA1 Message Date
syuilo
58d0dc1795 11.28.1 2019-08-18 16:56:42 +09:00
syuilo
bc11702f7d Fix #5290 2019-08-18 16:55:09 +09:00
syuilo
6288de5813 Fix #5291 2019-08-18 16:51:27 +09:00
syuilo
bbf59c7d9f 11.28.0 2019-08-18 16:07:55 +09:00
imgbot[bot]
d7787bacf7 [ImgBot] Optimize images (#5289)
*Total -- 2,381.44kb -> 1,992.59kb (16.33%)

/src/client/assets/room/furnitures/photoframe/photo-uv.png -- 20.34kb -> 2.58kb (87.33%)
/src/client/assets/room/furnitures/pc/motherboard-uv.png -- 21.43kb -> 2.85kb (86.69%)
/src/client/assets/room/furnitures/cardboard-box3/uv.png -- 21.58kb -> 3.51kb (83.72%)
/src/client/assets/room/furnitures/poster-v/uv.png -- 21.85kb -> 3.71kb (83.04%)
/src/client/assets/room/furnitures/milk/milk.png -- 3.76kb -> 0.64kb (82.89%)
/src/client/assets/room/furnitures/server/uv.png -- 22.29kb -> 3.92kb (82.42%)
/src/client/assets/room/furnitures/facial-tissue/facial-tissue.png -- 3.80kb -> 0.67kb (82.3%)
/src/client/assets/room/rooms/washitsu/tatami-uv.png -- 22.27kb -> 3.98kb (82.13%)
/src/client/assets/room/rooms/washitsu/husuma-uv.png -- 22.06kb -> 3.94kb (82.13%)
/src/client/assets/room/furnitures/tv/screen-uv.png -- 20.55kb -> 3.70kb (81.98%)
/src/client/assets/room/furnitures/poster-h/uv.png -- 20.15kb -> 3.70kb (81.65%)
/src/client/assets/room/furnitures/plant/plant-soil-uv.png -- 23.17kb -> 4.40kb (81%)
/src/client/assets/room/furnitures/facial-tissue/facial-tissue-uv.png -- 22.96kb -> 4.37kb (80.99%)
/src/client/assets/room/furnitures/cardboard-box2/uv.png -- 22.36kb -> 4.52kb (79.78%)
/src/client/assets/room/furnitures/book2/uv.png -- 37.36kb -> 8.41kb (77.49%)
/src/client/assets/room/furnitures/server/rack-uv.png -- 26.68kb -> 6.55kb (75.46%)
/src/client/assets/room/furnitures/monitor/screen-uv.png -- 5.81kb -> 1.46kb (74.88%)
/src/client/assets/room/furnitures/milk/milk-uv.png -- 49.21kb -> 16.19kb (67.1%)
/src/client/assets/room/furnitures/eraser/eraser-uv.png -- 27.93kb -> 10.90kb (60.96%)
/src/client/assets/room/furnitures/book2/barcode.png -- 8.71kb -> 3.42kb (60.72%)
/src/client/assets/room/rooms/washitsu/husuma.png -- 4.23kb -> 2.48kb (41.44%)
/src/client/assets/room/furnitures/server/rack.png -- 9.42kb -> 6.88kb (26.92%)
/src/client/assets/room/furnitures/eraser/cover.png -- 10.75kb -> 8.14kb (24.26%)
/src/client/assets/room/furnitures/plant2/soil.png -- 13.06kb -> 10.60kb (18.83%)
/src/client/assets/room/furnitures/plant/plant-soil.png -- 13.06kb -> 10.60kb (18.83%)
/src/client/assets/room/furnitures/monitor/screen.jpg -- 28.93kb -> 24.26kb (16.16%)
/src/client/assets/room/furnitures/cardboard-box3/texture.png -- 18.23kb -> 15.82kb (13.21%)
/src/client/assets/room/furnitures/cardboard-box2/texture.png -- 18.64kb -> 16.48kb (11.56%)
/src/client/assets/room/furnitures/book2/texture.png -- 64.05kb -> 59.59kb (6.97%)
/src/client/assets/room/furnitures/server/server.png -- 121.75kb -> 114.13kb (6.26%)
/src/client/assets/room/furnitures/pc/motherboard.jpg -- 67.25kb -> 63.09kb (6.19%)
/src/client/assets/room/furnitures/photoframe/photo.jpg -- 31.10kb -> 29.79kb (4.22%)
/src/client/assets/room/rooms/washitsu/tatami-single1600.png -- 1,163.42kb -> 1,145.84kb (1.51%)
/src/client/assets/room/rooms/washitsu/tatami.png -- 308.19kb -> 306.37kb (0.59%)
/src/client/assets/room/furnitures/moon/moon.jpg -- 85.11kb -> 85.11kb (0%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2019-08-18 15:06:22 +09:00
syuilo
23ae0515c4 Update CHANGELOG.md 2019-08-18 15:05:13 +09:00
syuilo
fe88b34b8a Update node version 2019-08-18 14:56:54 +09:00
syuilo
74aa031a22 Update dependencies 🚀 2019-08-18 14:56:47 +09:00
syuilo
6aeed212d9 New Crowdin translations (#5249)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

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

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Czech)

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

* New translations ja-JP.yml (Danish)

* New translations ja-JP.yml (Dutch)
2019-08-18 14:43:30 +09:00
syuilo
45b972c059 MisskeyRoom (#5267)
* wip

* Add pemcil

* Fix bug

* Update .gitattributes

* Add 🍮

* Better 🍮

* Add color boxes

* Add pc

* Add keyboard

* Add 📦

* Add more 📦

* ✌️

* carpet

* Add plant

* ✌️

* ✌️

* Update room.vue

* Add plant

* ✌️

* ✌️

* ✌️

* ✌️

* ✌️

* 段ボール箱がてかりすぎているのを修正

* Update room.ts

* Render username

* ✌️

* Add new 📦

* Update room.ts

* Remove blender backup files

* Refactor

* Improve performance

* Update room.ts

* Update .gitattributes

* Update room.ts

* Better fan

* Better tissue rendering

* Add 🐧

* Create photoframe2.glb

* chairs

* Add 📖

* fix: HiDPi環境でオブジェクトを選択できない (#5268)

* Better monitor

* ✌️

* Add corkboard

* Add missing blend

* mousepad

* Add missing blend

* Add cube

* 額縁やモニターなどに任意の画像を表示できるように

* Update MisskeyRoom section of CONTRIBUTING.md (#5272)

* Update MisskeyRoom section of CONTRIBUTING.md

* Update CONTRIBUTING.md

* Update CONTRIBUTING.md

* Refactor

* カスタムテクスチャがずれないように

* Remove debug code

* Update furnitures.json5

* 一部の家具の色を自由に変えられるように

* Update ja-JP.yml

* Type annotation

* 家具の色やテクスチャをすぐ反映するように

* Update room.vue

* Update furnitures.json5

* Add 📺

* Update ja-JP.yml

* 床の色を変えられるように

* 和室にできるように

* Update washitsu

* Use MeshLambertMaterial to improve performance

* Use MeshLambertMaterial

* Fix bug

* Refactor

* Update room.ts

* Fix washitsu

* Update room.vue

* Update washistu

* Use MeshLambertMaterial

* Update room.ts

* Set current property value

* Disable reactivity to improve performance a bit

* Fix bug

* Set current carpet color

* Update ja-JP.yml

* Add rubik-cube (#5278)

* Update ja-JP.yml (#5279)

* Update UI

* ルームの設定を追加

* Add room link

* 家具をドラッグで移動や回転できるように

* esnextにする (#5286)

* Fix moduleResolution

* Use uuid v4

* Fix bug

* マットの色を変えられるように

* ✌️

* 異方性フィルタリングするように

* グラフィックの品質をフィルタリングに反映

* Add bloom effect when ultra graphics

* Add posters

* 🎨
2019-08-18 14:41:33 +09:00
MeiMei
7ecfc007a9 updateHashtagを並列で行わないように (#5284) 2019-08-18 12:47:45 +09:00
MeiMei
fc78c75bab Fix: Hashtagがupdateできない (#5285) 2019-08-18 12:46:47 +09:00
Satsuki Yanagi
59493a0cd9 uuid() と lint (#5288)
* Import only v4 uuid, uuid() without version is deprecated

* Add Missing semicolon
2019-08-18 12:42:58 +09:00
MeiMei
6060c6d56e リモートユーザー向けのNoteUnreadsレコードは作成しないように (#5280) 2019-08-17 14:40:06 +09:00
MeiMei
6cdbb27169 Fix: ドライブアップロード直後に取得できるURLがoriginalじゃない (#5274) 2019-08-17 12:32:52 +09:00
MeiMei
ed8b073e54 タイトルやアイコンがきちんと設定されないことがあるのを修正 (#5265)
* Fix: og:site_nameがbase系ページに正しく反映さんれない

* instanceNameはAPIのmetaじゃなくてog:site_nameを参照するように

* Fix: タイトルが変更されるページから通常ページに遷移してもタイトルが戻らない

* Fix: タイトルが戻らない mobile / notifications

* Fix: faviconの変更が効かないページがある
2019-08-16 14:16:19 +09:00
syuilo
7dd193636c Fix #5260 2019-08-11 20:13:57 +09:00
MeiMei
01d018510c ユーザー名の突き抜けの修正 (#5261)
* 突き抜け deck フォロリク/vote

* 突き抜け desktop

* notification reactionで絵文字の縦がずれないように

* Fix: ユーザーページの名前が突き抜ける

* Fix: デッキカラムでユーザー名が長いと閉じれなくなる

* デッキのカウントの位置が右になってしまってたのを修正

* デッキヘッダーのellipsis
2019-08-11 19:48:54 +09:00
和風ドレッシング
1c273a0a75 Elasticsearchのインデックス名をconfigで変更できるように (#5257) 2019-08-09 13:04:35 +09:00
Satsuki Yanagi
fa2c7658a0 PackedUserがサイレンスや凍結の情報を持つように (#5255) 2019-08-08 09:31:40 +09:00
Satsuki Yanagi
84ca3a7d45 Unified the notation with "Renote" for consistency (#5256)
https://crowdin.com/translate/misskey/3/ja-zhcn#5238
2019-08-08 06:45:39 +09:00
Acid Chicken (硫酸鶏)
902c73e6ac Update README.md [AUTOGEN] (#5251) 2019-08-06 23:23:01 +09:00
MeiMei
3b626f72e4 Resolve #5247 (#5248) 2019-08-05 10:30:31 +09:00
Acid Chicken (硫酸鶏)
f5ce137a6b Update README.md [AUTOGEN] (#5246) 2019-08-03 19:56:59 +09:00
Acid Chicken (硫酸鶏)
3ce9d12361 Update README.md [AUTOGEN] (#5243) 2019-08-02 22:37:31 +09:00
Acid Chicken (硫酸鶏)
2fe2f3b1eb Update README.md [AUTOGEN] (#5240) 2019-08-02 21:21:31 +09:00
Acid Chicken (硫酸鶏)
17b3ee41db Update README.md [AUTOGEN] (#5239) 2019-08-01 22:26:11 +09:00
Takeshi Umeda
56d2a5d5d3 Modify ssl directive to 'ssl' for listen directive (#5237)
the "ssl" directive is deprecated. 
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl
2019-08-01 19:12:32 +09:00
210 changed files with 2052 additions and 185 deletions

4
.gitattributes vendored
View File

@@ -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
View File

@@ -30,3 +30,10 @@ api-docs.json
.DS_Store
/files
ormconfig.json
# blender backups
*.blend1
*.blend2
*.blend3
*.blend4
*.blend5

View File

@@ -1 +1 @@
v12.6.0
v12.8.1

View File

@@ -1,6 +1,29 @@
ChangeLog
=========
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

View File

@@ -38,6 +38,16 @@ 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).
## Adding MisskeyRoom items
Currently, we accept only 3D models created with [Blender](https://www.blender.org/).
* Use English for material, object and texture names
* Use meter for unit of length
* Your PR must include all source files of your models (for later editing)
* Your PR must include the glTF binary files (.glb) of your models
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?

View File

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

View File

@@ -104,7 +104,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<!-- PATREON_START -->
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20010324/b8af4bd31ae34fbf8806cc0e6228e400/1.png?token-time=2145916800&token-hash=iyiocfousNIUwASmatsIDq8EOsmLUdrQNkWyktHlmJg%3D" alt="Nemo" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
@@ -112,7 +111,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/user?u=20010324">Nemo</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
@@ -124,32 +122,27 @@ 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>
@@ -159,7 +152,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<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>
@@ -173,7 +165,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Wed, 31 Jul 2019 15:23:08 UTC
**Last updated:** Mon, 05 Aug 2019 20:46:06 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright

View File

@@ -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

View File

@@ -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/**/*',

View File

@@ -34,10 +34,14 @@ 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"
@@ -500,6 +504,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í"
@@ -1345,3 +1350,8 @@ pages:
arg1: "Seznamy"
types:
array: "Seznamy"
room:
translate: "Přesunout"
save: "Uložit"
furnitures:
moon: "Po"

View File

@@ -35,6 +35,7 @@ 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"
@@ -1909,3 +1910,8 @@ pages:
enviromentVariables: "Miljø variabel"
pageVariables: "Side element"
argVariables: "Input slot"
room:
translate: "Flyt"
save: "Gem"
furnitures:
moon: "Man"

View File

@@ -35,6 +35,7 @@ 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"
@@ -945,3 +946,7 @@ pages:
arg1: "Listen"
types:
array: "Listen"
room:
save: "Speichern"
furnitures:
moon: "Mo"

View File

@@ -30,13 +30,14 @@ 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 fediverse"
unfollow-confirm: "Do you want to unfollow {name}?"
signin-required: "Please Log In"
delete-confirm: "Are you sure you want to delete this post?"
signin-required: "Please login"
notification-type: "Notification Type"
notification-types:
all: "All"
@@ -2073,3 +2074,8 @@ pages:
enviromentVariables: "Environment variable"
pageVariables: "Page element"
argVariables: "Input slot"
room:
translate: "Move"
save: "Save"
furnitures:
moon: "M"

View File

@@ -31,6 +31,7 @@ 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"

View File

@@ -35,6 +35,7 @@ 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:
@@ -1998,3 +1999,8 @@ pages:
emptySlot: "Slot vide"
enviromentVariables: "Variables d'environnement"
pageVariables: "Élément de page"
room:
translate: "Déplacer"
save: "Enregistrer"
furnitures:
moon: "L"

View File

@@ -37,6 +37,7 @@ common:
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
fetching-as-ap-object: "連合に照会中"
unfollow-confirm: "{name}さんをフォロー解除しますか?"
delete-confirm: "この投稿を削除しますか?"
signin-required: "ログインしてください"
notification-type: "通知の種類"
notification-types:
@@ -307,6 +308,16 @@ common:
saved: "保存しました"
home-profile: "ホームのプロファイル"
deck-profile: "デッキのプロファイル"
room: "ルーム"
_room:
graphicsQuality: "グラフィックの品質"
_graphicsQuality:
ultra: "最高"
high: "高"
medium: "中"
low: "低"
cheep: "最低"
useOrthographicCamera: "平行投影カメラを使用"
search: "検索"
delete: "削除"
@@ -583,7 +594,7 @@ common/views/components/note-menu.vue:
delete: "削除"
delete-confirm: "この投稿を削除しますか?"
delete-and-edit: "削除して編集"
delete-and-edit-confirm: "この投稿を削除してもう一度編集しますか?この投稿へのリアクション、リノート、返信も全て削除されます。"
delete-and-edit-confirm: "この投稿を削除してもう一度編集しますか?この投稿へのリアクション、Renote、返信も全て削除されます。"
remote: "投稿元で見る"
pin-limit-exceeded: "これ以上ピン留めできません。"
@@ -1249,6 +1260,7 @@ desktop/views/components/ui.header.account.vue:
groups: "グループ"
follow-requests: "フォロー申請"
admin: "管理"
room: "ルーム"
desktop/views/components/ui.header.nav.vue:
game: "ゲーム"
@@ -2280,3 +2292,55 @@ pages:
enviromentVariables: "環境変数"
pageVariables: "ページ要素"
argVariables: "入力スロット"
room:
add-furniture: "家具を置く"
translate: "移動"
rotate: "回転"
exit: "戻る"
remove: "しまう"
save: "保存"
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: "ポスター(縦長)"

View File

@@ -27,6 +27,7 @@ common:
load-more: "もっとあらへんのか!"
enter-password: "パスワードを入れてや"
2fa: "二段階認証"
delete-confirm: "この投稿を削除してもええか?"
notification-types:
all: "すべて"
follow: "フォロー"

View File

@@ -36,6 +36,7 @@ common:
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
fetching-as-ap-object: "연합에서 조회 중"
unfollow-confirm: "{name} 님을 팔로우 해제하시겠습니까?"
delete-confirm: "이 글을 삭제하시겠습니까?"
signin-required: "로그인 해주세요"
notification-type: "알림의 종류"
notification-types:
@@ -2073,3 +2074,8 @@ pages:
enviromentVariables: "환경 변수"
pageVariables: "페이지 요소"
argVariables: "입력 슬롯"
room:
translate: "이동"
save: "저장"
furnitures:
moon: "월"

View File

@@ -638,3 +638,7 @@ pages:
arg1: "Lijsten"
types:
array: "Lijsten"
room:
translate: "Verplaatsen"
furnitures:
moon: "M"

View File

@@ -28,12 +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"
reply: "Odpowiedz"
receiveFollowRequest: "Prośby o śledzenie"
reply: "Odpowiedzi"
quote: "Cytat"
renote: "Udostępnij"
reaction: "Reakcja"
mention: "Wzmianki"
reaction: "Reakcje"
got-it: "Rozumiem!"
customization-tips:
title: "Wskazówki o dostosowywaniu"
@@ -1250,3 +1256,8 @@ pages:
arg1: "Listy"
types:
array: "Listy"
room:
translate: "Przenieś"
save: "Zapisz"
furnitures:
moon: "Pn"

View File

@@ -36,6 +36,7 @@ common:
reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?"
fetching-as-ap-object: "联合查询"
unfollow-confirm: "取消对{name}的关注?"
delete-confirm: "确定删除这个投稿吗?"
signin-required: "请先登录"
notification-type: "通知类型"
notification-types:
@@ -2073,3 +2074,8 @@ pages:
enviromentVariables: "环境变量"
pageVariables: "页面元素"
argVariables: "输入槽函数"
room:
translate: "移动"
save: "保存"
furnitures:
moon: "一"

View 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"`);
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "11.27.1",
"version": "11.28.1",
"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",
@@ -99,25 +99,25 @@
"@types/ws": "6.0.1",
"@typescript-eslint/parser": "1.11.0",
"agentkeepalive": "4.0.2",
"animejs": "3.0.1",
"apexcharts": "3.8.3",
"animejs": "3.1.0",
"apexcharts": "3.8.4",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
"aws-sdk": "2.500.0",
"aws-sdk": "2.512.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.2.1",
"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.1.0",
"css-loader": "3.2.0",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"deep-equal": "1.0.1",
@@ -128,7 +128,7 @@
"eslint-plugin-vue": "5.2.3",
"eventemitter3": "4.0.0",
"feed": "3.0.0",
"file-type": "12.0.1",
"file-type": "12.1.0",
"fluent-ffmpeg": "2.1.2",
"fuckadblock": "3.2.1",
"gulp": "4.0.2",
@@ -155,7 +155,7 @@
"json5": "2.1.0",
"json5-loader": "3.0.0",
"jsrsasign": "8.0.12",
"katex": "0.10.2",
"katex": "0.11.0",
"koa": "2.7.0",
"koa-bodyparser": "4.2.1",
"koa-compress": "3.0.0",
@@ -170,7 +170,7 @@
"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",
"mocha": "6.2.0",
"moji": "0.5.1",
@@ -182,11 +182,11 @@
"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-limit": "2.7.0",
"promise-sequential": "1.1.1",
@@ -196,7 +196,7 @@
"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",
"redis": "2.8.0",
@@ -209,8 +209,8 @@
"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",
@@ -221,8 +221,9 @@
"summaly": "2.3.0",
"systeminformation": "4.14.4",
"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.107.0",
"tinycolor2": "1.4.1",
"tmp": "0.1.0",
"ts-loader": "5.3.3",
@@ -241,13 +242,13 @@
"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.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",
@@ -257,10 +258,10 @@
"vuex": "3.1.1",
"vuex-persistedstate": "2.5.4",
"web-push": "3.3.5",
"webpack": "4.36.1",
"webpack-cli": "3.3.6",
"webpack": "4.39.2",
"webpack-cli": "3.3.7",
"websocket": "1.0.29",
"ws": "7.1.1",
"ws": "7.1.2",
"xev": "2.0.1"
},
"devDependencies": {

View File

@@ -1,5 +1,5 @@
declare module 'lookup-dns-cache' {
import { LookupOneOptions, LookupAllOptions, LookupOptions, LookupAddress } from 'dns'
import { LookupOneOptions, LookupAllOptions, LookupOptions, LookupAddress } from 'dns';
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;

View File

@@ -4,6 +4,7 @@ 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);
@@ -21,6 +22,8 @@ type Opts = {
};
export default (opts: Opts = {}) => ({
i18n: i18n(),
data() {
return {
showContent: false,
@@ -175,8 +178,16 @@ 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
});
});
},

View File

@@ -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);
}
},
@@ -152,7 +160,7 @@ export default (opts) => ({
this.queue = [];
},
onWindowScroll() {
onScroll() {
if (this.isScrollTop()) {
this.onTop();
}
@@ -163,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();
}
},

View 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>;
};

View File

@@ -0,0 +1,324 @@
// 家具メタデータ
// 家具にはユーザーが設定できるプロパティを設定可能です:
//
// 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"
},
{
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,
},
},
},
},
]

View File

@@ -0,0 +1,695 @@
import autobind from 'autobind-decorator';
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 { v4 as uuid } from 'uuid';
const furnitureDefs = require('./furnitures.json5');
THREE.ImageUtils.crossOrigin = '';
type Options = {
graphicsQuality: Room['graphicsQuality'];
onChangeSelect: Room['onChangeSelect'];
useOrthographicCamera: boolean;
};
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;
public canvas: HTMLCanvasElement;
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.gammaOutput = true;
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
this.canvas = this.renderer.domElement;
container.appendChild(this.renderer.domElement);
//#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 outLight = new THREE.SpotLight(0xffffff, 0.4);
outLight.position.set(9, 3, -2);
outLight.castShadow = this.enableShadow;
outLight.shadow.bias = -0.001; // アクネ、アーチファクト対策 その代わりピーターパンが発生する可能性がある
outLight.shadow.mapSize.width = this.shadowQuality;
outLight.shadow.mapSize.height = this.shadowQuality;
outLight.shadow.camera.near = 6;
outLight.shadow.camera.far = 15;
outLight.shadow.camera.fov = 45;
this.scene.add(outLight);
//#endregion
//#region Init a controller
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
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 = 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.MeshLambertMaterial({
emissive: 0x111111,
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.renderer.domElement);
this.scene.add(this.furnitureControl);
// Hover highlight
this.renderer.domElement.onmousemove = this.onmousemove;
// Click
this.renderer.domElement.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() {
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() {
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() {
new GLTFLoader().load(`/assets/room/rooms/${this.roomInfo.roomType}/${this.roomInfo.roomType}.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 = 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;
}
});
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') {
(child.material as THREE.MeshStandardMaterial).color.setHex(parseInt(this.roomInfo.carpetColor.substr(1), 16));
}
});
}
@autobind
public 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;
(child.material as THREE.MeshStandardMaterial).color.setHex(parseInt(val.substr(1), 16));
}
});
}
@autobind
public 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.renderer.domElement.width) * 2 - 1;
const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.renderer.domElement.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)) {
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.renderer.domElement || ev.button !== 0) return;
const rect = (ev.target as HTMLElement).getBoundingClientRect();
const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.renderer.domElement.width) * 2 - 1;
const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.renderer.domElement.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);
}
});
}
@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();
}
@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);
}
@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 updateCarpetColor(color: string) {
this.roomInfo.carpetColor = color;
this.applyCarpetColor();
}
@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);
}
}

View File

@@ -159,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>
@@ -503,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 }); }

View File

@@ -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'),

View File

@@ -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';

View File

@@ -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: {

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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'),

View File

@@ -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(),

View File

@@ -143,7 +143,11 @@ export default Vue.extend({
this.$root.getMeta().then(meta => {
this.meta = meta;
});
}
},
mounted() {
document.title = this.$root.instanceName;
},
});
</script>

View File

@@ -40,5 +40,9 @@ export default Vue.extend({
icon: faStar
});
},
mounted() {
document.title = this.$root.instanceName;
},
});
</script>

View File

@@ -40,5 +40,9 @@ export default Vue.extend({
icon: faNewspaper
});
},
mounted() {
document.title = this.$root.instanceName;
},
});
</script>

View File

@@ -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 });
},
}

View File

@@ -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 });
},
}

View File

@@ -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);

View File

@@ -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',
@@ -292,7 +292,7 @@ export default Vue.extend({
});
if (canceled) return;
const id = uuid.v4();
const id = uuid();
this.content.push({ id, type });
},
@@ -316,7 +316,7 @@ export default Vue.extend({
return;
}
const id = uuid.v4();
const id = uuid();
this.variables.push({ id, name, type: null });
},

View File

@@ -52,6 +52,9 @@ export default Vue.extend({
icon: faStickyNote
});
},
mounted() {
document.title = this.$root.instanceName;
},
methods: {
create() {
this.$router.push(`/i/pages/new`);

View File

@@ -0,0 +1,98 @@
<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
};
},
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 = 2 + this.objectHeight / 2;
camera.position.z = Math.cos(timer) * 10;
camera.position.x = Math.sin(timer) * 10;
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;
scene.add(obj);
};
render();
},
});
</script>

View File

@@ -0,0 +1,239 @@
<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">
<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 @click="save()"><fa :icon="faSave"/> {{ $t('save') }}</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 } from '@fortawesome/free-solid-svg-icons';
import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
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,
faBoxOpen, faSave, faTrashAlt, faUndo, faArrowsAlt, faBan,
};
},
async mounted() {
const user = await this.$root.api('users/show', {
...parseAcct(this.acct)
});
const roomInfo = await this.$root.api('room/show', {
userId: user.id
});
this.roomType = roomInfo.roomType;
this.carpetColor = roomInfo.carpetColor;
room = new Room(user, this.$store.getters.isSignedIn && this.$store.state.i.id === user.id, 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
});
},
methods: {
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);
},
remove() {
this.isTranslateMode = false;
this.isRotateMode = false;
room.removeFurniture();
},
save() {
this.$root.api('room/update', {
room: room.getRoomInfo()
});
},
chooseImage(key) {
this.$chooseDriveFile({
multiple: false
}).then(file => {
room.updateProp(key, file.thumbnailUrl);
this.$refs.preview.selected(room.getSelectedObject());
});
},
updateColor(key, ev) {
room.updateProp(key, ev.target.value);
this.$refs.preview.selected(room.getSelectedObject());
},
updateCarpetColor(ev) {
room.updateCarpetColor(ev.target.value);
this.carpetColor = ev.target.value;
},
updateRoomType(type) {
room.changeRoomType(type);
this.roomType = type;
},
translate() {
if (this.isTranslateMode) {
this.exit();
} else {
this.isRotateMode = false;
this.isTranslateMode = true;
room.enterTransformMode('translate');
}
},
rotate() {
if (this.isRotateMode) {
this.exit();
} else {
this.isTranslateMode = false;
this.isRotateMode = true;
room.enterTransformMode('rotate');
}
},
exit() {
this.isTranslateMode = false;
this.isRotateMode = false;
room.exitTransformMode();
}
}
});
</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>

View File

@@ -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;
});

View File

@@ -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;

View File

@@ -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',

View File

@@ -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'],

View File

@@ -187,6 +187,7 @@ init(async (launch, os) => {
{ path: '/i/drive/folder/:folder', component: MkDrive },
{ path: '/i/settings', 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 },

View File

@@ -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,7 +165,9 @@ export default Vue.extend({
i18n: i18n(),
mixins: [
paging({}),
paging({
isContainer: true
}),
],
props: {
@@ -241,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
@@ -270,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
@@ -296,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

View File

@@ -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: {

View File

@@ -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">

View File

@@ -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({

View File

@@ -94,6 +94,8 @@ export default Vue.extend({
},
mounted() {
document.title = this.$root.instanceName;
(this.$refs.tl as any).$once('loaded', () => {
this.$emit('loaded');
});

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;

View File

@@ -180,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

View File

@@ -25,6 +25,9 @@ export default Vue.extend({
faBell,
};
},
mounted() {
document.title = this.$root.instanceName;
},
methods: {
beforeInit() {
Progress.start();

View File

@@ -17,7 +17,7 @@
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
</div>
<div class="title">
<h1><mk-user-name :user="user" :key="user.id"/></h1>
<h1><mk-user-name :user="user" :key="user.id" :nowrap="false"/></h1>
<span class="username"><mk-acct :user="user" :detail="true" :key="user.id"/></span>
<span class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</span>
</div>

View File

@@ -54,7 +54,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';
export default Vue.extend({
i18n: i18n('mobile/views/pages/widgets.vue'),

View File

@@ -76,6 +76,8 @@ const defaultDeviceSettings = {
expandUsersPhotos: true,
expandUsersActivity: true,
enableMobileQuickNotificationView: false,
roomGraphicsQuality: 'medium',
roomUseOrthographicCamera: true,
};
export default (os: MiOS) => new Vuex.Store({

View File

@@ -11,11 +11,17 @@
"sourceMap": false,
"target": "es2017",
"module": "esnext",
"moduleResolution": "node",
"removeComments": false,
"noLib": false,
"strict": true,
"strictNullChecks": false,
"experimentalDecorators": true
"experimentalDecorators": true,
"resolveJsonModule": true,
"typeRoots": [
"node_modules/@types",
"src/@types"
]
},
"compileOnSave": false,
"include": [

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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