Compare commits

...

149 Commits

Author SHA1 Message Date
syuilo
6fb7721798 Merge branch 'develop' 2020-03-21 13:36:41 +09:00
syuilo
0018fd469e 12.22.0 2020-03-21 13:36:21 +09:00
syuilo
019f7480e8 Fix bug 2020-03-21 13:21:32 +09:00
syuilo
8e0b088deb 🎨 2020-03-21 13:19:42 +09:00
syuilo
452005381f Fix bug 2020-03-21 13:10:44 +09:00
syuilo
9722ed99a3 リモートユーザーでも投稿数とか見れるように 2020-03-21 13:07:15 +09:00
syuilo
ee6311e83d Resolve #6145 2020-03-21 13:07:02 +09:00
syuilo
1471e52307 Resolve #6110 2020-03-21 12:48:25 +09:00
syuilo
f1fc12d9cc wip 2020-03-21 12:32:40 +09:00
syuilo
ebbc42bebc wip 2020-03-21 00:21:33 +09:00
syuilo
4785ee8c32 wip 2020-03-20 23:08:45 +09:00
syuilo
ab40756c1a wip 2020-03-20 22:42:35 +09:00
syuilo
c88e737a84 wip 2020-03-20 22:37:44 +09:00
syuilo
def5ea7978 wip 2020-03-20 21:58:04 +09:00
syuilo
e69ab45044 wip 2020-03-20 19:19:28 +09:00
syuilo
25d0b4bbf1 wip 2020-03-20 19:06:50 +09:00
syuilo
f86f5ac6cc wip 2020-03-20 18:58:17 +09:00
syuilo
07ce365bfd wip 2020-03-20 18:55:15 +09:00
syuilo
f31c94e2ea wip 2020-03-20 18:11:39 +09:00
syuilo
933638d035 Fix bug 2020-03-20 18:00:42 +09:00
Oni-Men
b0151afa9a Add range component, 音量設定で使用する (#6146)
* add range component, use range component at volume setting

* refactor

* refactor 2

* Update range.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-03-20 14:30:37 +09:00
MeiMei
5bbd4ae703 ElasticSearchで認証ができるように (#6158) 2020-03-20 14:00:34 +09:00
Oni-Men
f2f7f532a0 use username if name was empty (#6166) 2020-03-20 13:57:55 +09:00
MeiMei
80eedf7449 連携ログインができないのなどを修正 (#6162)
* 連携ログインができないのを修正

* Cookie名変更, セッションに

* igiはやっぱり非セッションCookieで

* 2回目以降Discordログインできなくなるのを修正
2020-03-20 13:56:22 +09:00
MeiMei
1b48e0d6e0 Revert "Update dependencies (#6167)" (#6168)
This reverts commit 0420c548da.
2020-03-20 02:46:13 +09:00
MeiMei
0420c548da Update dependencies (#6167)
* Update CI (#11)

* Update nodejs.yml

* Fix time

* no docker

* no CI

* build only

* Update dependencies
2020-03-20 02:40:35 +09:00
Acid Chicken (硫酸鶏)
6e98b75d13 Merge pull request #6165 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-03-19 12:54:00 +09:00
Acid Chicken (硫酸鶏)
e772cb00d1 Update README.md [AUTOGEN] 2020-03-18 03:57:08 +09:00
Yuiga Wada
b5d5275e9b Auth認証画面から正常にログインできるよう修正 (#6154)
* Fix #6095

Auth認証画面から正常にログインできるよう修正

* Fix #6095
2020-03-14 15:59:02 +09:00
MeiMei
a2d3d22b6e オブジェクトストレージでS3のvirtual-host形式のサポートなど (#6148)
* オブジェクトストレージでS3のvirtual-host形式のサポートなど

* 表記揺れ

* more simply

* S3ならばs3ForcePathStyleしない
2020-03-14 11:33:19 +09:00
tamaina
1ad8603cc2 fix gif badge (#6153) 2020-03-14 11:31:03 +09:00
syuilo
aeaf535ea2 Update test 2020-03-07 11:25:39 +09:00
syuilo
917726fecc wip #6140 2020-03-07 11:23:31 +09:00
syuilo
49a5b4eb14 Refactor: Better arg name 2020-03-07 09:56:13 +09:00
syuilo
8946f3ea18 Add test 2020-03-07 01:10:13 +09:00
syuilo
1947835c51 Resolve #6137 2020-03-07 01:04:36 +09:00
syuilo
c7c537c8b8 Refactor 2020-03-07 00:35:00 +09:00
Oni-Men
4e2f954683 note overflow: hidden (#6138) 2020-03-07 00:31:48 +09:00
syuilo
abb0184329 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-03-07 00:29:27 +09:00
syuilo
ee483ecfd3 Refactor 2020-03-07 00:29:09 +09:00
fuyu
99384b4c22 チャートログの取得範囲の修正 (#5923) 2020-03-07 00:28:21 +09:00
syuilo
65503bc68d Update commands 2020-03-07 00:12:23 +09:00
syuilo
ec6aadb5ce Migrate deprecated mocha configuration 2020-03-07 00:00:12 +09:00
syuilo
5f642886d9 chore: Update commands 2020-03-06 23:58:27 +09:00
MeiMei
4c26e3c54d note_reaction.reaction は 130文字に (#6105) 2020-03-06 23:09:06 +09:00
fuyu
3ca3712bae ダークテーマ利用時にセレクトが使いにくくなる問題を修正 (#6117) 2020-03-06 22:54:23 +09:00
Oni-Men
a471e4b783 MFMをテキストに戻す (#6131)
* Disable Nyaize in quote

* mfmを文字列に戻す、nyaizeにmfmを使用

* Revert "Disable Nyaize in quote"

This reverts commit 1b238905a5.

* refactor

* use return type as string
2020-03-06 22:51:50 +09:00
MeiMei
20ac7e62e9 チャートInsert時にロックをかけるように (#6100)
* chart lock

* fix
2020-03-06 22:33:54 +09:00
Acid Chicken (硫酸鶏)
3e61aa0835 Merge pull request #6130 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-03-04 21:08:05 +09:00
rinsuki
c18f6fde80 lintをGitHub Actions でするように (#6101)
* package.json の lint スクリプトを修正

* lint アクションを追加

* yarn lint --fix

* 手動修正
2020-03-04 11:45:33 +09:00
Acid Chicken (硫酸鶏)
8b9397a0ce Update README.md [AUTOGEN] 2020-03-04 04:40:12 +09:00
Acid Chicken (硫酸鶏)
678ff17d0f Merge pull request #6121 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-03-04 04:39:40 +09:00
Acid Chicken (硫酸鶏)
ea2016c208 Update README.md [AUTOGEN] 2020-03-02 01:58:06 +09:00
Acid Chicken (硫酸鶏)
b5bdf266d3 Merge pull request #6109 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-03-01 23:54:37 +09:00
syuilo
2309680c38 Refactor 2020-02-29 16:38:07 +09:00
Acid Chicken (硫酸鶏)
e99bf569c5 Update README.md [AUTOGEN] 2020-02-29 11:05:09 +09:00
tamaina
d3fd0f810a ボリュームを0にしてもサウンドが鳴っていることになっていたのを修正 (#6098)
* Update init.ts

* parseFloat
2020-02-27 16:32:10 +09:00
syuilo
484dc9b08a Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-27 15:13:50 +09:00
syuilo
3bd827d7da Update google.vue 2020-02-27 15:13:46 +09:00
rinsuki
d425c72134 GitHub Actions のテストで postgres がコケてるのを修正 (#6089)
* github actionsのfail原因調査用

* fix

* fix

* fux

* remove tihs branch from CI target

* ログ表示削除

* fix
2020-02-27 10:08:31 +09:00
syuilo
969cd16638 Resolve #6091 2020-02-27 07:04:28 +09:00
Oni-Men
569be15705 同じホットキーが連続で発動しないように (#6082)
* add cooldown to hotkey

* remove blank

* use repeat flag

* format

* Add Repeatable option to Hotkey

* Boolean型のみに

* console.log消すの忘れてた
2020-02-26 17:22:43 +09:00
MeiMei
03f54c5b02 リアクション絵文字設定をいい感じに (#6074)
* リアクション絵文字設定をいい感じに

* みじかく
2020-02-26 16:48:23 +09:00
MeiMei
06ddc8ec50 Fix: mainStreamのミュート情報が再接続まで反映されない (#6072) 2020-02-26 08:03:23 +09:00
MeiMei
1528935008 GitHub Actionsでテストが動かなくなっているのを修正 (#6088)
* CI test

* Add pg healthcheck

* postgres:10.8

* 試しにhealthcheckなしに

* postgres:10

* Revert "試しにhealthcheckなしに"

This reverts commit 4a7ba19ea9.

* は?

* postgres:10.8-alpine

* postgres:10.11-alpine

* テスト用ブランチ指定を削除
2020-02-26 07:57:24 +09:00
syuilo
7121bdef6b Refactor 2020-02-26 07:56:32 +09:00
Oni-Men
f6c376f20d 同じノートを何回リノートしても一回として数えるように (#6086)
* 同じノートを何回リノートしても一回として数えるように

* Update count-same-renotes.ts

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-02-26 07:54:35 +09:00
Acid Chicken (硫酸鶏)
241769d6fc Merge pull request #6084 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-25 17:47:38 +09:00
Acid Chicken (硫酸鶏)
7727651871 Update README.md [AUTOGEN] 2020-02-25 17:46:07 +09:00
Acid Chicken (硫酸鶏)
ce331826ac Merge pull request #6078 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-24 09:52:25 +09:00
Acid Chicken (硫酸鶏)
ae92c52d61 Update README.md [AUTOGEN] 2020-02-24 09:28:06 +09:00
syuilo
54aef5fe6f Update CHANGELOG.md 2020-02-23 03:11:51 +09:00
syuilo
d22148b418 12.21.0 2020-02-23 02:53:44 +09:00
syuilo
5dc75c9cea Fix #6029 2020-02-23 02:34:54 +09:00
syuilo
200e82decb Fix #6063 2020-02-23 02:28:07 +09:00
syuilo
50359dbaf4 Resolve #6053 2020-02-22 06:57:54 +09:00
syuilo
7165f21a62 Fix style 2020-02-22 06:54:35 +09:00
syuilo
8aab828c65 Better featured injection 2020-02-22 06:49:12 +09:00
syuilo
c9f8c12f5b 🍕 2020-02-22 06:43:46 +09:00
syuilo
a347f8fa49 🎨 2020-02-22 06:40:48 +09:00
syuilo
2d76bdd0f8 Fix bug 2020-02-22 06:36:15 +09:00
syuilo
c5cdd56edb 🎨 2020-02-22 03:51:31 +09:00
Acid Chicken (硫酸鶏)
6901ab39ed Merge pull request #6058 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-22 03:02:58 +09:00
syuilo
b851b7f431 12.20.0 2020-02-22 02:38:37 +09:00
syuilo
ccaa99115c New Crowdin translations (#6047)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kannada)
2020-02-22 02:38:11 +09:00
syuilo
813de15e85 🎨 2020-02-22 02:30:41 +09:00
syuilo
fa33181fa9 Update particle.vue 2020-02-22 02:26:01 +09:00
syuilo
d4324dc0cb Reaction particle 2020-02-22 01:20:58 +09:00
tamaina
ccc27bcc14 Fix #6057 (#6061) 2020-02-22 01:03:50 +09:00
Acid Chicken (硫酸鶏)
014c1673c6 Update README.md [AUTOGEN] 2020-02-21 22:39:06 +09:00
Acid Chicken (硫酸鶏)
3a3319ff52 Merge pull request #6056 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-21 20:11:43 +09:00
Acid Chicken (硫酸鶏)
5b54ec8fb5 Update README.md [AUTOGEN] 2020-02-21 18:37:07 +09:00
Oni-Men
e690556286 patch #6039 (#6052) 2020-02-21 17:16:51 +09:00
Acid Chicken (硫酸鶏)
660956917f Merge pull request #6034 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-21 11:18:52 +09:00
syuilo
3a5201747b 🎨 2020-02-21 09:17:33 +09:00
syuilo
b338e8a83f 🎨 2020-02-21 09:11:35 +09:00
syuilo
5584d56b6a Clean up 2020-02-21 08:36:18 +09:00
syuilo
c925498120 Improve usability 2020-02-21 07:21:27 +09:00
syuilo
75615cf503 Update style.scss 2020-02-21 07:11:25 +09:00
syuilo
39f708b0fc 複数タブで開いてるときに動作がおかしい問題を修正 2020-02-21 03:51:41 +09:00
syuilo
ac32077221 12.19.0 2020-02-21 00:36:17 +09:00
syuilo
a5902acacd New Crowdin translations (#6037)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)
2020-02-21 00:36:03 +09:00
syuilo
c7c08b7511 Resolve #6043 2020-02-21 00:28:45 +09:00
syuilo
7de915d47b Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-20 23:27:48 +09:00
syuilo
9107547501 Update CHANGELOG.md 2020-02-20 23:27:32 +09:00
syuilo
95dc76ca19 Fix comments 2020-02-20 23:26:35 +09:00
syuilo
49c2a9b372 ボリュームが0のときサウンドを鳴らさないように 2020-02-20 23:17:17 +09:00
syuilo
b378cabfc7 Fix bug 2020-02-20 23:11:09 +09:00
syuilo
4263dbef31 Fix #6026 2020-02-20 23:07:20 +09:00
syuilo
32fc6ae2eb Fix bug 2020-02-20 23:07:03 +09:00
syuilo
238cb0077f Fix bug 2020-02-20 23:02:55 +09:00
syuilo
f5a06b6494 Fix #6036 2020-02-20 22:54:26 +09:00
Acid Chicken (硫酸鶏)
2c01329085 Update README.md [AUTOGEN] 2020-02-20 14:10:06 +09:00
syuilo
502de89ab1 12.18.1 2020-02-20 13:41:28 +09:00
syuilo
128de6750c New translations ja-JP.yml (Spanish) (#6027) 2020-02-20 13:41:16 +09:00
syuilo
e59e2d9f0b Resolve #6028 2020-02-20 13:38:40 +09:00
syuilo
2504b8391b Better validation 2020-02-20 13:33:41 +09:00
syuilo
330ea7d210 12.18.0 2020-02-20 07:30:43 +09:00
syuilo
1edd173a29 Add sounds 2020-02-20 07:29:34 +09:00
syuilo
98d873a7f9 Update search-by-tag.ts 2020-02-20 07:19:27 +09:00
syuilo
09175b84df Fix #6016 2020-02-20 07:18:40 +09:00
syuilo
177e19632a Fix #6016 2020-02-20 07:18:16 +09:00
syuilo
8e6207f3e9 Remove header transition 2020-02-20 06:42:20 +09:00
syuilo
ff3a97f6cf Fix #5943 2020-02-20 06:38:19 +09:00
syuilo
b8e155ab40 🎨 2020-02-20 06:08:54 +09:00
syuilo
b8e7df198d Improve sound 2020-02-20 06:08:49 +09:00
syuilo
34311e3181 12.17.0 2020-02-20 03:55:38 +09:00
syuilo
46115d3f04 New Crowdin translations (#5997)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

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

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

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

* New translations ja-JP.yml (French)

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

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Kannada)
2020-02-20 03:52:23 +09:00
MeiMei
c1d25d2394 切断時ダイアログのタイミングの変更など (#6014)
* 再接続時インジケーター

* Update ja-JP.yml

* Update stream-indicator.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-02-20 03:42:35 +09:00
syuilo
880cea5a56 Better sfx 2020-02-20 03:14:17 +09:00
syuilo
e7205d9cc2 サウンド設定など 2020-02-20 02:40:53 +09:00
syuilo
f456feb3ff media-listのgridの高さがsub-note-detailsのdetailsの中だと287pxになってしまっていたのを修正 (#5951)
* fix files grid height

* missing colon

* ✌️

* ✌️

* fix

* remove unused event listener
2020-02-20 01:24:45 +09:00
MeiMei
3f83beedb7 Fix #5943 (#6023) 2020-02-20 00:38:26 +09:00
MeiMei
e6c9b1d9bd LegacyReaction変換にstarを追加 (#6013) 2020-02-19 22:06:54 +09:00
syuilo
b46114f4fa Update index.home.vue 2020-02-19 17:59:28 +09:00
syuilo
8d77e2ba22 Fix bug 2020-02-19 17:55:55 +09:00
tamaina
cb3900921f remove unused event listener 2020-02-19 07:49:53 +09:00
syuilo
ae2021583d 🎨 2020-02-19 07:00:44 +09:00
syuilo
36cd88e6b7 12.16.0 2020-02-19 06:42:46 +09:00
syuilo
517b0908da 🎨 2020-02-19 06:41:30 +09:00
syuilo
b23b3e4d21 Fix #5984 2020-02-19 06:36:50 +09:00
syuilo
883fc5dde0 Improve notification 2020-02-19 06:26:29 +09:00
syuilo
9d044329f6 🎨 2020-02-19 06:17:41 +09:00
syuilo
d1e9e74cb8 Resolve #5978 2020-02-19 06:16:49 +09:00
tamaina
f799375635 fix 2020-02-15 21:31:56 +09:00
tamaina
65704bbf01 ✌️ 2020-02-15 21:20:01 +09:00
tamaina
9cb3882efa ✌️ 2020-02-15 20:18:37 +09:00
tamaina
a0833ca691 missing colon 2020-02-15 19:34:30 +09:00
tamaina
a4f197f608 fix files grid height 2020-02-15 19:33:12 +09:00
167 changed files with 2253 additions and 1105 deletions

View File

@@ -88,7 +88,9 @@ redis:
#elasticsearch:
# host: localhost
# port: 9200
# pass: null
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────

View File

@@ -21,6 +21,7 @@ jobs:
- 5432:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:alpine
ports:
@@ -40,3 +41,13 @@ jobs:
run: yarn build
- name: Test
run: yarn test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12.x
- run: yarn install
- run: yarn lint

4
.mocharc.json Normal file
View File

@@ -0,0 +1,4 @@
{
"timeout": 30000,
"slow": 1000
}

View File

@@ -1,6 +1,77 @@
ChangeLog
=========
12.21.0 (2020/02/23)
-------------------
### ✨Improvements
* タイムラインに挿入されるおすすめノートに自分がリアクションしたものは含めないように
* ノートのメニューに詳細ページへのリンクを追加
* UIの調整
### 🐛Fixes
* チャットで自分の送信したURLが視認しにくい問題を修正
* ノートの内のインラインコードが横に突き抜ける問題を修正
* (新しいノートがあります)表示中にタイムラインを切り替えると、表示が消えなくなってしまう問題を修正
* 引用RNフォームを開いた時だけ、textareaにフォーカスが当たっていない問題を修正
12.20.0 (2020/02/22)
-------------------
### ✨Improvements
* UIの調整
### 🐛Fixes
* 複数タブで開いてるときに動作がおかしい問題を修正
* メディア付きートの詳細表示をした後TLに戻るとートがバグる問題を修正
12.19.0 (2020/02/21)
-------------------
### ✨Improvements
* アンテナで除外キーワードを設定できるように
### 🐛Fixes
* ハッシュタグをもっと見るできないのを修正
* 無効になっているタイムラインでも使用できるかのように表示される問題を修正
* バックグラウンドで受信したノートの画像が表示されない問題を修正
* サインインフォームが表示されない場所がある問題を修正
* ボリュームが0のときサウンドを鳴らさないように
12.18.1 (2020/02/20)
-------------------
### 🐛Fixes
* タイムラインのハイライトに自分のノートは含めないように
* ハッシュタグの集計に関する問題を修正
12.18.0 (2020/02/20)
-------------------
### ✨Improvements
* 効果音設定を強化
* UIの調整
### 🐛Fixes
* ユーザープレビューが稀に画面上から消えなくなってしまう問題を修正
* ハッシュタグ検索が遅い問題を修正
12.17.0 (2020/02/20)
-------------------
### ✨Improvements
* 効果音を実装
* 切断時ダイアログを控えめに
### 🐛Fixes
* 新しいノートの通知が崩れる問題を修正
* LegacyReaction変換にstarを追加
* ユーザープレビューが稀に画面上から消えなくなってしまう問題を修正
* media-listのgridの高さがsub-note-detailsのdetailsの中だと287pxになってしまっていたのを修正
12.16.0 (2020/02/19)
-------------------
### ✨Improvements
* 通知一覧をポップアップではなくページで表示できるように
* 返信、引用、メンションの通知を直接ノートとして表示するように
### 🐛Fixes
* v12以前のリアクションが表示されない問題を修正
12.15.0 (2020/02/19)
-------------------
### ✨Improvements

View File

@@ -110,7 +110,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24430516/b1964ac5b9f746d2a12ff53dbc9aa40a/1.jpg?token-time=2145916800&token-hash=bmEiMGYpp3bS7hCCbymjGGsHBZM3AXuBOFO3Kro37PU%3D" alt="Eduardo Quiros" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/14215107/1cbe1912c26143919fa0faca16f12ce1/3.png?token-time=2145916800&token-hash=Zq1TCK2tdY7xudEm_aV70bc_wxmol6pNj3ZWbpFUNbI%3D" alt="Nesakko" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=20832595">Roujo</a></td>
<td><a href="https://www.patreon.com/user?u=27956229">Oliver Maximilian Seidel</a></td>
@@ -119,9 +118,9 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
<td><a href="https://www.patreon.com/user?u=24430516">Eduardo Quiros</a></td>
<td><a href="https://www.patreon.com/Nesakko">Nesakko</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
</tr></table>
<table><tr>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/3075183/c2ae575c604e420297f000ccc396e395/1.jpeg?token-time=2145916800&token-hash=O9qmPtpo6wWb0OuvnkEekhk_1WO2MTdytLr7ZgsAr80%3D" alt="Liaizon Wakest" 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/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y" width="100"></td>
@@ -130,9 +129,8 @@ 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/5788159/af42076ab3354bb49803cfba65f94bee/1.jpg?token-time=2145916800&token-hash=iSaxp_Yr2-ZiU2YVi9rcpZZj9mj3UvNSMrZr4CU4qtA%3D" alt="mewl hayabusa" 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/28779508/3cd4cb7f017f4ee0864341e3464d42f9/1.png?token-time=2145916800&token-hash=eGQtR15be44kgvh8fw2Jx8Db4Bv15YBp2ldxh0EKRxA%3D" alt="S Y" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
<td><a href="https://www.patreon.com/wakest">Liaizon Wakest</a></td>
<td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td>
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y</a></td>
@@ -141,56 +139,64 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<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/hs_sh_net">mewl hayabusa</a></td>
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28779508/3cd4cb7f017f4ee0864341e3464d42f9/1.png?token-time=2145916800&token-hash=eGQtR15be44kgvh8fw2Jx8Db4Bv15YBp2ldxh0EKRxA%3D" alt="S Y" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" 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://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26340354/08834cf767b3449e93098ef73a434e2f/2.png?token-time=2145916800&token-hash=nyM8DnKRL8hR47HQ619mUzsqVRpkWZjgtgBU9RY15Uc%3D" alt="totokoro" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</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=26340354">totokoro</a></td>
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
<td><a href="https://www.patreon.com/user?u=5827393">motcha</a></td>
<td><a href="https://www.patreon.com/user?u=20494440">axtuki1</a></td>
<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/aqz">aqz tamaina</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28295158/cd2451bfb94a449dbf705ef4718cd355/2.jpeg?token-time=2145916800&token-hash=MRv3BxufHPuCyiBSxU5UYmLGvD6YZlhtSFRfMWg2k4U%3D" alt="012" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco" 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/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.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24641572/b4fd175424814f15b0ca9178d2d2d2e4/1.png?token-time=2145916800&token-hash=e2fyqdbuJbpCckHcwux7rbuW6OPkKdERcus0u2wIEWU%3D" alt="uroco @99" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
<td><a href="https://www.patreon.com/user?u=28295158">012</a></td>
<td><a href="https://www.patreon.com/nijimiss">nafuchoco</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/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/user?u=24641572">uroco @99</a></td>
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
</tr></table>
<table><tr>
<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/9481273/7fa89168e72943859c3d3c96e424ed31/4.jpeg?token-time=2145916800&token-hash=5w1QV1qXe-NdWbdFmp1H7O_-QBsSiV0haumk3XTHIEg%3D" alt="Efertone" 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/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/efertone">Efertone</a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Wed, 05 Feb 2020 00:42:12 UTC
**Last updated:** Tue, 17 Mar 2020 18:57:08 UTC
<!-- PATREON_END -->
[backer-url]: #backers

View File

@@ -26,14 +26,86 @@ uploading: "Upload läuft"
save: "Speichern"
users: "Benutzer"
addUser: "Benutzer hinzufügen"
favorite: "Favoriten"
favorites: "Favoriten"
unfavorite: "Aus Favoriten entfernen"
pin: "Anheften"
unpin: "Lösen"
copyContent: "Inhalt kopieren"
copyLink: "Link kopieren"
delete: "Löschen"
addToList: "Zur Liste hinzufügen"
sendMessage: "Nachricht senden"
copyUsername: "Benutzernamen kopieren"
reply: "Antworten"
loadMore: "Zeige mehr"
youGotNewFollower: "Sie haben einen neuen Follower"
receiveFollowRequest: "Follow Request erhalten."
followRequestAccepted: "FollowRequestAkzeptiert"
mentions: "Erwähnungen"
directNotes: "Direktnachrichten"
importAndExport: "Importieren und Exportieren"
import: "Importieren"
export: "Exportieren"
files: "Dateien"
download: "Download"
lists: "Listen"
noLists: "Keine Liste!"
note: "Noten"
following: "Folgen"
followers: "Folgende"
manageLists: "Liste verwalten"
error: "Ein Problem ist aufgetreten"
retry: "Wiederholen"
privacy: "Privatsphäre"
defaultNoteVisibility: "Die Standardsichtbarkeit"
follow: "Folgen"
followRequest: "Follower-Anfragen"
followRequests: "Follower-Anfragen"
unfollow: "Nicht mehr folgen"
followRequestPending: "Ausstehend"
clickToShow: "Klicke zum den Inhalt anzusehen"
sensitive: "Dieser Inhalt ist NSFW"
add: "Hinzufügen"
reaction: "Reaktionen"
selectUser: "Benutzer wählen"
instances: "Instanz"
mutedUsers: "Stummgestellte Benutzer"
blockedUsers: "Blockierte Benutzer"
noUsers: "Keine Benutzer"
remove: "Löschen"
nsfw: "Dieser Inhalt ist NSFW"
userList: "Listen"
_sfx:
notification: "Benachrichtigungen"
_widgets:
notifications: "Benachrichtigungen"
timeline: "Zeitleiste"
_cw:
show: "Zeige mehr"
_visibility:
followers: "Folgende"
_profile:
username: "Benutzername"
_exportOrImport:
followingList: "Folgen"
userLists: "Listen"
_pages:
script:
categories:
list: "Listen"
blocks:
_join:
arg1: "Listen"
_randomPick:
arg1: "Listen"
_dailyRandomPick:
arg1: "Listen"
_seedRandomPick:
arg2: "Listen"
_pick:
arg1: "Listen"
_listLen:
arg1: "Listen"
types:
array: "Listen"

View File

@@ -239,6 +239,8 @@ avatar: "Avatar"
banner: "Banner"
nsfw: "NSFW"
disconnectedFromServer: "Connection to the server was inturrupted"
reload: "Refresh"
doNothing: "Ignore"
reloadConfirm: "Would you like to retry?"
watch: "Watch"
unwatch: "Undo Watch"
@@ -283,7 +285,8 @@ antennas: "Antennas"
manageAntennas: "Manage Antennas"
name: "Name"
antennaSource: "Antenna source"
antennaKeywords: "Antenna keywords"
antennaKeywords: "Keywords to receive"
antennaExcludeKeywords: "Keywords to exclude"
antennaKeywordsDescription: "Separate with spaces for AND condition. Separate with line breaks for OR."
notifyAntenna: "Notify newer notes"
withFileAntenna: "Filter only notes with file attached"
@@ -419,6 +422,22 @@ hideThisNote: "Hide this note"
showFeaturedNotesInTimeline: "Show Featured notes in Timeline"
objectStorage: "Object Storage"
useObjectStorage: "Use object storage"
serverLogs: "Server logs"
deleteAll: "Delete all"
showFixedPostForm: "Display the posting form at the top of the timeline"
newNoteRecived: "You've got a new note"
useNotificationsPopup: "Display notification list in popup"
sounds: "Sounds"
listen: "Listen"
none: "None"
volume: "Volume"
_sfx:
note: "New note"
noteMy: "My note"
notification: "Notifications"
chat: "Messaging"
chatBg: "Messaging (Background)"
antenna: "Antenna Reception"
_ago:
unknown: "Unknown"
future: "Future"

View File

@@ -239,6 +239,8 @@ avatar: "Avatar"
banner: "Banner"
nsfw: "Marcado como sensible"
disconnectedFromServer: "Desconectado del servidor"
reload: "Recargar"
doNothing: "No hacer nada"
reloadConfirm: "¿Desea recargar?"
watch: "Ver"
unwatch: "Dejar de ver"
@@ -283,7 +285,8 @@ antennas: "Antenas"
manageAntennas: "Administrar antenas"
name: "Nombre"
antennaSource: "Origen de la antena"
antennaKeywords: "Palabras clave de la antena"
antennaKeywords: "Palabras clave para recibir"
antennaExcludeKeywords: "Palabras clave para excluir"
antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR"
notifyAntenna: "Notificar nueva nota"
withFileAntenna: "Sólo notas con archivos adjuntados"
@@ -419,6 +422,22 @@ hideThisNote: "Ocultar esta nota"
showFeaturedNotesInTimeline: "Mostrar notas destacadas en la línea de tiempo"
objectStorage: "Almacenamiento de objetos"
useObjectStorage: "Usar almacenamiento de objetos"
serverLogs: "Registros del servidor"
deleteAll: "Eliminar todos"
showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo"
newNoteRecived: "Tienes una nota nuevo"
useNotificationsPopup: "Mostrar lista de notificaciones en ventana emergente"
sounds: "Sonidos"
listen: "Escuchar"
none: "Ninguna"
volume: "Volumen"
_sfx:
note: "Notas"
noteMy: "Nota (a mí mismo)"
notification: "Notificaciones"
chat: "Chat"
chatBg: "Chat (Fondo)"
antenna: "Antena receptora"
_ago:
unknown: "Desconocido"
future: "Futuro"

View File

@@ -239,6 +239,8 @@ avatar: "Avatar"
banner: "Bannière"
nsfw: "Contenu sensible"
disconnectedFromServer: "Déconnecté du serveur"
reload: "Rafraîchir"
doNothing: "Ignorer"
reloadConfirm: "Voulez-vous recharger?"
watch: "Surveiller"
unwatch: "Ne plus surveiller"
@@ -283,7 +285,8 @@ antennas: "Antenne"
manageAntennas: "Gestion d'antenne"
name: "Nom"
antennaSource: "Recevoir la source"
antennaKeywords: "Mots clés entrants"
antennaKeywords: "Mots clés à recevoir"
antennaExcludeKeywords: "Mots clés à exclure"
antennaKeywordsDescription: "Lorsqu'il est séparé par un espace, il devient une spécification ET, et lorsqu'il est séparé par un saut de ligne, il devient une spécification OU."
notifyAntenna: "Notifier les nouvelles notes"
withFileAntenna: "Notes uniquement avec fichiers joints"
@@ -419,6 +422,22 @@ hideThisNote: "Masquer cette note"
showFeaturedNotesInTimeline: "Afficher les notes en vedette dans Fil d'actualité"
objectStorage: "Stockage d'objets"
useObjectStorage: "Utiliser le stockage d'objets"
serverLogs: "Journaux serveur"
deleteAll: "Supprimer tout"
showFixedPostForm: "Afficher le formulaire en haut du fil d'actualité"
newNoteRecived: "Vous avez un nouveau note"
useNotificationsPopup: "Afficher la liste des notifications dans une fenêtre contextuelle"
sounds: "Sons"
listen: "Écouter"
none: "Rien"
volume: "Volume"
_sfx:
note: "Nouvelle note"
noteMy: "Ma note"
notification: "Notifications"
chat: "Discuter"
chatBg: "Discuter (De fond)"
antenna: "Réception d'antenne"
_ago:
unknown: "Inconnu"
future: "Futur"
@@ -438,6 +457,16 @@ _time:
_tutorial:
title: "Comment utiliser Misskey"
step1_1: "Bienvenue,"
step1_2: "Cette page est appelée \"timeline\". Elle montre les \"notes\" des personnes que vous \"suivez\" dans l'ordre chronologique."
step1_3: "Vous n'avez pas encore posté de notes ou ne suivez personne, vous ne devriez donc rien voir dans la chronologie."
step2_1: "Finissons de créer votre profil avant d'écrire une note ou de suivre quelqu'un."
step2_2: "En fournissant quelques informations sur vous, il sera plus facile pour les autres de vous suivre."
step3_1: "Vous avez fini de créer votre profil ?"
step3_2: "Létape suivante consiste à créer une note. Vous pouvez commencer en cliquant sur licône crayon sur lécran."
step3_3: "Remplissez le cadran et cliquez sur le bouton en haut à droite pour envoyer."
step3_4: "Vous n'avez rien à dire ? Essayez de dire \"J'ai commencé à utiliser Misskey\"."
step4_1: "Avez-vous posté votre première notes ?"
step4_2: "Votre première note est maintenant affichée sur votre timeline."
_2fa:
alreadyRegistered: "Cette étape à déjà été complétée"
registerDevice: "Sinscrire l'appareil"

View File

@@ -84,7 +84,7 @@ clickToShow: "クリックして表示"
sensitive: "閲覧注意"
add: "追加"
reaction: "リアクション"
reactionSettingDescription: "リアクションピッカーに表示するリアクションを改行で区切って設定します。"
reactionSettingDescription: "リアクションピッカーに表示するリアクションを設定します。"
rememberNoteVisibility: "公開範囲を記憶する"
renameFile: "ファイル名を変更"
attachCancel: "添付取り消し"
@@ -168,6 +168,7 @@ intro: "Misskeyのインストールが完了しました管理者アカウ
done: "完了"
processing: "処理中"
preview: "プレビュー"
default: "デフォルト"
noCustomEmojis: "絵文字はありません"
customEmojisOfRemote: "リモートの絵文字"
noJobs: "ジョブはありません"
@@ -239,6 +240,8 @@ avatar: "アイコン"
banner: "バナー"
nsfw: "閲覧注意"
disconnectedFromServer: "サーバーから切断されました"
reload: "リロード"
doNothing: "なにもしない"
reloadConfirm: "リロードしますか?"
watch: "ウォッチ"
unwatch: "ウォッチ解除"
@@ -284,6 +287,7 @@ manageAntennas: "アンテナの管理"
name: "名前"
antennaSource: "受信ソース"
antennaKeywords: "受信キーワード"
antennaExcludeKeywords: "除外キーワード"
antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
notifyAntenna: "新しいノートを通知する"
withFileAntenna: "ファイルが添付されたノートのみ"
@@ -419,10 +423,36 @@ hideThisNote: "このノートを非表示"
showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示する"
objectStorage: "オブジェクトストレージ"
useObjectStorage: "オブジェクトストレージを使用"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "参照に使用するURL。CDNやProxyを使用している場合はそのURL、S3: 'https://<bucket>.s3.amazonaws.com'、GCS等: 'https://storage.googleapis.com/<bucket>'。"
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "使用サービスのbucket名を指定してください。"
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "このprefixのディレクトリ下に格納されます。"
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "S3の場合は空、それ以外の場合は各サービスのendpointを指定してください。'<host>'または'<host>:<port>'のように指定します。"
objectStorageRegion: "Region"
objectStorageRegionDesc: "'xx-east-1'のようなregionを指定してください。使用サービスにregionの概念がない場合は、空または'us-east-1'にしてください。"
objectStorageUseSSL: "SSLを使用する"
objectStorageUseSSLDesc: "API接続にhttpsを使用しない場合はオフにしてください"
serverLogs: "サーバーログ"
deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
newNoteRecived: "新しいノートがあります"
sounds: "サウンド"
listen: "聴く"
none: "なし"
volume: "音量"
details: "詳細"
chooseEmoji: "絵文字を選択"
_sfx:
note: "ノート"
noteMy: "ノート(自分)"
notification: "通知"
chat: "チャット"
chatBg: "チャット(バックグラウンド)"
antenna: "アンテナ受信"
_ago:
unknown: "謎"

View File

@@ -109,6 +109,8 @@ aboutMisskey: "Misskeyってなんや"
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
close: "さいなら"
joinedGroups: "参加しとるグループ"
_sfx:
notification: "通知"
_ago:
unknown: "謎"
future: "未来"

View File

@@ -26,9 +26,40 @@ signup: "ನೋಂದಣಿ"
uploading: "ಅಪ್‌ಲೋಡಾಗುತ್ತಿದೆ"
save: "ಉಳಿಸಿ"
users: "ಬಳಕೆದಾರ"
addUser: "ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"
favorite: "ಮೆಚ್ಚಿನ"
favorites: "ಮೆಚ್ಚಿನವುಗಳು"
unfavorite: "ಮೆಚ್ಚುಗೆ ಅಳಿಸು"
pin: "ಪ್ರೊಫ಼ೈಲಿಗೆ ಅಂಟಿಸು"
unpin: "ಪ್ರೊಫ಼ೈಲಿಂದ ಅಂಟುತೆಗೆ"
copyContent: "ವಿಷಯವನ್ನು ನಕಲಿಸು"
copyLink: "ಲಿಂಕನ್ನು ನಕಲಿಸು"
delete: "ಅಳಿಸು"
addToList: "ಪಟ್ಟಿಗೆ ಸೇರಿಸು"
sendMessage: "ಸಂದೇಶ ಕಳುಹಿಸು"
copyUsername: "ಬಳಕೆಹೆಸರು ನಕಲಿಸು"
reply: "ಉತ್ತರಿಸು"
loadMore: "ಇನ್ನಷ್ಟು ನೋಡು"
youGotNewFollower: "ಹಿಂಬಾಲಿಸಿದರು"
receiveFollowRequest: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಬಂದಿದೆ"
followRequestAccepted: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಸ್ವೀಕರಿಸಲಾಯಿತು"
mentions: "ಹೆಸರಿಸಿದ"
directNotes: "ನೇರ ಟಿಪ್ಪಣಿಗಳು"
importAndExport: "ಆಮದು/ರಫ್ತು"
import: "ಆಮದು"
export: "ರಫ್ತು"
files: "ಕಡತಗಳು"
download: "ಜಾಲದಿಂದಿಳಿಸು"
driveFileDeleteConfirm: "\"{name}\" ಕಡತವನ್ನು ಅಳಿಸಲು ನೀವು ಬಯಸುವಿರಾ? ಈ ನೋಡಿರಿ ಲಗತ್ತಿಸಲಾದ ಟಿಪ್ಪಣಿ ಸಹ ಕಣ್ಮರೆಯಾಗುತ್ತದೆ."
unfollowConfirm: "{name}ಅನ್ನು ಹಿಂಬಾಲಿಸದಿರುವುದೇ?"
instances: "ನಿದರ್ಶನ"
remove: "ಅಳಿಸು"
_sfx:
notification: "ಅಧಿಸೂಚನೆಗಳು"
_widgets:
notifications: "ಅಧಿಸೂಚನೆಗಳು"
timeline: "ಸಮಯಸಾಲು"
_cw:
show: "ಇನ್ನಷ್ಟು ನೋಡು"
_profile:
username: "ಬಳಕೆಹೆಸರು"

View File

@@ -239,6 +239,8 @@ avatar: "아바타"
banner: "배너"
nsfw: "열람주의"
disconnectedFromServer: "서버와의 연결이 끊어졌습니다"
reload: "새로고침"
doNothing: "무시하기"
reloadConfirm: "새로고침 하시겠습니까?"
watch: "지켜보기"
unwatch: "지켜보기 해제"
@@ -284,6 +286,7 @@ manageAntennas: "안테나 관리"
name: "이름"
antennaSource: "받을 소스"
antennaKeywords: "받을 키워드"
antennaExcludeKeywords: "제외할 키워드"
antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
notifyAntenna: "새로운 노트를 알림"
withFileAntenna: "파일이 첨부된 노트만"
@@ -419,6 +422,22 @@ hideThisNote: "이 노트를 숨기기"
showFeaturedNotesInTimeline: "타임라인에 추천 노트를 표시"
objectStorage: "오브젝트 스토리지"
useObjectStorage: "오브젝트 스토리지를 사용"
serverLogs: "서버 로그"
deleteAll: "모두 삭제"
showFixedPostForm: "타임라인 상단에 글 작성란을 표시"
newNoteRecived: "새 노트가 있습니다"
useNotificationsPopup: "알림 목록을 팝업으로 표시"
sounds: "소리"
listen: "듣기"
none: "없음"
volume: "음량"
_sfx:
note: "새 노트"
noteMy: "내 노트"
notification: "알림"
chat: "대화"
chatBg: "대화 (백그라운드)"
antenna: "안테나 수신"
_ago:
unknown: "알 수 없음"
future: "미래"

View File

@@ -1,2 +1,36 @@
---
_lang_: "Русский язык"
search: "Поиск"
notifications: "Уведомления"
password: "Пароль"
ok: "Окей"
cancel: "Отмена"
instance: "Экземпляр"
settings: "Настройки"
profile: "Профиль"
timeline: "Лента"
login: "Войти"
logout: "Выйти"
signup: "Регистрация"
save: "Сохранить"
favorite: "Избранное"
favorites: "Избранное"
unfavorite: "Удалить из избранных"
pin: "Закрепить"
unpin: "Открепить"
copyLink: "Скопировать ссылку"
delete: "Удалить"
addToList: "Добавить в список"
reply: "Ответить"
loadMore: "Показать еще"
importAndExport: "Импорт / Экспорт"
files: "Файл"
instances: "Экземпляр"
remove: "Удалить"
_sfx:
notification: "Уведомления"
_widgets:
notifications: "Уведомления"
timeline: "Лента"
_cw:
show: "Показать еще"

View File

@@ -315,8 +315,10 @@ moderator: "版主"
nUsersMentioned: "{n} 被提到"
securityKey: "安全密钥"
securityKeyName: "密钥名称"
registerSecurityKey: "注册安全密钥"
lastUsed: "最后使用:"
unregister: "删除账户"
passwordLessLogin: "无密码登录"
resetPassword: "重置密码"
newPasswordIs: "新的密码是「{password}」"
post: "投稿"
@@ -327,10 +329,12 @@ autoNoteWatchDescription: "让您能够收到关于「反应」和回复其他
reduceUiAnimation: "减少UI动画"
share: "分享"
notFound: "未找到"
notFoundDescription: "没有与指定URL对应的页面。"
uploadFolder: "默认上传文件夹"
cacheClear: "清空缓存"
markAsReadAllNotifications: "将所有通知标为已读"
markAsReadAllUnreadNotes: "将所有帖子标记为已读"
markAsReadAllTalkMessages: "将所有聊天标记为已读"
help: "帮助"
inputMessageHere: "在此键入信息"
close: "关闭"
@@ -343,6 +347,8 @@ invites: "邀请"
groupName: "群组名"
members: "成员"
transfer: "转让"
messagingWithUser: "与用户聊天"
messagingWithGroup: "与群组聊天"
title: "标题"
text: "文本"
enable: "启用"
@@ -353,6 +359,7 @@ inviteToGroup: "群组邀请"
maxNoteTextLength: "帖子的字数限制"
quoteAttached: "已引用"
quoteQuestion: "是否将其作为引用附上?"
noMessagesYet: "现在没有新的聊天"
newMessageExists: "新信息"
onlyOneFileCanBeAttached: "只能添加一个附件"
signinRequired: "请先登录"
@@ -368,6 +375,8 @@ normalPassword: "密码强度:中等"
strongPassword: "密码强度:强"
passwordMatched: "密码一致"
passwordNotMatched: "密码不一致"
signinWith: "以{x}登录"
tapSecurityKey: "点击安全密钥"
or: "或者"
uiLanguage: "显示语言"
groupInvited: "群组招待"
@@ -380,6 +389,7 @@ disableAnimatedMfm: "禁用MFM动画"
doing: "正在进行"
category: "类别"
tags: "标签"
docSource: "文件来源"
createAccount: "注册账户"
existingAcount: "现有的帐户"
regenerate: "重新生成"
@@ -395,6 +405,21 @@ dayOverDayChanges: "与前一日相比"
accessibility: "辅助功能"
clinetSettings: "客户端设置"
accountSettings: "账户设置"
numberOfDays: "天数"
hideThisNote: "隐藏这条帖子"
showFeaturedNotesInTimeline: "在时间轴上显示热门推荐"
objectStorage: "对象存储"
useObjectStorage: "使用对象存储"
serverLogs: "服务器日志"
deleteAll: "删除全部"
showFixedPostForm: "在时间线顶部显示帖子表单"
newNoteRecived: "有新的帖子"
useNotificationsPopup: "在弹出窗口中显示通知列表"
none: "空"
_sfx:
note: "帖子"
notification: "通知"
chat: "聊天"
_ago:
unknown: "未知"
future: "未来"
@@ -457,6 +482,7 @@ _auth:
permissionAsk: "这个应用程序需要以下权限"
_antennaSources:
all: "所有帖子"
homeTimeline: "已关注用户的帖子"
_weekday:
sunday: "星期日"
monday: "星期一"
@@ -474,6 +500,7 @@ _widgets:
clock: "时钟"
rss: "RSS阅读器"
activity: "活动"
photos: "照片"
_cw:
hide: "隐藏"
show: "查看更多"
@@ -544,6 +571,7 @@ _charts:
notesIncDec: "帖子:增加/减少"
notesTotal: "帖子总数"
_instanceCharts:
requests: "请求"
users: "用户数量:增加/减少"
usersTotal: "用户总数"
notes: "帖子:增加/减少"

View File

@@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class antennaExclude1582210532752 implements MigrationInterface {
name = 'antennaExclude1582210532752'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "antenna" ADD "excludeKeywords" jsonb NOT NULL DEFAULT '[]'`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "excludeKeywords"`, undefined);
}
}

View File

@@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class noteReactionLength1582875306439 implements MigrationInterface {
name = 'noteReactionLength1582875306439'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(128)`, undefined);
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.15.0",
"version": "12.22.0",
"codename": "indigo",
"repository": {
"type": "git",
@@ -21,7 +21,7 @@
"gulp": "gulp build",
"clean": "gulp clean",
"cleanall": "gulp cleanall",
"lint": "gulp lint",
"lint": "tslint 'src/**/*.ts'",
"test": "cross-env TS_NODE_FILES=true gulp test",
"format": "gulp format"
},
@@ -250,7 +250,6 @@
"vue-meta": "2.3.2",
"vue-prism-component": "1.1.1",
"vue-router": "3.1.5",
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.4.5",
"vue-template-compiler": "2.6.11",

View File

@@ -171,6 +171,7 @@ declare module 'jsrsasign' {
public static getTLVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string): ASN1TLV;
// tslint:disable-next-line:bool-param-default
public static getVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string, removeUnusedbits?: boolean): ASN1V;
public static hextooidstr(hex: ASN1OIDV): OID;
@@ -620,9 +621,7 @@ declare module 'jsrsasign' {
public encrypt(text: string): HexString | null;
public encryptOAEP(text: string, hash?: string, hashLen?: number): HexString | null;
public encryptOAEP(text: string, hash?: (s: string) => string, hashLen?: number): HexString | null;
public encryptOAEP(text: string, hash?: string | ((s: string) => string), hashLen?: number): HexString | null;
//// RSA PRIVATE
@@ -638,9 +637,7 @@ declare module 'jsrsasign' {
public decrypt(ctext: HexString): string;
public decryptOAEP(ctext: HexString, hash?: string, hashLen?: number): string | null;
public encryptOAEP(ctext: HexString, hash?: (s: string) => string, hashLen?: number): string | null;
public decryptOAEP(ctext: HexString, hash?: string | ((s: string) => string), hashLen?: number): string | null;
//// RSA PEM

View File

@@ -50,21 +50,23 @@
<router-link class="item index" active-class="active" to="/" exact v-else>
<fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</router-link>
<button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn">
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn">
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
</router-link>
<router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn">
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked">
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
<i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i>
</router-link>
<template v-if="$store.getters.isSignedIn">
<router-link class="item notifications" active-class="active" to="/my/notifications" ref="notificationButton">
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
</router-link>
<router-link class="item" active-class="active" to="/my/messaging">
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
</router-link>
<router-link class="item" active-class="active" to="/my/drive">
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
</router-link>
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.state.i.isLocked">
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
<i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i>
</router-link>
</template>
<div class="divider"></div>
<router-link class="item" active-class="active" to="/featured">
<fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span>
@@ -87,7 +89,7 @@
<fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
<i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes)"><fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/settings">
<router-link class="item" active-class="active" to="/preferences">
<fa :icon="faCog" fixed-width/><span class="text">{{ $t('settings') }}</span>
</router-link>
</div>
@@ -143,15 +145,13 @@
<button class="button nav _button" @click="showNav = true" ref="navButton"><fa :icon="faBars"/><i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadSpecifiedNotes || $store.state.i.hasPendingReceivedFollowRequest || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement)"><fa :icon="faCircle"/></i></button>
<button v-if="$route.name === 'index'" class="button home _button" @click="top()"><fa :icon="faHome"/></button>
<button v-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button>
<button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
<button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="$router.push('/my/notifications')" ref="notificationButton2"><fa :icon="faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
<button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
</div>
<button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
<transition name="zoom-in-top">
<x-notifications v-if="notificationsOpen" class="notifications" ref="notifications"/>
</transition>
<stream-indicator v-if="$store.getters.isSignedIn"/>
</div>
</template>
@@ -164,7 +164,6 @@ import { v4 as uuid } from 'uuid';
import i18n from './i18n';
import { host, instanceName } from './config';
import { search } from './scripts/search';
import contains from './scripts/contains';
import MkToast from './components/toast.vue';
const DESKTOP_THRESHOLD = 1100;
@@ -174,7 +173,6 @@ export default Vue.extend({
components: {
XClock: () => import('./components/header-clock.vue').then(m => m.default),
XNotifications: () => import('./components/notifications.vue').then(m => m.default),
MkButton: () => import('./components/ui/button.vue').then(m => m.default),
XDraggable: () => import('vuedraggable'),
},
@@ -185,7 +183,6 @@ export default Vue.extend({
pageKey: 0,
showNav: false,
searching: false,
notificationsOpen: false,
accounts: [],
lists: [],
connection: null,
@@ -194,7 +191,6 @@ export default Vue.extend({
widgetsEditMode: false,
isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
canBack: false,
disconnectedDialog: null as Promise<void> | null,
wallpaper: localStorage.getItem('wallpaper') != null,
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer
};
@@ -218,23 +214,10 @@ export default Vue.extend({
watch:{
$route(to, from) {
this.pageKey++;
this.notificationsOpen = false;
this.showNav = false;
this.canBack = (window.history.length > 0 && !['index'].includes(to.name));
},
notificationsOpen(open) {
if (open) {
for (const el of Array.from(document.querySelectorAll('*'))) {
el.addEventListener('mousedown', this.onMousedown);
}
} else {
for (const el of Array.from(document.querySelectorAll('*'))) {
el.removeEventListener('mousedown', this.onMousedown);
}
}
},
isDesktop() {
if (this.isDesktop) this.adjustWidgetsWidth();
}
@@ -258,30 +241,6 @@ export default Vue.extend({
}]);
}
}
this.$root.stream.on('_disconnected_', () => {
if (this.disconnectedDialog) return;
if (this.$store.state.device.autoReload) {
location.reload();
return;
}
setTimeout(() => {
if (this.$root.stream.state !== 'reconnecting') return;
this.disconnectedDialog = this.$root.dialog({
type: 'warning',
showCancelButton: true,
title: this.$t('disconnectedFromServer'),
text: this.$t('reloadConfirm'),
}).then(({ canceled }) => {
if (!canceled) {
location.reload();
}
this.disconnectedDialog = null;
});
}, 150)
});
},
mounted() {
@@ -571,22 +530,17 @@ export default Vue.extend({
onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.$root.stream.send('readNotification', {
id: notification.id
});
if (true) {
this.$root.stream.send('readNotification', {
id: notification.id
});
this.$root.new(MkToast, {
notification
});
},
this.$root.new(MkToast, {
notification
});
}
onMousedown(e) {
e.preventDefault();
if (!contains(this.$refs.notifications.$el, e.target) &&
!contains(this.$refs.notificationButton, e.target) &&
!contains(this.$refs.notificationButton2, e.target)
) this.notificationsOpen = false;
return false;
this.$root.sound('notification');
},
widgetFunc(id) {
@@ -638,18 +592,6 @@ export default Vue.extend({
</script>
<style lang="scss" scoped>
.header-enter-active, .header-leave-active {
transition: opacity 0.5s, transform 0.5s !important;
}
.header-enter {
opacity: 0;
transform: scale(0.9);
}
.header-leave-to {
opacity: 0;
transform: scale(0.9);
}
.nav-enter-active,
.nav-leave-active {
opacity: 1;
@@ -676,7 +618,7 @@ export default Vue.extend({
$header-height: 60px;
$nav-width: 250px;
$nav-icon-only-width: 74px;
$main-width: 700px;
$main-width: 650px;
$ui-font-size: 1em;
$nav-icon-only-threshold: 1300px;
$nav-hide-threshold: 700px;
@@ -999,17 +941,21 @@ export default Vue.extend({
> main {
width: $main-width;
min-width: $main-width;
box-shadow: 1px 0 0 0 var(--divider), -1px 0 0 0 var(--divider);
@media (max-width: $side-hide-threshold) {
min-width: 0;
}
> .content {
padding: 16px;
box-sizing: border-box;
> * {
&:not(.full) {
padding: var(--margin) 0;
}
@media (max-width: 500px) {
padding: 8px;
&:not(.naked) {
background: var(--pageBg);
}
}
}
@@ -1047,6 +993,7 @@ export default Vue.extend({
> .widgets {
box-sizing: border-box;
margin-left: var(--margin);
@media (max-width: $side-hide-threshold) {
display: none;
@@ -1199,32 +1146,5 @@ export default Vue.extend({
}
}
}
> .notifications {
position: fixed;
top: 32px;
left: 0;
right: 0;
margin: 0 auto;
z-index: 10001;
width: 350px;
height: 400px;
background: var(--vocsgcxy);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
border-radius: 6px;
box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15);
overflow: hidden;
@media (max-width: 800px) {
width: 320px;
height: 350px;
}
@media (max-width: 500px) {
width: 290px;
height: 310px;
}
}
}
</style>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
<template>
<sequential-entrance class="sqadhkmv" ref="list" :direction="direction" :reversed="reversed">
<component :is="$store.state.device.animation ? 'transition-group' : 'div'" class="sqadhkmv" name="list" tag="div" :data-direction="direction" :data-reversed="reversed ? 'true' : 'false'">
<template v-for="(item, i) in items">
<slot :item="item" :i="i"></slot>
<div class="separator" :key="item.id + '_date'" v-if="showDate(i, item)">
@@ -9,7 +9,7 @@
</p>
</div>
</template>
</sequential-entrance>
</component>
</template>
<script lang="ts">
@@ -27,7 +27,8 @@ export default Vue.extend({
},
direction: {
type: String,
required: false
required: false,
default: 'down'
},
reversed: {
type: Boolean,
@@ -63,12 +64,38 @@ export default Vue.extend({
},
focus() {
this.$refs.list.focus();
this.$slots.default[0].elm.focus();
}
}
});
</script>
<style lang="scss">
.sqadhkmv {
> .list-move {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
> .list-enter-active {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1);
}
&[data-direction="up"] {
> .list-enter {
opacity: 0;
transform: translateY(64px);
}
}
&[data-direction="down"] {
> .list-enter {
opacity: 0;
transform: translateY(-64px);
}
}
}
</style>
<style lang="scss" scoped>
.sqadhkmv {
> .separator {
@@ -82,8 +109,6 @@ export default Vue.extend({
line-height: 32px;
text-align: center;
font-size: 12px;
border-radius: 64px;
background: var(--dateLabelBg);
color: var(--dateLabelFg);
> span {

View File

@@ -55,6 +55,7 @@ import { faTimesCircle, faQuestionCircle } from '@fortawesome/free-regular-svg-i
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue';
import MkSignin from './signin.vue';
import parseAcct from '../../misc/acct/parse';
import i18n from '../i18n';
@@ -65,6 +66,7 @@ export default Vue.extend({
MkButton,
MkInput,
MkSelect,
MkSignin,
},
props: {

View File

@@ -27,8 +27,6 @@ export default Vue.extend({
<style lang="scss" scoped>
.mjndxjcg {
max-width: 350px;
margin: 0 auto;
padding: 32px;
text-align: center;

View File

@@ -1,12 +1,13 @@
<template>
<div class="mk-google">
<input type="search" v-model="query" :placeholder="q">
<button @click="search"><fa icon="search"/> {{ $t('search') }}</button>
<button @click="search"><fa :icon="faSearch"/> {{ $t('search') }}</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
@@ -14,7 +15,8 @@ export default Vue.extend({
props: ['q'],
data() {
return {
query: null
query: null,
faSearch
};
},
mounted() {
@@ -42,27 +44,17 @@ export default Vue.extend({
width: 100%;
height: 40px;
font-size: 16px;
color: var(--googleSearchFg);
background: var(--googleSearchBg);
border: solid 1px var(--googleSearchBorder);
border: solid 1px var(--divider);
border-radius: 4px 0 0 4px;
&:hover {
border-color: var(--googleSearchHoverBorder);
}
}
> button {
flex-shrink: 0;
padding: 0 16px;
border: solid 1px var(--googleSearchBorder);
border: solid 1px var(--divider);
border-left: none;
border-radius: 0 4px 4px 0;
&:hover {
background-color: var(--googleSearchHoverButton);
}
&:active {
box-shadow: 0 2px 4px rgba(#000, 0.15) inset;
}

View File

@@ -9,8 +9,8 @@ import ellipsis from './ellipsis.vue';
import time from './time.vue';
import url from './url.vue';
import loading from './loading.vue';
import SequentialEntrance from './sequential-entrance.vue';
import error from './error.vue';
import streamIndicator from './stream-indicator.vue';
Vue.component('mfm', mfm);
Vue.component('mk-acct', acct);
@@ -22,4 +22,4 @@ Vue.component('mk-time', time);
Vue.component('mk-url', url);
Vue.component('mk-loading', loading);
Vue.component('mk-error', error);
Vue.component('sequential-entrance', SequentialEntrance);
Vue.component('stream-indicator', streamIndicator);

View File

@@ -90,7 +90,7 @@ export default Vue.extend({
> div {
background-color: var(--fg);
border-radius: 6px;
color: var(--secondary);
color: var(--accentLighten);
display: inline-block;
font-size: 14px;
font-weight: bold;

View File

@@ -3,8 +3,8 @@
<template v-for="media in mediaList.filter(media => !previewable(media))">
<x-banner :media="media" :key="media.id"/>
</template>
<div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container">
<div :data-count="mediaList.filter(media => previewable(media)).length" ref="grid">
<div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container" ref="gridOuter">
<div :data-count="mediaList.filter(media => previewable(media)).length" :style="gridInnerStyle">
<template v-for="media in mediaList">
<x-video :video="media" :key="media.id" v-if="media.type.startsWith('video')"/>
<x-image :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
@@ -32,19 +32,56 @@ export default Vue.extend({
},
raw: {
default: false
},
// specify the parent element
parentElement: {
type: Object
}
},
data() {
return {
gridInnerStyle: {},
sizeWaiting: false
}
},
mounted() {
//#region for Safari bug
if (this.$refs.grid) {
this.$refs.grid.style.height = this.$refs.grid.clientHeight ? `${this.$refs.grid.clientHeight}px`
: '287px';
}
//#endregion
this.size();
window.addEventListener('resize', this.size);
},
beforeDestroy() {
window.removeEventListener('resize', this.size);
},
activated() {
this.size();
},
methods: {
previewable(file) {
return file.type.startsWith('video') || file.type.startsWith('image');
},
size() {
// for Safari bug
if (this.sizeWaiting) return;
this.sizeWaiting = true;
window.requestAnimationFrame(() => {
this.sizeWaiting = false;
if (this.$refs.gridOuter) {
let height = 287;
const parent = this.$props.parentElement || this.$parent.$el;
if (this.$refs.gridOuter.clientHeight) {
height = this.$refs.gridOuter.clientHeight;
} else if (parent) {
height = parent.getBoundingClientRect().width * 9 / 16;
}
this.gridInnerStyle = { height: `${height}px` };
} else {
this.gridInnerStyle = {};
}
});
}
}
});

View File

@@ -1,6 +1,6 @@
<template>
<x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
<sequential-entrance class="rrevdjwt" :class="{ left: align === 'left' }" :delay="15" :direction="direction" ref="items">
<div class="rrevdjwt" :class="{ left: align === 'left' }" ref="items">
<template v-for="(item, i) in items.filter(item => item !== undefined)">
<div v-if="item === null" class="divider" :key="i"></div>
<span v-else-if="item.type === 'label'" class="label item" :key="i">
@@ -28,7 +28,7 @@
<i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button>
</template>
</sequential-entrance>
</div>
</x-popup>
</template>
@@ -91,7 +91,7 @@ export default Vue.extend({
mounted() {
if (this.viaKeyboard) {
this.$nextTick(() => {
focusNext(this.$refs.items.$slots.default[0].elm, true);
focusNext(this.$refs.items.children[0], true);
});
}
},

View File

@@ -37,6 +37,10 @@ export default Vue.extend({
font-size: 0.8em;
}
::v-deep > code {
word-break: break-all;
}
::v-deep .title {
text-align: center;
border-bottom: solid 1px var(--divider);

View File

@@ -1,5 +1,5 @@
<template>
<div class="zlrxdaqttccpwhpaagdmkawtzklsccam">
<div class="wrpstxzv" v-size="[{ max: 450 }]">
<mk-avatar class="avatar" :user="note.user"/>
<div class="main">
<x-note-header class="header" :note="note" :mini="true"/>
@@ -56,13 +56,12 @@ export default Vue.extend({
</script>
<style lang="scss" scoped>
.zlrxdaqttccpwhpaagdmkawtzklsccam {
.wrpstxzv {
display: flex;
padding: 16px 32px;
font-size: 0.9em;
background: rgba(0, 0, 0, 0.03);
@media (max-width: 450px) {
&.max-width_450px {
padding: 14px 16px;
}

View File

@@ -79,13 +79,13 @@
<div class="deleted" v-if="appearNote.deletedAt != null">{{ $t('deleted') }}</div>
</div>
</article>
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
<x-sub v-for="note in replies" :key="note.id" :note="note" class="reply"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight } from '@fortawesome/free-solid-svg-icons';
import { faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array';
@@ -392,7 +392,7 @@ export default Vue.extend({
}]
source: this.$refs.renoteButton,
viaKeyboard
}).then(this.focus);
});
},
renoteDirectly() {
@@ -489,6 +489,11 @@ export default Vue.extend({
noteId: this.appearNote.id
});
menu = [{
type: 'link',
icon: faInfoCircle,
text: this.$t('details'),
to: '/notes/' + this.appearNote.id
}, null, {
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent
@@ -679,6 +684,7 @@ export default Vue.extend({
.note {
position: relative;
transition: box-shadow 0.1s ease;
overflow: hidden;
&.max-width_500px {
font-size: 0.9em;
@@ -744,14 +750,6 @@ export default Vue.extend({
opacity: 1;
}
> *:first-child {
border-radius: var(--radius) var(--radius) 0 0;
}
> *:last-child {
border-radius: 0 0 var(--radius) var(--radius);
}
> .info {
display: flex;
align-items: center;
@@ -779,6 +777,11 @@ export default Vue.extend({
padding-top: 8px;
}
> .reply-to {
opacity: 0.7;
padding-bottom: 0;
}
> .renote {
display: flex;
align-items: center;
@@ -932,5 +935,9 @@ export default Vue.extend({
}
}
}
> .reply {
border-top: solid 1px var(--divider);
}
}
</style>

View File

@@ -7,22 +7,22 @@
<mk-error v-if="error" @retry="init()"/>
<div class="more" v-if="more && reversed" style="margin-bottom: var(--margin);">
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
<div v-if="more && reversed" style="margin-bottom: var(--margin);">
<button class="_panel _button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template>
</mk-button>
</button>
</div>
<x-list ref="notes" class="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
<x-note :note="note" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/>
</x-list>
<div class="more" v-if="more && !reversed" style="margin-top: var(--margin);">
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
<div v-if="more && !reversed" style="margin-top: var(--margin);">
<button class="_panel _button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template>
</mk-button>
</button>
</div>
</div>
</template>
@@ -111,16 +111,10 @@ export default Vue.extend({
&.max-width_500px {
> .notes {
> ::v-deep *:not(:last-child) {
margin-bottom: var(--marginHalf);
//margin-bottom: var(--marginHalf);
margin-bottom: 0;
}
}
}
> .more > .button {
margin-left: auto;
margin-right: auto;
height: 48px;
width: 100%;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="mk-notification" :class="notification.type">
<div class="mk-notification" :class="notification.type" v-size="[{ max: 500 }, { max: 600 }]">
<div class="head">
<mk-avatar class="avatar" :user="notification.user"/>
<div class="icon" :class="notification.type">
@@ -113,12 +113,17 @@ export default Vue.extend({
.mk-notification {
position: relative;
box-sizing: border-box;
padding: 16px;
padding: 24px 32px;
font-size: 0.9em;
overflow-wrap: break-word;
display: flex;
@media (max-width: 500px) {
&.max-width_600px {
padding: 16px;
font-size: 0.9em;
}
&.max-width_500px {
padding: 12px;
font-size: 0.8em;
}

View File

@@ -1,29 +1,28 @@
<template>
<div class="mk-notifications">
<div class="contents">
<x-list class="notifications" :items="items" v-slot="{ item: notification, i }">
<x-notification :notification="notification" :with-time="true" :full="true" class="notification" :key="notification.id"/>
</x-list>
<x-list class="notifications" :items="items" v-slot="{ item: notification }">
<x-note v-if="['reply', 'quote', 'mention'].includes(notification.type)" :note="notification.note" :key="notification.id"/>
<x-notification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
</x-list>
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
</button>
<button class="_panel _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template>
</button>
<p class="empty" v-if="empty">{{ $t('noNotifications') }}</p>
<p class="empty" v-if="empty">{{ $t('noNotifications') }}</p>
<mk-error v-if="error" @retry="init()"/>
</div>
<mk-error v-if="error" @retry="init()"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import paging from '../scripts/paging';
import XNotification from './notification.vue';
import XList from './date-separated-list.vue';
import XNote from './note.vue';
export default Vue.extend({
i18n,
@@ -31,6 +30,7 @@ export default Vue.extend({
components: {
XNotification,
XList,
XNote,
},
mixins: [
@@ -42,11 +42,6 @@ export default Vue.extend({
type: String,
required: false
},
wide: {
type: Boolean,
required: false,
default: false
}
},
data() {
@@ -59,7 +54,6 @@ export default Vue.extend({
includeTypes: this.type ? [this.type] : undefined
})
},
faSpinner
};
},
@@ -93,44 +87,23 @@ export default Vue.extend({
<style lang="scss" scoped>
.mk-notifications {
> .contents {
overflow: auto;
height: 100%;
padding: 8px 8px 0 8px;
> .notifications {
> ::v-deep * {
margin-bottom: 8px;
}
> .notification {
background: var(--panel);
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
> .notifications {
> ::v-deep * {
//margin-bottom: var(--margin);
margin-bottom: 0;
}
}
> .more {
display: block;
width: 100%;
padding: 16px;
> .empty {
margin: 0;
padding: 16px;
text-align: center;
color: var(--fg);
}
> [data-icon] {
margin-right: 4px;
}
}
> .empty {
margin: 0;
padding: 16px;
text-align: center;
color: var(--fg);
}
> .placeholder {
padding: 32px;
opacity: 0.3;
}
> .placeholder {
padding: 32px;
opacity: 0.3;
}
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<div class="vswabwbm" :style="{ top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }">
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<circle fill="none" cx="64" cy="64">
<animate attributeName="r"
begin="0s" dur="0.5s"
values="4; 32"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.165, 0.84, 0.44, 1"
repeatCount="1" />
<animate attributeName="stroke-width"
begin="0s" dur="0.5s"
values="16; 0"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.3, 0.61, 0.355, 1"
repeatCount="1" />
</circle>
<g fill="none" fill-rule="evenodd">
<circle v-for="(particle, i) in particles" :key="i" :fill="particle.color">
<animate attributeName="r"
begin="0s" dur="0.8s"
:values="`${particle.size}; 0`"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.165, 0.84, 0.44, 1"
repeatCount="1" />
<animate attributeName="cx"
begin="0s" dur="0.8s"
:values="`${particle.xA}; ${particle.xB}`"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.3, 0.61, 0.355, 1"
repeatCount="1" />
<animate attributeName="cy"
begin="0s" dur="0.8s"
:values="`${particle.yA}; ${particle.yB}`"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.3, 0.61, 0.355, 1"
repeatCount="1" />
</circle>
</g>
</svg>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
}
},
data() {
const particles = [];
const origin = 64;
const colors = ['#FF1493', '#00FFFF', '#FFE202'];
for (let i = 0; i < 12; i++) {
const angle = Math.random() * (Math.PI * 2);
const pos = Math.random() * 16;
const velocity = 16 + (Math.random() * 48);
particles.push({
size: 4 + (Math.random() * 8),
xA: origin + (Math.sin(angle) * pos),
yA: origin + (Math.cos(angle) * pos),
xB: origin + (Math.sin(angle) * (pos + velocity)),
yB: origin + (Math.cos(angle) * (pos + velocity)),
color: colors[Math.floor(Math.random() * colors.length)]
});
}
return {
particles
};
},
mounted() {
setTimeout(() => {
this.destroyDom();
}, 1100);
}
});
</script>
<style lang="scss" scoped>
.vswabwbm {
pointer-events: none;
position: fixed;
z-index: 1000000;
width: 128px;
height: 128px;
> svg {
> circle {
stroke: var(--accent);
}
}
}
</style>

View File

@@ -53,7 +53,7 @@ import Vue from 'vue';
import { faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import { erase } from '../../prelude/array';
import { addTimespan } from '../../prelude/time';
import { addTime } from '../../prelude/time';
import { formatDateTimeString } from '../../misc/format-time-string';
import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue';
@@ -73,7 +73,7 @@ export default Vue.extend({
choices: ['', ''],
multiple: false,
expiration: 'infinite',
atDate: formatDateTimeString(addTimespan(new Date(), 1, 'days'), 'yyyy-MM-dd'),
atDate: formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'),
atTime: '00:00',
after: 0,
unit: 'second',

View File

@@ -1,6 +1,6 @@
<template>
<div class="ulveipglmagnxfgvitaxyszerjwiqmwl">
<transition :name="$store.state.device.animation ? 'form-fade' : ''" appear>
<transition :name="$store.state.device.animation ? 'form-fade' : ''" appear @after-leave="$emit('closed');">
<div class="bg" ref="bg" v-if="show" @click="close()"></div>
</transition>
<div class="main" ref="main" @click.self="close()" @keydown="onKeydown">
@@ -17,7 +17,8 @@
:initial-note="initialNote"
:instant="instant"
@posted="onPosted"
@cancel="onCanceled"/>
@cancel="onCanceled"
style="border-radius: var(--radius);"/>
</transition>
</div>
</div>

View File

@@ -586,7 +586,6 @@ export default Vue.extend({
<style lang="scss" scoped>
.gafaadew {
background: var(--panel);
border-radius: var(--radius);
> header {
z-index: 1000;

View File

@@ -1,20 +1,9 @@
<template>
<x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
<div class="rdfaahpb">
<transition-group
name="reaction-fade"
tag="div"
class="buttons"
ref="buttons"
:class="{ showFocus }"
:css="false"
@before-enter="beforeEnter"
@enter="enter"
mode="out-in"
appear
>
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction"><x-reaction-icon :reaction="reaction"/></button>
</transition-group>
<div class="buttons" ref="buttons" :class="{ showFocus }">
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><x-reaction-icon :reaction="reaction"/></button>
</div>
<input class="text" v-model="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
</div>
</x-popup>
@@ -84,7 +73,7 @@ export default Vue.extend({
watch: {
focus(i) {
this.$refs.buttons.children[i].elm.focus();
this.$refs.buttons.children[i].focus();
}
},
@@ -129,21 +118,7 @@ export default Vue.extend({
},
choose() {
this.$refs.buttons.children[this.focus].elm.click();
},
beforeEnter(el) {
el.style.opacity = 0;
el.style.transform = 'scale(0.7)';
},
enter(el, done) {
el.style.transition = [getComputedStyle(el).transition, 'transform 1s cubic-bezier(0.23, 1, 0.32, 1)', 'opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1)'].filter(x => x != '').join(',');
setTimeout(() => {
el.style.opacity = 1;
el.style.transform = 'scale(1)';
setTimeout(done, 1000);
}, 0 * el.dataset.index)
this.$refs.buttons.children[this.focus].click();
},
}
});

View File

@@ -1,16 +1,17 @@
<template>
<span
class="reaction _button"
<button
class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction }"
@click="toggleReaction(reaction)"
v-if="count > 0"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
ref="reaction"
v-particle
>
<x-reaction-icon :reaction="reaction" ref="icon"/>
<span>{{ count }}</span>
</span>
</button>
</template>
<script lang="ts">
@@ -136,7 +137,7 @@ export default Vue.extend({
</script>
<style lang="scss" scoped>
.reaction {
.hkzvhatu {
display: inline-block;
height: 32px;
margin: 2px;

View File

@@ -1,5 +1,5 @@
<template>
<div class="mk-reactions-viewer" :class="{ isMe }">
<div class="tdflqwzn" :class="{ isMe }">
<x-reaction v-for="(count, reaction) in note.reactions" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note" :key="reaction"/>
</div>
</template>
@@ -32,7 +32,7 @@ export default Vue.extend({
</script>
<style lang="scss" scoped>
.mk-reactions-viewer {
.tdflqwzn {
margin: 4px -2px 0 -2px;
&:empty {

View File

@@ -0,0 +1,36 @@
<template>
<div class="jmgmzlwq _panel"><fa :icon="faExclamationTriangle" style="margin-right: 8px;"/>{{ $t('remoteUserCaution') }}<a :href="href" rel="nofollow noopener" target="_blank">{{ $t('showOnRemote') }}</a></div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
i18n,
props: {
href: {
type: String,
required: true
},
},
data() {
return {
faExclamationTriangle
};
}
});
</script>
<style lang="scss" scoped>
.jmgmzlwq {
font-size: 0.8em;
padding: 16px;
> a {
margin-left: 4px;
color: var(--accent);
}
}
</style>

View File

@@ -1,80 +0,0 @@
<template>
<transition-group v-if="$store.state.device.animation"
class="uupnnhew"
:data-direction="direction"
:data-reversed="reversed ? 'true' : 'false'"
name="staggered"
tag="div"
appear
>
<slot></slot>
</transition-group>
<div v-else>
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
delay: {
type: Number,
required: false,
default: 40
},
direction: {
type: String,
required: false,
default: 'down'
},
reversed: {
type: Boolean,
required: false,
default: false
}
},
methods: {
focus() {
this.$slots.default[0].elm.focus();
}
},
});
</script>
<style lang="scss">
.staggered-move {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) !important;
}
.uupnnhew[data-direction="up"] {
.staggered-enter {
opacity: 0;
transform: translateY(64px);
}
}
.uupnnhew[data-direction="down"] {
.staggered-enter {
opacity: 0;
transform: translateY(-64px);
}
}
.uupnnhew[data-reversed="true"] {
@for $i from 1 through 30 {
.staggered-enter-active:nth-last-child(#{$i}) {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1)), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1));
}
}
}
.uupnnhew[data-reversed="false"] {
@for $i from 1 through 30 {
.staggered-enter-active:nth-child(#{$i}) {
transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1)), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1) (15ms * ($i - 1));
}
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }">
<template #header>{{ $t('login') }}</template>
<x-signin :auto-set="autoSet" @login="onLogin"/>
<mk-signin :auto-set="autoSet" @login="onLogin"/>
</x-window>
</template>
@@ -9,13 +9,13 @@
import Vue from 'vue';
import i18n from '../i18n';
import XWindow from './window.vue';
import XSignin from './signin.vue';
import MkSignin from './signin.vue';
export default Vue.extend({
i18n,
components: {
XSignin,
MkSignin,
XWindow,
},

0
src/client/components/signin.vue Normal file → Executable file
View File

View File

@@ -0,0 +1,80 @@
<template>
<div class="nsbbhtug" v-if="hasDisconnected" @click="resetDisconnected">
<div>{{ $t('disconnectedFromServer') }}</div>
<div class="command">
<button class="_textButton" @click="reload">{{ $t('reload') }}</button>
<button class="_textButton">{{ $t('doNothing') }}</button>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
export default Vue.extend({
i18n,
data() {
return {
hasDisconnected: false,
}
},
computed: {
stream() {
return this.$root.stream;
},
},
created() {
this.$root.stream.on('_connected_', this.onConnected);
this.$root.stream.on('_disconnected_', this.onDisconnected);
},
beforeDestroy() {
this.$root.stream.off('_connected_', this.onConnected);
this.$root.stream.off('_disconnected_', this.onDisconnected);
},
methods: {
onConnected() {
if (this.hasDisconnected) {
if (this.$store.state.device.autoReload) {
this.reload();
}
}
},
onDisconnected() {
this.hasDisconnected = true;
},
resetDisconnected() {
this.hasDisconnected = false;
},
reload() {
location.reload();
},
}
});
</script>
<style lang="scss" scoped>
.nsbbhtug {
position: fixed;
z-index: 16385;
bottom: 8px;
right: 8px;
margin: 0;
padding: 6px 12px;
font-size: 0.9em;
color: #fff;
background: #000;
opacity: 0.8;
border-radius: 4px;
max-width: 320px;
> .command {
display: flex;
justify-content: space-around;
> button {
padding: 0.7em;
}
}
}
</style>

View File

@@ -21,6 +21,11 @@ export default Vue.extend({
},
antenna: {
required: false
},
sound: {
type: Boolean,
required: false,
default: false,
}
},
@@ -46,6 +51,10 @@ export default Vue.extend({
const prepend = note => {
(this.$refs.tl as any).prepend(note);
if (this.sound) {
this.$root.sound(note.userId === this.$store.state.i.id ? 'noteMy' : 'note');
}
};
const onUserAdded = () => {

View File

@@ -124,7 +124,6 @@ export default Vue.extend({
&.primary {
color: #fff;
background: var(--accent);
box-shadow: 0 6px 16px var(--accentShadow);
&:not(:disabled):hover {
background: var(--jkhztclx);

View File

@@ -110,6 +110,7 @@ export default Vue.extend({
> header {
position: relative;
box-shadow: 0 1px 0 0 var(--divider);
z-index: 1;
> .title {
margin: 0;

View File

@@ -1,5 +1,5 @@
<template>
<sequential-entrance class="cxiknjgy" :class="{ autoMargin }">
<div class="cxiknjgy" :class="{ autoMargin }">
<slot :items="items"></slot>
<div class="empty" v-if="empty" key="_empty_">
<slot name="empty"></slot>
@@ -10,7 +10,7 @@
<template v-if="moreFetching"><mk-loading inline/></template>
</mk-button>
</div>
</sequential-entrance>
</div>
</template>
<script lang="ts">

View File

@@ -0,0 +1,138 @@
<template>
<div class="timctyfi" :class="{ focused, disabled }">
<div class="icon"><slot name="icon"></slot></div>
<span class="title"><slot name="title"></slot></span>
<input
type="range"
ref="input"
v-model="v"
:disabled="disabled"
:min="min"
:max="max"
:step="step"
:autofocus="autofocus"
@focus="focused = true"
@blur="focused = false"
@input="$emit('input', $event.target.value)"
/>
</div>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
props: {
value: {
type: Number,
required: false,
default: 0
},
disabled: {
type: Boolean,
required: false,
default: false
},
min: {
type: Number,
required: false,
default: 0
},
max: {
type: Number,
required: false,
default: 100
},
step: {
type: Number,
required: false,
default: 1
},
autofocus: {
type: Boolean,
required: false
}
},
data() {
return {
v: this.value,
focused: false
};
},
watch: {
value(v) {
this.v = parseFloat(v);
}
},
mounted() {
if (this.autofocus) {
this.$nextTick(() => {
this.$refs.input.focus();
});
}
}
});
</script>
<style lang="scss" scoped>
.timctyfi {
position: relative;
margin: 8px;
> .icon {
display: inline-block;
width: 24px;
text-align: center;
}
> .title {
pointer-events: none;
font-size: 16px;
color: var(--inputLabel);
overflow: hidden;
}
> input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: var(--xxubwiul);
height: 7px;
margin: 0 8px;
outline: 0;
border: 0;
border-radius: 7px;
&.disabled {
opacity: 0.6;
cursor: not-allowed;
}
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
cursor: pointer;
width: 20px;
height: 20px;
display: block;
border-radius: 50%;
border: none;
background: var(--accent);
box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
box-sizing: content-box;
}
&::-moz-range-thumb {
-moz-appearance: none;
appearance: none;
cursor: pointer;
width: 20px;
height: 20px;
display: block;
border-radius: 50%;
border: none;
background: var(--accent);
box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
}
}
}
</style>

View File

@@ -56,7 +56,7 @@ export default Vue.extend({
}
},
filled(): boolean {
return this.v != '' && this.v != null;
return true;
}
},
mounted() {
@@ -100,6 +100,7 @@ export default Vue.extend({
> .input {
display: flex;
position: relative;
&:before {
content: '';
@@ -151,12 +152,17 @@ export default Vue.extend({
font-weight: normal;
font-size: 16px;
height: 32px;
background: var(--panel);
background: none;
border: none;
border-radius: 0;
outline: none;
box-shadow: none;
color: var(--fg);
option,
optgroup {
background: var(--bg);
}
}
> .prefix,

View File

@@ -230,8 +230,8 @@ export default Vue.extend({
position: relative;
display: block;
font-size: 14px;
box-shadow: 0 1px 4px var(--tyvedwbe);
border-radius: 4px;
box-shadow: 0 0 0 1px var(--divider);
border-radius: 6px;
overflow: hidden;
&:hover {

View File

@@ -51,6 +51,7 @@ export default Vue.extend({
target: self ? null : '_blank',
showTimer: null,
hideTimer: null,
checkTimer: null,
preview: null,
faExternalLinkSquareAlt
};
@@ -78,9 +79,14 @@ export default Vue.extend({
}).$mount();
document.body.appendChild(this.preview.$el);
this.checkTimer = setInterval(() => {
if (!document.body.contains(this.$el)) this.closePreview();
}, 1000);
},
closePreview() {
if (this.preview) {
clearInterval(this.checkTimer);
this.preview.destroyDom();
this.preview = null;
}

View File

@@ -53,6 +53,7 @@ export default Vue.extend({
return {
u: null,
show: false,
closed: false,
top: 0,
left: 0,
};
@@ -68,6 +69,7 @@ export default Vue.extend({
{ userId: this.user };
this.$root.api('users/show', query).then(user => {
if (this.closed) return;
this.u = user;
this.show = true;
});
@@ -83,6 +85,7 @@ export default Vue.extend({
methods: {
close() {
this.closed = true;
this.show = false;
if (this.$refs.content) (this.$refs.content as any).style.pointerEvents = 'none';
}

View File

@@ -6,15 +6,15 @@
<button class="_button" @click="close()"><fa :icon="faTimes"/></button>
</div>
<sequential-entrance class="users">
<router-link v-for="(item, i) in items" class="user" :key="item.id" :to="extract ? extract(item) : item | userPage">
<div class="users">
<router-link v-for="item in items" class="user" :key="item.id" :to="extract ? extract(item) : item | userPage">
<mk-avatar :user="extract ? extract(item) : item" class="avatar" :disable-link="true"/>
<div class="body">
<mk-user-name :user="extract ? extract(item) : item" class="name"/>
<mk-acct :user="extract ? extract(item) : item" class="acct"/>
</div>
</router-link>
</sequential-entrance>
</div>
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>

View File

@@ -1,6 +1,6 @@
<template>
<x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }">
<sequential-entrance class="gqyayizv" :delay="30">
<div class="gqyayizv">
<button class="_button" @click="choose('public')" :class="{ active: v == 'public' }" data-index="1" key="public">
<div><fa :icon="faGlobe"/></div>
<div>
@@ -29,7 +29,7 @@
<span>{{ $t('_visibility.specifiedDescription') }}</span>
</div>
</button>
</sequential-entrance>
</div>
</x-popup>
</template>

View File

@@ -3,8 +3,10 @@ import Vue from 'vue';
import userPreview from './user-preview';
import autocomplete from './autocomplete';
import size from './size';
import particle from './particle';
Vue.directive('autocomplete', autocomplete);
Vue.directive('userPreview', userPreview);
Vue.directive('user-preview', userPreview);
Vue.directive('size', size);
Vue.directive('particle', particle);

View File

@@ -0,0 +1,22 @@
import Particle from '../components/particle.vue';
export default {
bind(el, binding, vn) {
el.addEventListener('click', () => {
const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2);
const y = rect.top + (el.clientHeight / 2);
const particle = new Particle({
parent: vn.context,
propsData: {
x,
y
}
}).$mount();
document.body.appendChild(particle.$el);
});
}
};

View File

@@ -59,7 +59,7 @@ export default {
const ro = new ResizeObserver((entries, observer) => {
calc();
});
ro.observe(el);
el._ro_ = ro;

View File

@@ -8,9 +8,11 @@ export default {
self.tag = null;
self.showTimer = null;
self.hideTimer = null;
self.checkTimer = null;
self.close = () => {
if (self.tag) {
clearInterval(self.checkTimer);
self.tag.close();
self.tag = null;
}
@@ -38,6 +40,14 @@ export default {
});
document.body.appendChild(self.tag.$el);
self.checkTimer = setInterval(() => {
if (!document.body.contains(el)) {
clearTimeout(self.showTimer);
clearTimeout(self.hideTimer);
self.close();
}
}, 1000);
};
el.addEventListener('mouseover', () => {
@@ -60,8 +70,6 @@ export default {
unbind(el, binding, vn) {
const self = el._userPreviewDirective_;
clearTimeout(self.showTimer);
clearTimeout(self.hideTimer);
self.close();
clearInterval(self.checkTimer);
}
};

View File

@@ -81,14 +81,14 @@ if (lang == null) {
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
let isMobile = /mobile|iphone|ipad|android/.test(ua);
const isMobile = /mobile|iphone|ipad|android/.test(ua);
// Get the <head> element
const head = document.getElementsByTagName('head')[0];
// If mobile, insert the viewport meta tag
if (isMobile || window.innerWidth <= 1024) {
const viewport = document.getElementsByName("viewport").item(0);
const viewport = document.getElementsByName('viewport').item(0);
viewport.setAttribute('content',
`${viewport.getAttribute('content')},minimum-scale=1,maximum-scale=1,user-scalable=no`);
head.appendChild(viewport);
@@ -136,6 +136,14 @@ document.body.innerHTML = '<div id="app"></div>';
const os = new MiOS();
os.init(async () => {
window.addEventListener('storage', e => {
if (e.key === 'vuex') {
os.store.replaceState(JSON.parse(localStorage['vuex']));
} else if (e.key === 'i') {
location.reload();
}
}, false)
if ('Notification' in window && os.store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
@@ -189,6 +197,14 @@ os.init(async () => {
if (cb) vm.$once('closed', cb);
(vm as any).focus();
},
sound(type: string) {
if (this.$store.state.device.sfxVolume === 0) return;
const sound = this.$store.state.device['sfx' + type.substr(0, 1).toUpperCase() + type.substr(1)];
if (sound == null) return;
const audio = new Audio(`/assets/sounds/${sound}.mp3`);
audio.volume = this.$store.state.device.sfxVolume;
audio.play();
}
},
router: router,
render: createEl => createEl(App)
@@ -198,4 +214,96 @@ os.init(async () => {
// マウント
app.$mount('#app');
if (app.$store.getters.isSignedIn) {
const main = os.stream.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
app.$store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
app.$store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
app.$store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('unreadMention', () => {
app.$store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
app.$store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
app.$store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
app.$store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('readAllMessagingMessages', () => {
app.$store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
app.$store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
app.sound('chatBg');
});
main.on('readAllAntennas', () => {
app.$store.dispatch('mergeMe', {
hasUnreadAntenna: false
});
});
main.on('unreadAntenna', () => {
app.$store.dispatch('mergeMe', {
hasUnreadAntenna: true
});
app.sound('antenna');
});
main.on('readAllAnnouncements', () => {
app.$store.dispatch('mergeMe', {
hasUnreadAnnouncement: false
});
});
main.on('clientSettingUpdated', x => {
app.$store.commit('settings/set', {
key: x.key,
value: x.value
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
os.signout();
});
}
});

View File

@@ -3,7 +3,7 @@ import Vue from 'vue';
import { EventEmitter } from 'eventemitter3';
import initStore from './store';
import { apiUrl, version, locale } from './config';
import { apiUrl, version } from './config';
import Progress from './scripts/loading';
import Stream from './scripts/stream';
@@ -123,8 +123,13 @@ export default class MiOS extends EventEmitter {
});
} else {
// Get token from localStorage
const i = localStorage.getItem('i');
let i = localStorage.getItem('i');
// 連携ログインの場合用にCookieを参照する
if (i == null || i === 'null') {
i = (document.cookie.match(/igi=(\w+)/) || [null, null])[1];
}
fetchme(i, me => {
if (me) {
this.store.dispatch('login', me);
@@ -142,94 +147,6 @@ export default class MiOS extends EventEmitter {
@autobind
private initStream() {
this.stream = new Stream(this);
if (this.store.getters.isSignedIn) {
const main = this.stream.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
this.store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
this.store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
this.store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('unreadMention', () => {
this.store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
this.store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
this.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
this.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('readAllMessagingMessages', () => {
this.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
this.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
});
main.on('readAllAntennas', () => {
this.store.dispatch('mergeMe', {
hasUnreadAntenna: false
});
});
main.on('unreadAntenna', () => {
this.store.dispatch('mergeMe', {
hasUnreadAntenna: true
});
});
main.on('readAllAnnouncements', () => {
this.store.dispatch('mergeMe', {
hasUnreadAnnouncement: false
});
});
main.on('clientSettingUpdated', x => {
this.store.commit('settings/set', {
key: x.key,
value: x.value
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
this.signout();
});
}
}
/**

9
src/client/pages/auth.vue Normal file → Executable file
View File

@@ -26,7 +26,7 @@
</div>
<div class="signin" v-else>
<h1>{{ $t('sign-in') }}</h1>
<mk-signin/>
<mk-signin @login="onLogin"/>
</div>
</template>
@@ -34,11 +34,13 @@
import Vue from 'vue';
import i18n from '../i18n';
import XForm from './auth.form.vue';
import MkSignin from '../components/signin.vue';
export default Vue.extend({
i18n,
components: {
XForm
XForm,
MkSignin,
},
data() {
return {
@@ -83,6 +85,9 @@ export default Vue.extend({
if (this.session.app.callbackUrl) {
location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`;
}
}, onLogin(res) {
localStorage.setItem('i', res.i);
location.reload();
}
}
});

View File

@@ -1,5 +1,5 @@
<template>
<div>
<div class="naked full">
<portal to="header">
<button @click="menu" class="_button _jmoebdiw_">
<fa :icon="faCloud" style="margin-right: 8px;"/>

View File

@@ -19,7 +19,7 @@
<x-tutorial class="tutorial" v-if="$store.state.settings.tutorial != -1"/>
<x-post-form class="post-form _panel" fixed v-if="$store.state.device.showFixedPostForm"/>
<x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src" :src="src" :list="list" :antenna="antenna" @before="before()" @after="after()" @queue="queueUpdated"/>
<x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src" :src="src" :list="list" :antenna="antenna" :sound="true" @before="before()" @after="after()" @queue="queueUpdated"/>
</div>
</template>
@@ -70,6 +70,10 @@ export default Vue.extend({
't': this.focus
};
},
meta() {
return this.$store.state.instance.meta;
},
},
watch: {
@@ -98,6 +102,10 @@ export default Vue.extend({
}
},
mounted() {
this.width = this.$el.offsetWidth;
},
methods: {
before() {
Progress.start();
@@ -108,7 +116,7 @@ export default Vue.extend({
},
queueUpdated(q) {
this.width = this.$el.offsetWidth;
if (this.$el.offsetWidth !== 0) this.width = this.$el.offsetWidth;
this.queue = q;
},
@@ -117,6 +125,7 @@ export default Vue.extend({
},
async choose(ev) {
if (this.meta == null) return;
this.menuOpened = true;
const [antennas, lists] = await Promise.all([
this.$root.api('antennas/list'),
@@ -144,15 +153,15 @@ export default Vue.extend({
text: this.$t('_timelines.home'),
icon: faHome,
action: () => { this.setSrc('home') }
}, {
}, this.meta.disableLocalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.local'),
icon: faComments,
action: () => { this.setSrc('local') }
}, {
}, this.meta.disableLocalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.social'),
icon: faShareAlt,
action: () => { this.setSrc('social') }
}, {
}, this.meta.disableGlobalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.global'),
icon: faGlobe,
action: () => { this.setSrc('global') }
@@ -192,7 +201,7 @@ export default Vue.extend({
> button {
display: block;
margin: 0 auto;
padding: 8px 12px;
padding: 8px 16px;
border-radius: 32px;
}
}

View File

@@ -11,12 +11,12 @@
<canvas ref="chart"></canvas>
</div>
<div class="_content" style="max-height: 180px; overflow: auto;">
<sequential-entrance :delay="15" v-if="jobs.length > 0">
<div v-for="(job, i) in jobs" :key="job[0]">
<div v-if="jobs.length > 0">
<div v-for="job in jobs" :key="job[0]">
<span>{{ job[0] }}</span>
<span style="margin-left: 8px; opacity: 0.7;">({{ job[1] | number }} jobs)</span>
</div>
</sequential-entrance>
</div>
<span v-else style="opacity: 0.5;">{{ $t('noJobs') }}</span>
</div>
</section>

View File

@@ -102,21 +102,20 @@
<div class="_content">
<mk-switch v-model="useObjectStorage">{{ $t('useObjectStorage') }}</mk-switch>
<template v-if="useObjectStorage">
<mk-input v-model="objectStorageBaseUrl" :disabled="!useObjectStorage">URL</mk-input>
<mk-input v-model="objectStorageBaseUrl" :disabled="!useObjectStorage">{{ $t('objectStorageBaseUrl') }}<template #desc>{{ $t('objectStorageBaseUrlDesc') }}</template></mk-input>
<div class="_inputs">
<mk-input v-model="objectStorageBucket" :disabled="!useObjectStorage">Bucket</mk-input>
<mk-input v-model="objectStoragePrefix" :disabled="!useObjectStorage">Prefix</mk-input>
<mk-input v-model="objectStorageBucket" :disabled="!useObjectStorage">{{ $t('objectStorageBucket') }}<template #desc>{{ $t('objectStorageBucketDesc') }}</template></mk-input>
<mk-input v-model="objectStoragePrefix" :disabled="!useObjectStorage">{{ $t('objectStoragePrefix') }}<template #desc>{{ $t('objectStoragePrefixDesc') }}</template></mk-input>
</div>
<mk-input v-model="objectStorageEndpoint" :disabled="!useObjectStorage">Endpoint</mk-input>
<mk-input v-model="objectStorageEndpoint" :disabled="!useObjectStorage">{{ $t('objectStorageEndpoint') }}<template #desc>{{ $t('objectStorageEndpointDesc') }}</template></mk-input>
<div class="_inputs">
<mk-input v-model="objectStorageRegion" :disabled="!useObjectStorage">Region</mk-input>
<mk-input v-model="objectStoragePort" type="number" :disabled="!useObjectStorage">Port</mk-input>
<mk-input v-model="objectStorageRegion" :disabled="!useObjectStorage">{{ $t('objectStorageRegion') }}<template #desc>{{ $t('objectStorageRegionDesc') }}</template></mk-input>
</div>
<div class="_inputs">
<mk-input v-model="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Access key</mk-input>
<mk-input v-model="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Secret key</mk-input>
</div>
<mk-switch v-model="objectStorageUseSSL" :disabled="!useObjectStorage">SSL</mk-switch>
<mk-switch v-model="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $t('objectStorageUseSSL') }}<template #desc>{{ $t('objectStorageUseSSLDesc') }}</template></mk-switch>
</template>
</div>
<div class="_footer">

View File

@@ -2,7 +2,7 @@
<div class="thvuemwp" :data-is-me="isMe">
<mk-avatar class="avatar" :user="message.user"/>
<div class="content">
<div class="balloon _panel" :data-no-text="message.text == null">
<div class="balloon" :data-no-text="message.text == null">
<button class="delete-button" v-if="isMe" :title="$t('delete')" @click="del">
<img src="/assets/remove.png" alt="Delete"/>
</button>
@@ -243,13 +243,14 @@ export default Vue.extend({
}
&:not([data-is-me]) {
padding-left: var(--margin);
> .content {
padding-left: 16px;
padding-right: 32px;
> .balloon {
$color: var(--panel);
$color: var(--messageBg);
background: $color;
&[data-no-text] {
@@ -279,6 +280,7 @@ export default Vue.extend({
&[data-is-me] {
flex-direction: row-reverse;
padding-right: var(--margin);
> .content {
padding-right: 16px;
@@ -287,7 +289,6 @@ export default Vue.extend({
> .balloon {
background: $me-balloon-color;
box-shadow: 0 6px 16px var(--accentShadow);
text-align: left;
&[data-no-text] {
@@ -310,7 +311,7 @@ export default Vue.extend({
}
> .text {
&, * {
&, ::v-deep * {
color: #fff !important;
}
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="mk-messaging-room"
<div class="mk-messaging-room naked"
@dragover.prevent.stop="onDragover"
@drop.prevent.stop="onDrop"
>
@@ -184,12 +184,7 @@ export default Vue.extend({
},
onMessage(message) {
// サウンドを再生する
if (this.$store.state.device.enableSounds) {
const sound = new Audio(`${url}/assets/message.mp3`);
sound.volume = this.$store.state.device.soundVolume;
sound.play();
}
this.$root.sound('chat');
const isBottom = this.isBottom();

View File

@@ -5,7 +5,7 @@
<mk-button @click="start" primary class="start"><fa :icon="faPlus"/> {{ $t('startMessaging') }}</mk-button>
<sequential-entrance class="history" v-if="messages.length > 0" :delay="30">
<div class="history" v-if="messages.length > 0">
<router-link v-for="(message, i) in messages"
class="message _panel"
:to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
@@ -30,7 +30,7 @@
</div>
</div>
</router-link>
</sequential-entrance>
</div>
<div class="no-history" v-if="!fetching && messages.length == 0">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div>{{ $t('noHistory') }}</div>

View File

@@ -30,6 +30,10 @@
<span>{{ $t('antennaKeywords') }}</span>
<template #desc>{{ $t('antennaKeywordsDescription') }}</template>
</mk-textarea>
<mk-textarea v-model="excludeKeywords">
<span>{{ $t('antennaExcludeKeywords') }}</span>
<template #desc>{{ $t('antennaKeywordsDescription') }}</template>
</mk-textarea>
<mk-switch v-model="caseSensitive">{{ $t('caseSensitive') }}</mk-switch>
<mk-switch v-model="withFile">{{ $t('withFileAntenna') }}</mk-switch>
<mk-switch v-model="notify">{{ $t('notifyAntenna') }}</mk-switch>
@@ -75,6 +79,7 @@ export default Vue.extend({
userGroupId: null,
users: '',
keywords: '',
excludeKeywords: '',
caseSensitive: false,
withReplies: false,
withFile: false,
@@ -107,6 +112,7 @@ export default Vue.extend({
this.userGroupId = this.antenna.userGroupId;
this.users = this.antenna.users.join('\n');
this.keywords = this.antenna.keywords.map(x => x.join(' ')).join('\n');
this.excludeKeywords = this.antenna.excludeKeywords.map(x => x.join(' ')).join('\n');
this.caseSensitive = this.antenna.caseSensitive;
this.withReplies = this.antenna.withReplies;
this.withFile = this.antenna.withFile;
@@ -126,7 +132,8 @@ export default Vue.extend({
notify: this.notify,
caseSensitive: this.caseSensitive,
users: this.users.trim().split('\n').map(x => x.trim()),
keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' '))
keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' ')),
excludeKeywords: this.excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
});
this.$emit('created');
} else {
@@ -141,7 +148,8 @@ export default Vue.extend({
notify: this.notify,
caseSensitive: this.caseSensitive,
users: this.users.trim().split('\n').map(x => x.trim()),
keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' '))
keywords: this.keywords.trim().split('\n').map(x => x.trim().split(' ')),
excludeKeywords: this.excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
});
}

View File

@@ -53,6 +53,7 @@ export default Vue.extend({
userGroupId: null,
users: [],
keywords: [],
excludeKeywords: [],
withReplies: false,
caseSensitive: false,
withFile: false,

View File

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

View File

@@ -2,7 +2,10 @@
<section class="_card">
<div class="_title"><fa :icon="faLaugh"/> {{ $t('reaction') }}</div>
<div class="_content">
<mk-textarea v-model="reactions">{{ $t('reaction') }}<template #desc>{{ $t('reactionSettingDescription') }}</template></mk-textarea>
<mk-input v-model="reactions" style="font-family: 'Segoe UI Emoji', 'Noto Color Emoji', Roboto, HelveticaNeue, Arial, sans-serif">
{{ $t('reaction') }}<template #desc>{{ $t('reactionSettingDescription') }} <button class="_textButton" @click="chooseEmoji">{{ $t('chooseEmoji') }}</button></template>
</mk-input>
<mk-button inline @click="setDefault"><fa :icon="faUndo"/> {{ $t('default') }}</mk-button>
</div>
<div class="_footer">
<mk-button @click="save()" primary inline :disabled="!changed"><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
@@ -14,24 +17,26 @@
<script lang="ts">
import Vue from 'vue';
import { faLaugh, faSave, faEye } from '@fortawesome/free-regular-svg-icons';
import MkTextarea from '../../components/ui/textarea.vue';
import { faUndo } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue';
import MkReactionPicker from '../../components/reaction-picker.vue';
import i18n from '../../i18n';
import { emojiRegexWithCustom } from '../../../misc/emoji-regex';
export default Vue.extend({
i18n,
components: {
MkTextarea,
MkInput,
MkButton,
},
data() {
return {
reactions: this.$store.state.settings.reactions.join('\n'),
reactions: this.$store.state.settings.reactions.join(''),
changed: false,
faLaugh, faSave, faEye
faLaugh, faSave, faEye, faUndo
}
},
@@ -41,21 +46,40 @@ export default Vue.extend({
}
},
computed: {
splited(): any {
return this.reactions.match(emojiRegexWithCustom);
},
},
methods: {
save() {
this.$store.dispatch('settings/set', { key: 'reactions', value: this.reactions.trim().split('\n') });
this.$store.dispatch('settings/set', { key: 'reactions', value: this.splited });
this.changed = false;
},
preview(ev) {
const picker = this.$root.new(MkReactionPicker, {
source: ev.currentTarget || ev.target,
reactions: this.reactions.trim().split('\n'),
reactions: this.splited,
showFocus: false,
});
picker.$once('chosen', reaction => {
picker.close();
});
},
setDefault() {
this.reactions = '👍❤😆🤔😮🎉💢😥😇🍮';
},
async chooseEmoji(ev) {
const vm = this.$root.new(await import('../../components/emoji-picker.vue').then(m => m.default), {
source: ev.currentTarget || ev.target
}).$once('chosen', emoji => {
this.reactions += emoji;
vm.close();
});
}
}
});

View File

@@ -1,24 +1,28 @@
<template>
<div class="mk-note-page">
<portal to="avatar" v-if="note"><mk-avatar class="avatar" :user="note.user" :disable-preview="true"/></portal>
<portal to="title" v-if="note">{{ $t('noteOf', { user: note.user.name }) }}</portal>
<portal to="title" v-if="note">
<mfm
:text="$t('noteOf', { user: note.user.name || note.user.username })"
:plain="true" :nowrap="true" :custom-emojis="note.user.emojis" :is-note="false"
/>
</portal>
<transition :name="$store.state.device.animation ? 'zoom' : ''" mode="out-in">
<div v-if="note">
<mk-button v-if="hasNext && !showNext" @click="showNext = true" primary style="margin: 0 auto var(--margin) auto;"><fa :icon="faChevronUp"/></mk-button>
<x-notes v-if="showNext" ref="next" :pagination="next"/>
<hr v-if="showNext"/>
<div v-if="note">
<button class="_panel _button" v-if="hasNext && !showNext" @click="showNext = true" style="margin: 0 auto var(--margin) auto;"><fa :icon="faChevronUp"/></button>
<x-notes v-if="showNext" ref="next" :pagination="next"/>
<hr v-if="showNext"/>
<x-note :note="note" :key="note.id" :detail="true"/>
<div v-if="error">
<mk-error @retry="fetch()"/>
</div>
<mk-button v-if="hasPrev && !showPrev" @click="showPrev = true" primary style="margin: var(--margin) auto 0 auto;"><fa :icon="faChevronDown"/></mk-button>
<hr v-if="showPrev"/>
<x-notes v-if="showPrev" ref="prev" :pagination="prev" style="margin-top: var(--margin);"/>
<mk-remote-caution v-if="note.user.host != null" :href="note.uri" style="margin-bottom: var(--margin)"/>
<x-note :note="note" :key="note.id" :detail="true"/>
<div v-if="error">
<mk-error @retry="fetch()"/>
</div>
</transition>
<button class="_panel _button" v-if="hasPrev && !showPrev" @click="showPrev = true" style="margin: var(--margin) auto 0 auto;"><fa :icon="faChevronDown"/></button>
<hr v-if="showPrev"/>
<x-notes v-if="showPrev" ref="prev" :pagination="prev" style="margin-top: var(--margin);"/>
</div>
</div>
</template>
@@ -29,7 +33,7 @@ import i18n from '../i18n';
import Progress from '../scripts/loading';
import XNote from '../components/note.vue';
import XNotes from '../components/notes.vue';
import MkButton from '../components/ui/button.vue';
import MkRemoteCaution from '../components/remote-caution.vue';
export default Vue.extend({
i18n,
@@ -41,7 +45,7 @@ export default Vue.extend({
components: {
XNote,
XNotes,
MkButton,
MkRemoteCaution,
},
data() {
return {

View File

@@ -0,0 +1,42 @@
<template>
<div>
<portal to="icon"><fa :icon="faBell"/></portal>
<portal to="title">{{ $t('notifications') }}</portal>
<x-notifications @before="before" @after="after" page/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faBell } from '@fortawesome/free-solid-svg-icons';
import Progress from '../scripts/loading';
import XNotifications from '../components/notifications.vue';
export default Vue.extend({
metaInfo() {
return {
title: this.$t('notifications') as string
};
},
components: {
XNotifications
},
data() {
return {
faBell
};
},
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
});
</script>

View File

@@ -0,0 +1,260 @@
<template>
<div>
<portal to="icon"><fa :icon="faCog"/></portal>
<portal to="title">{{ $t('clinetSettings') }}</portal>
<x-theme/>
<section class="_card">
<div class="_title"><fa :icon="faMusic"/> {{ $t('sounds') }}</div>
<div class="_content">
<mk-range v-model="sfxVolume" min="0" max="1" step="0.1">
<fa slot="icon" :icon="volumeIcon"/>
<span slot="title">{{ $t('volume') }}</span>
</mk-range>
</div>
<div class="_content">
<mk-select v-model="sfxNote">
<template #label>{{ $t('_sfx.note') }}</template>
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxNote)" v-if="sfxNote"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
<mk-select v-model="sfxNoteMy">
<template #label>{{ $t('_sfx.noteMy') }}</template>
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxNoteMy)" v-if="sfxNoteMy"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
<mk-select v-model="sfxNotification">
<template #label>{{ $t('_sfx.notification') }}</template>
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxNotification)" v-if="sfxNotification"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
<mk-select v-model="sfxChat">
<template #label>{{ $t('_sfx.chat') }}</template>
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxChat)" v-if="sfxChat"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
<mk-select v-model="sfxChatBg">
<template #label>{{ $t('_sfx.chatBg') }}</template>
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxChatBg)" v-if="sfxChatBg"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
<mk-select v-model="sfxAntenna">
<template #label>{{ $t('_sfx.antenna') }}</template>
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxAntenna)" v-if="sfxAntenna"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
</div>
</section>
<section class="_card">
<div class="_title"><fa :icon="faCog"/> {{ $t('accessibility') }}</div>
<div class="_content">
<mk-switch v-model="autoReload">
{{ $t('autoReloadWhenDisconnected') }}
</mk-switch>
</div>
<div class="_content">
<mk-switch v-model="imageNewTab">{{ $t('openImageInNewTab') }}</mk-switch>
<mk-switch v-model="disableAnimatedMfm">{{ $t('disableAnimatedMfm') }}</mk-switch>
<mk-switch v-model="reduceAnimation">{{ $t('reduceUiAnimation') }}</mk-switch>
<mk-switch v-model="useOsNativeEmojis">
{{ $t('useOsNativeEmojis') }}
<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
</mk-switch>
<mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch>
</div>
<div class="_content">
<mk-select v-model="lang">
<template #label>{{ $t('uiLanguage') }}</template>
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
</mk-select>
</div>
<div class="_content">
<div>{{ $t('fontSize') }}</div>
<mk-radio v-model="fontSize" value="small"><span style="font-size: 14px;">Aa</span></mk-radio>
<mk-radio v-model="fontSize" :value="null"><span style="font-size: 16px;">Aa</span></mk-radio>
<mk-radio v-model="fontSize" value="large"><span style="font-size: 18px;">Aa</span></mk-radio>
<mk-radio v-model="fontSize" value="veryLarge"><span style="font-size: 20px;">Aa</span></mk-radio>
</div>
</section>
<mk-button @click="cacheClear()" primary style="margin: var(--margin) auto;">{{ $t('cacheClear') }}</mk-button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faImage, faCog, faMusic, faPlay, faVolumeUp, faVolumeMute } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue';
import MkSelect from '../../components/ui/select.vue';
import MkRadio from '../../components/ui/radio.vue';
import MkRange from '../../components/ui/range.vue';
import XTheme from './theme.vue';
import i18n from '../../i18n';
import { langs } from '../../config';
const sounds = [
null,
'syuilo/up',
'syuilo/down',
'syuilo/pope1',
'syuilo/pope2',
'syuilo/waon',
'syuilo/popo',
'syuilo/triple',
'syuilo/poi1',
'syuilo/poi2',
'aisha/1',
'aisha/2',
'aisha/3',
'noizenecio/kick_gaba',
];
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('settings') as string
};
},
components: {
XTheme,
MkInput,
MkButton,
MkSwitch,
MkSelect,
MkRadio,
MkRange
},
data() {
return {
langs,
lang: localStorage.getItem('lang'),
fontSize: localStorage.getItem('fontSize'),
sounds,
faImage, faCog, faMusic, faPlay, faVolumeUp, faVolumeMute
}
},
computed: {
autoReload: {
get() { return this.$store.state.device.autoReload; },
set(value) { this.$store.commit('device/set', { key: 'autoReload', value }); }
},
reduceAnimation: {
get() { return !this.$store.state.device.animation; },
set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); }
},
disableAnimatedMfm: {
get() { return !this.$store.state.device.animatedMfm; },
set(value) { this.$store.commit('device/set', { key: 'animatedMfm', value: !value }); }
},
useOsNativeEmojis: {
get() { return this.$store.state.device.useOsNativeEmojis; },
set(value) { this.$store.commit('device/set', { key: 'useOsNativeEmojis', value }); }
},
imageNewTab: {
get() { return this.$store.state.device.imageNewTab; },
set(value) { this.$store.commit('device/set', { key: 'imageNewTab', value }); }
},
showFixedPostForm: {
get() { return this.$store.state.device.showFixedPostForm; },
set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); }
},
sfxVolume: {
get() { return this.$store.state.device.sfxVolume; },
set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value: parseFloat(value, 10) }); }
},
sfxNote: {
get() { return this.$store.state.device.sfxNote; },
set(value) { this.$store.commit('device/set', { key: 'sfxNote', value }); }
},
sfxNoteMy: {
get() { return this.$store.state.device.sfxNoteMy; },
set(value) { this.$store.commit('device/set', { key: 'sfxNoteMy', value }); }
},
sfxNotification: {
get() { return this.$store.state.device.sfxNotification; },
set(value) { this.$store.commit('device/set', { key: 'sfxNotification', value }); }
},
sfxChat: {
get() { return this.$store.state.device.sfxChat; },
set(value) { this.$store.commit('device/set', { key: 'sfxChat', value }); }
},
sfxChatBg: {
get() { return this.$store.state.device.sfxChatBg; },
set(value) { this.$store.commit('device/set', { key: 'sfxChatBg', value }); }
},
sfxAntenna: {
get() { return this.$store.state.device.sfxAntenna; },
set(value) { this.$store.commit('device/set', { key: 'sfxAntenna', value }); }
},
volumeIcon: {
get() {
return this.sfxVolume === 0 ? faVolumeMute : faVolumeUp;
}
}
},
watch: {
lang() {
localStorage.setItem('lang', this.lang);
localStorage.removeItem('locale');
location.reload();
},
fontSize() {
if (this.fontSize == null) {
localStorage.removeItem('fontSize');
} else {
localStorage.setItem('fontSize', this.fontSize);
}
location.reload();
},
},
methods: {
listen(sound) {
const audio = new Audio(`/assets/sounds/${sound}.mp3`);
audio.volume = this.$store.state.device.sfxVolume;
audio.play();
},
cacheClear() {
// Clear cache (service worker)
try {
navigator.serviceWorker.controller.postMessage('clear');
navigator.serviceWorker.getRegistrations().then(registrations => {
for (const registration of registrations) registration.unregister();
});
} catch (e) {
console.error(e);
}
// Force reload
location.reload(true);
}
}
});
</script>

View File

@@ -1,151 +0,0 @@
<template>
<div>
<portal to="icon"><fa :icon="faCog"/></portal>
<portal to="title">{{ $t('clinetSettings') }}</portal>
<x-theme/>
<section class="_card">
<div class="_title"><fa :icon="faCog"/> {{ $t('accessibility') }}</div>
<div class="_content">
<mk-switch v-model="autoReload">
{{ $t('autoReloadWhenDisconnected') }}
</mk-switch>
</div>
<div class="_content">
<mk-switch v-model="imageNewTab">{{ $t('openImageInNewTab') }}</mk-switch>
<mk-switch v-model="disableAnimatedMfm">{{ $t('disableAnimatedMfm') }}</mk-switch>
<mk-switch v-model="reduceAnimation">{{ $t('reduceUiAnimation') }}</mk-switch>
<mk-switch v-model="useOsNativeEmojis">
{{ $t('useOsNativeEmojis') }}
<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
</mk-switch>
<mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch>
</div>
<div class="_content">
<mk-select v-model="lang">
<template #label>{{ $t('uiLanguage') }}</template>
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
</mk-select>
</div>
<div class="_content">
<div>{{ $t('fontSize') }}</div>
<mk-radio v-model="fontSize" value="small"><span style="font-size: 14px;">Aa</span></mk-radio>
<mk-radio v-model="fontSize" :value="null"><span style="font-size: 16px;">Aa</span></mk-radio>
<mk-radio v-model="fontSize" value="large"><span style="font-size: 18px;">Aa</span></mk-radio>
<mk-radio v-model="fontSize" value="veryLarge"><span style="font-size: 20px;">Aa</span></mk-radio>
</div>
</section>
<mk-button @click="cacheClear()" primary style="margin: var(--margin) auto;">{{ $t('cacheClear') }}</mk-button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faImage, faCog } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue';
import MkSelect from '../../components/ui/select.vue';
import MkRadio from '../../components/ui/radio.vue';
import XTheme from './theme.vue';
import i18n from '../../i18n';
import { langs } from '../../config';
export default Vue.extend({
i18n,
metaInfo() {
return {
title: this.$t('settings') as string
};
},
components: {
XTheme,
MkInput,
MkButton,
MkSwitch,
MkSelect,
MkRadio,
},
data() {
return {
langs,
lang: localStorage.getItem('lang'),
fontSize: localStorage.getItem('fontSize'),
faImage, faCog
}
},
computed: {
autoReload: {
get() { return this.$store.state.device.autoReload; },
set(value) { this.$store.commit('device/set', { key: 'autoReload', value }); }
},
reduceAnimation: {
get() { return !this.$store.state.device.animation; },
set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); }
},
disableAnimatedMfm: {
get() { return !this.$store.state.device.animatedMfm; },
set(value) { this.$store.commit('device/set', { key: 'animatedMfm', value: !value }); }
},
useOsNativeEmojis: {
get() { return this.$store.state.device.useOsNativeEmojis; },
set(value) { this.$store.commit('device/set', { key: 'useOsNativeEmojis', value }); }
},
imageNewTab: {
get() { return this.$store.state.device.imageNewTab; },
set(value) { this.$store.commit('device/set', { key: 'imageNewTab', value }); }
},
showFixedPostForm: {
get() { return this.$store.state.device.showFixedPostForm; },
set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); }
},
},
watch: {
lang() {
localStorage.setItem('lang', this.lang);
localStorage.removeItem('locale');
location.reload();
},
fontSize() {
if (this.fontSize == null) {
localStorage.removeItem('fontSize');
} else {
localStorage.setItem('fontSize', this.fontSize);
}
location.reload();
},
},
methods: {
cacheClear() {
// Clear cache (service worker)
try {
navigator.serviceWorker.controller.postMessage('clear');
navigator.serviceWorker.getRegistrations().then(registrations => {
for (const registration of registrations) registration.unregister();
});
} catch (e) {
console.error(e);
}
// Force reload
location.reload(true);
}
}
});
</script>

View File

@@ -3,31 +3,13 @@
<portal to="title" v-if="user"><mk-user-name :user="user" :nowrap="false" class="name"/></portal>
<portal to="avatar" v-if="user"><mk-avatar class="avatar" :user="user" :disable-preview="true"/></portal>
<div class="remote-caution _panel" v-if="user.host != null"><fa :icon="faExclamationTriangle" style="margin-right: 8px;"/>{{ $t('remoteUserCaution') }}<a :href="user.url" rel="nofollow noopener" target="_blank">{{ $t('showOnRemote') }}</a></div>
<transition :name="$store.state.device.animation ? 'zoom' : ''" mode="out-in" appear>
<div class="profile _panel" :key="user.id">
<div class="banner-container" :style="style">
<div class="banner" ref="banner" :style="style"></div>
<div class="fade"></div>
<div class="title">
<mk-user-name class="name" :user="user" :nowrap="true"/>
<div class="bottom">
<span class="username"><mk-acct :user="user" :detail="true" /></span>
<span v-if="user.isAdmin" :title="$t('isAdmin')" style="color: var(--badge);"><fa :icon="faBookmark"/></span>
<span v-if="!user.isAdmin && user.isModerator" :title="$t('isModerator')" style="color: var(--badge);"><fa :icon="farBookmark"/></span>
<span v-if="user.isLocked" :title="$t('isLocked')"><fa :icon="faLock"/></span>
<span v-if="user.isBot" :title="$t('isBot')"><fa :icon="faRobot"/></span>
</div>
</div>
<span class="followed" v-if="$store.getters.isSignedIn && $store.state.i.id != user.id && user.isFollowed">{{ $t('followsYou') }}</span>
<div class="actions" v-if="$store.getters.isSignedIn">
<button @click="menu" class="menu _button" ref="menu"><fa :icon="faEllipsisH"/></button>
<mk-follow-button v-if="$store.state.i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
</div>
</div>
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<mk-remote-caution v-if="user.host != null" :href="user.url" style="margin-bottom: var(--margin)"/>
<div class="profile _panel" :key="user.id">
<div class="banner-container" :style="style">
<div class="banner" ref="banner" :style="style"></div>
<div class="fade"></div>
<div class="title">
<mk-user-name :user="user" :nowrap="false" class="name"/>
<mk-user-name class="name" :user="user" :nowrap="true"/>
<div class="bottom">
<span class="username"><mk-acct :user="user" :detail="true" /></span>
<span v-if="user.isAdmin" :title="$t('isAdmin')" style="color: var(--badge);"><fa :icon="faBookmark"/></span>
@@ -36,55 +18,71 @@
<span v-if="user.isBot" :title="$t('isBot')"><fa :icon="faRobot"/></span>
</div>
</div>
<div class="description">
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
<p v-else class="empty">{{ $t('noAccountDescription') }}</p>
</div>
<div class="fields system">
<dl class="field" v-if="user.location">
<dt class="name"><fa :icon="faMapMarker" fixed-width/> {{ $t('location') }}</dt>
<dd class="value">{{ user.location }}</dd>
</dl>
<dl class="field" v-if="user.birthday">
<dt class="name"><fa :icon="faBirthdayCake" fixed-width/> {{ $t('birthday') }}</dt>
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
</dl>
<dl class="field">
<dt class="name"><fa :icon="faCalendarAlt" fixed-width/> {{ $t('registeredDate') }}</dt>
<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<mk-time :time="user.createdAt"/>)</dd>
</dl>
</div>
<div class="fields" v-if="user.fields.length > 0">
<dl class="field" v-for="(field, i) in user.fields" :key="i">
<dt class="name">
<mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
</dt>
<dd class="value">
<mfm :text="field.value" :author="user" :i="$store.state.i" :custom-emojis="user.emojis" :colored="false"/>
</dd>
</dl>
</div>
<div class="status" v-if="user.host === null">
<router-link :to="user | userPage()" :class="{ active: $route.name === 'user' }">
<b>{{ user.notesCount | number }}</b>
<span>{{ $t('notes') }}</span>
</router-link>
<router-link :to="user | userPage('following')" :class="{ active: $route.name === 'userFollowing' }">
<b>{{ user.followingCount | number }}</b>
<span>{{ $t('following') }}</span>
</router-link>
<router-link :to="user | userPage('followers')" :class="{ active: $route.name === 'userFollowers' }">
<b>{{ user.followersCount | number }}</b>
<span>{{ $t('followers') }}</span>
</router-link>
<span class="followed" v-if="$store.getters.isSignedIn && $store.state.i.id != user.id && user.isFollowed">{{ $t('followsYou') }}</span>
<div class="actions" v-if="$store.getters.isSignedIn">
<button @click="menu" class="menu _button" ref="menu"><fa :icon="faEllipsisH"/></button>
<mk-follow-button v-if="$store.state.i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
</div>
</div>
</transition>
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<mk-user-name :user="user" :nowrap="false" class="name"/>
<div class="bottom">
<span class="username"><mk-acct :user="user" :detail="true" /></span>
<span v-if="user.isAdmin" :title="$t('isAdmin')" style="color: var(--badge);"><fa :icon="faBookmark"/></span>
<span v-if="!user.isAdmin && user.isModerator" :title="$t('isModerator')" style="color: var(--badge);"><fa :icon="farBookmark"/></span>
<span v-if="user.isLocked" :title="$t('isLocked')"><fa :icon="faLock"/></span>
<span v-if="user.isBot" :title="$t('isBot')"><fa :icon="faRobot"/></span>
</div>
</div>
<div class="description">
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
<p v-else class="empty">{{ $t('noAccountDescription') }}</p>
</div>
<div class="fields system">
<dl class="field" v-if="user.location">
<dt class="name"><fa :icon="faMapMarker" fixed-width/> {{ $t('location') }}</dt>
<dd class="value">{{ user.location }}</dd>
</dl>
<dl class="field" v-if="user.birthday">
<dt class="name"><fa :icon="faBirthdayCake" fixed-width/> {{ $t('birthday') }}</dt>
<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
</dl>
<dl class="field">
<dt class="name"><fa :icon="faCalendarAlt" fixed-width/> {{ $t('registeredDate') }}</dt>
<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<mk-time :time="user.createdAt"/>)</dd>
</dl>
</div>
<div class="fields" v-if="user.fields.length > 0">
<dl class="field" v-for="(field, i) in user.fields" :key="i">
<dt class="name">
<mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/>
</dt>
<dd class="value">
<mfm :text="field.value" :author="user" :i="$store.state.i" :custom-emojis="user.emojis" :colored="false"/>
</dd>
</dl>
</div>
<div class="status">
<router-link :to="user | userPage()" :class="{ active: $route.name === 'user' }">
<b>{{ user.notesCount | number }}</b>
<span>{{ $t('notes') }}</span>
</router-link>
<router-link :to="user | userPage('following')" :class="{ active: $route.name === 'userFollowing' }">
<b>{{ user.followingCount | number }}</b>
<span>{{ $t('following') }}</span>
</router-link>
<router-link :to="user | userPage('followers')" :class="{ active: $route.name === 'userFollowers' }">
<b>{{ user.followersCount | number }}</b>
<span>{{ $t('followers') }}</span>
</router-link>
</div>
</div>
<router-view :user="user"></router-view>
<template v-if="$route.name == 'user'">
<sequential-entrance class="pins">
<x-note v-for="(note, i) in user.pinnedNotes" class="note" :note="note" :key="note.id" :detail="true" :pinned="true"/>
</sequential-entrance>
<div class="pins">
<x-note v-for="note in user.pinnedNotes" class="note" :note="note" :key="note.id" :detail="true" :pinned="true"/>
</div>
<mk-container :body-togglable="true" class="content">
<template #header><fa :icon="faImage"/>{{ $t('images') }}</template>
<div>
@@ -107,7 +105,7 @@
<script lang="ts">
import Vue from 'vue';
import { faEllipsisH, faRobot, faLock, faBookmark, faExclamationTriangle, faChartBar, faImage, faBirthdayCake, faMapMarker } from '@fortawesome/free-solid-svg-icons';
import { faEllipsisH, faRobot, faLock, faBookmark, faChartBar, faImage, faBirthdayCake, faMapMarker } from '@fortawesome/free-solid-svg-icons';
import { faCalendarAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
import * as age from 's-age';
import XUserTimeline from './index.timeline.vue';
@@ -115,6 +113,7 @@ import XUserMenu from '../../components/user-menu.vue';
import XNote from '../../components/note.vue';
import MkFollowButton from '../../components/follow-button.vue';
import MkContainer from '../../components/ui/container.vue';
import MkRemoteCaution from '../../components/remote-caution.vue';
import Progress from '../../scripts/loading';
import parseAcct from '../../../misc/acct/parse';
@@ -124,6 +123,7 @@ export default Vue.extend({
XNote,
MkFollowButton,
MkContainer,
MkRemoteCaution,
XPhotos: () => import('./index.photos.vue').then(m => m.default),
XActivity: () => import('./index.activity.vue').then(m => m.default),
},
@@ -139,7 +139,7 @@ export default Vue.extend({
user: null,
error: null,
parallaxAnimationId: null,
faEllipsisH, faRobot, faLock, faBookmark, farBookmark, faExclamationTriangle, faChartBar, faImage, faBirthdayCake, faMapMarker, faCalendarAlt
faEllipsisH, faRobot, faLock, faBookmark, farBookmark, faChartBar, faImage, faBirthdayCake, faMapMarker, faCalendarAlt
};
},
@@ -217,17 +217,6 @@ export default Vue.extend({
<style lang="scss" scoped>
.mk-user-page {
> .remote-caution {
font-size: 0.8em;
padding: 16px;
margin-bottom: var(--margin);
> a {
margin-left: 4px;
color: var(--accent);
}
}
> .profile {
position: relative;
margin-bottom: var(--margin);

View File

@@ -27,6 +27,7 @@ export const router = new VueRouter({
{ path: '/explore', component: page('explore') },
{ path: '/explore/tags/:tag', props: true, component: page('explore') },
{ path: '/search', component: page('search') },
{ path: '/my/notifications', component: page('notifications') },
{ path: '/my/favorites', component: page('favorites') },
{ path: '/my/messages', component: page('messages') },
{ path: '/my/mentions', component: page('mentions') },
@@ -45,7 +46,7 @@ export const router = new VueRouter({
{ path: '/my/groups', component: page('my-groups/index') },
{ path: '/my/groups/:group', component: page('my-groups/group') },
{ path: '/my/antennas', component: page('my-antennas/index') },
{ path: '/settings', component: page('settings/index') },
{ path: '/preferences', component: page('preferences/index') },
{ path: '/instance', component: page('instance/index') },
{ path: '/instance/emojis', component: page('instance/emojis') },
{ path: '/instance/users', component: page('instance/users') },

View File

@@ -12,14 +12,22 @@ type action = {
patterns: pattern[];
callback: Function;
allowRepeat: boolean;
};
const getKeyMap = keymap => Object.entries(keymap).map(([patterns, callback]): action => {
const result = {
patterns: [],
callback: callback
callback: callback,
allowRepeat: true
} as action;
if (patterns.match(/^\(.*\)$/) !== null) {
result.allowRepeat = false;
patterns = patterns.slice(1, -1);
}
result.patterns = patterns.split('|').map(part => {
const pattern = {
which: [],
@@ -77,6 +85,7 @@ export default {
const matched = match(e, action.patterns);
if (matched) {
if (!action.allowRepeat && e.repeat) return;
if (el._hotkey_global && match(e, targetReservedKeys)) return;
e.preventDefault();

View File

@@ -62,6 +62,7 @@ export default (opts) => ({
},
async init() {
this.queue = [];
this.fetching = true;
if (opts.before) opts.before(this);
let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;

View File

@@ -40,6 +40,13 @@ const defaultDeviceSettings = {
animatedMfm: true,
imageNewTab: false,
showFixedPostForm: false,
sfxVolume: 0.3,
sfxNote: 'syuilo/down',
sfxNoteMy: 'syuilo/up',
sfxNotification: 'syuilo/pope2',
sfxChat: 'syuilo/pope1',
sfxChatBg: 'syuilo/waon',
sfxAntenna: 'syuilo/triple',
userData: {},
};
@@ -93,6 +100,7 @@ export default (os: MiOS) => new Vuex.Store({
ctx.commit('settings/init', {});
ctx.commit('deviceUser/init', {});
localStorage.removeItem('i');
document.cookie = `igi=; path=/`;
},
async switchAccount(ctx, i) {

View File

@@ -3,7 +3,7 @@
:root {
--radius: 8px;
--marginFull: 16px;
--marginHalf: 8px;
--marginHalf: 10px;
--margin: var(--marginFull);
@@ -18,6 +18,7 @@
}
html {
touch-action: manipulation;
background-color: var(--bg);
background-attachment: fixed;
background-size: cover;
@@ -199,6 +200,7 @@ hr {
._button {
appearance: none;
padding: 0;
margin: 0; // for Safari
background: none;
border: none;
cursor: pointer;
@@ -228,7 +230,6 @@ hr {
@extend ._button;
color: #fff;
background: var(--accent);
box-shadow: 0 6px 16px var(--accentShadow);
&:not(:disabled):hover {
background: var(--jkhztclx);
@@ -274,23 +275,29 @@ hr {
}
}
._shadow {
box-shadow: 0 8px 32px var(--shadow);
@media (max-width: 700px) {
box-shadow: 0 4px 16px var(--shadow);
}
@media (max-width: 500px) {
box-shadow: 0 2px 8px var(--shadow);
}
}
._panel {
@extend ._shadow;
position: relative;
background: var(--panel);
border-radius: var(--radius);
box-shadow: 0 0 0 1px var(--divider);
}
main ._panel {
border-radius: 0;
box-shadow: 0 1px 0 0 var(--divider), 0 -1px 0 0 var(--divider);
}
._panel ._panel {
border-radius: 0;
box-shadow: 0 1px 0 0 var(--divider), 0 -1px 0 0 var(--divider);
}
._panel._button {
display: flex;
width: 100%;
min-height: 48px;
align-items: center;
justify-content: center;
}
._card {

View File

@@ -17,9 +17,10 @@
fgHighlighted: ':lighten<3<@fg',
html: '@bg',
indicator: '@accent',
panel: '#111213',
panel: '#000',
shadow: 'rgba(0, 0, 0, 0.1)',
header: 'rgba(20, 20, 20, 0.75)',
pageBg: ':lighten<5<@bg',
navBg: '@panel',
navFg: '@fg',
navHoverFg: ':lighten<17<@fg',
@@ -33,8 +34,7 @@
divider: 'rgba(255, 255, 255, 0.1)',
scrollbarHandle: 'rgba(255, 255, 255, 0.2)',
scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)',
dateLabelBg: 'rgba(255, 255, 255, 0.08)',
dateLabelFg: '#fff',
dateLabelFg: '@fg',
infoBg: '#253142',
infoFg: '#fff',
infoWarnBg: '#42321c',
@@ -51,14 +51,13 @@
driveFolderBg: ':alpha<0.3<@accent',
wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
badge: '#31b1ce',
messageBg: ':lighten<5<@bg',
bonzsgfz: ':alpha<0<@bg',
pcncwizz: ':darken<2<@panel',
vocsgcxy: 'rgba(0, 0, 0, 0.5)',
yrnqrguo: 'rgba(255, 255, 255, 0.05)',
nwjktjjq: 'rgba(255, 255, 255, 0.1)',
geavgsxy: 'rgba(255, 255, 255, 0.05)',
nhzhphzx: 'rgba(255, 255, 255, 0.15)',
tyvedwbe: 'rgba(0, 0, 0, 0.5)',
bwqtlupy: 'rgba(255, 255, 255, 0.05)',
jkhztclx: ':lighten<5<@accent',
zbqjwygh: ':darken<5<@accent',

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