Compare commits

...

146 Commits

Author SHA1 Message Date
7f0084a7ed Merge branch 'develop' 2020-03-22 10:59:27 +09:00
1374a12f89 12.23.0 2020-03-22 10:59:10 +09:00
92937b8b3c Update theme.vue 2020-03-22 10:57:58 +09:00
d0ed5a8b59 🎨 2020-03-22 10:55:46 +09:00
2b318a1021 Fix error 2020-03-22 10:53:47 +09:00
903e93ae01 i18n (#6171)
* i18n

Resolve #6155

* i18n for drive

* ✌️

* Extract doc

Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
2020-03-22 10:51:40 +09:00
06c7fe669c Update ja-JP.yml 2020-03-22 10:41:25 +09:00
e2c0ee66e4 Resolve #6170 2020-03-22 10:39:12 +09:00
c6c62f956b 🎨 2020-03-22 09:44:02 +09:00
8fa27fea6c Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-03-22 09:43:32 +09:00
03c56f388c 🎨 2020-03-22 09:43:19 +09:00
4090922e7e CI通らないの修正 (#6173)
* CI通らないの修正

* Revert "CI通らないの修正"

This reverts commit 3eab7eab90050d60f0222ac0d08da7be0a53c300.

* ts-ignore

* んー
2020-03-22 09:21:21 +09:00
8061dedba1 Update collaborators 2020-03-21 23:33:39 +09:00
da2112b659 基底テーマ分離 2020-03-21 23:28:17 +09:00
ed5386771a 招待コードが発行できない問題を修正 2020-03-21 23:27:54 +09:00
d248828523 Update CHANGELOG.md 2020-03-21 22:32:58 +09:00
fc0b86fe19 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-03-21 20:14:41 +09:00
346036ec88 Fix #6110
updatedAt が null なユーザーは検索に出てこなくなる副作用あり
2020-03-21 20:14:26 +09:00
a5e1ce0e0b Add 削除して編集 (#6147)
* Add 削除して編集

* 言語ファイルを更新

* リプライとリノートも復元する

* Revert "リプライとリノートも復元する"

This reverts commit f23f7f8d784b28f3d365c8f5aecf81001577c672.

* リプライとリノートも復元する
2020-03-21 18:58:05 +09:00
67b39b1a98 New Crowdin translations (#6062)
* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

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

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

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

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

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

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

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

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

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

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

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

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

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

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

* New translations ja-JP.yml (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 (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)
2020-03-21 15:56:07 +09:00
6fb7721798 Merge branch 'develop' 2020-03-21 13:36:41 +09:00
0018fd469e 12.22.0 2020-03-21 13:36:21 +09:00
019f7480e8 Fix bug 2020-03-21 13:21:32 +09:00
8e0b088deb 🎨 2020-03-21 13:19:42 +09:00
452005381f Fix bug 2020-03-21 13:10:44 +09:00
9722ed99a3 リモートユーザーでも投稿数とか見れるように 2020-03-21 13:07:15 +09:00
ee6311e83d Resolve #6145 2020-03-21 13:07:02 +09:00
1471e52307 Resolve #6110 2020-03-21 12:48:25 +09:00
f1fc12d9cc wip 2020-03-21 12:32:40 +09:00
ebbc42bebc wip 2020-03-21 00:21:33 +09:00
4785ee8c32 wip 2020-03-20 23:08:45 +09:00
ab40756c1a wip 2020-03-20 22:42:35 +09:00
c88e737a84 wip 2020-03-20 22:37:44 +09:00
def5ea7978 wip 2020-03-20 21:58:04 +09:00
e69ab45044 wip 2020-03-20 19:19:28 +09:00
25d0b4bbf1 wip 2020-03-20 19:06:50 +09:00
f86f5ac6cc wip 2020-03-20 18:58:17 +09:00
07ce365bfd wip 2020-03-20 18:55:15 +09:00
f31c94e2ea wip 2020-03-20 18:11:39 +09:00
933638d035 Fix bug 2020-03-20 18:00:42 +09:00
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
5bbd4ae703 ElasticSearchで認証ができるように (#6158) 2020-03-20 14:00:34 +09:00
f2f7f532a0 use username if name was empty (#6166) 2020-03-20 13:57:55 +09:00
80eedf7449 連携ログインができないのなどを修正 (#6162)
* 連携ログインができないのを修正

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

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

* 2回目以降Discordログインできなくなるのを修正
2020-03-20 13:56:22 +09:00
1b48e0d6e0 Revert "Update dependencies (#6167)" (#6168)
This reverts commit 0420c548da.
2020-03-20 02:46:13 +09:00
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
6e98b75d13 Merge pull request #6165 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-03-19 12:54:00 +09:00
e772cb00d1 Update README.md [AUTOGEN] 2020-03-18 03:57:08 +09:00
b5d5275e9b Auth認証画面から正常にログインできるよう修正 (#6154)
* Fix #6095

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

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

* 表記揺れ

* more simply

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

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

* Revert "Disable Nyaize in quote"

This reverts commit 1b238905a5535267d32d7e1aec8afd8bb07b0619.

* refactor

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

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

* lint アクションを追加

* yarn lint --fix

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

* parseFloat
2020-02-27 16:32:10 +09:00
484dc9b08a Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-27 15:13:50 +09:00
3bd827d7da Update google.vue 2020-02-27 15:13:46 +09:00
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
969cd16638 Resolve #6091 2020-02-27 07:04:28 +09:00
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
03f54c5b02 リアクション絵文字設定をいい感じに (#6074)
* リアクション絵文字設定をいい感じに

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

* Add pg healthcheck

* postgres:10.8

* 試しにhealthcheckなしに

* postgres:10

* Revert "試しにhealthcheckなしに"

This reverts commit 4a7ba19ea9b93d54966f256f8f04090482b9005d.

* は?

* postgres:10.8-alpine

* postgres:10.11-alpine

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

* Update count-same-renotes.ts

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-02-26 07:54:35 +09:00
241769d6fc Merge pull request #6084 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-25 17:47:38 +09:00
7727651871 Update README.md [AUTOGEN] 2020-02-25 17:46:07 +09:00
ce331826ac Merge pull request #6078 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-24 09:52:25 +09:00
ae92c52d61 Update README.md [AUTOGEN] 2020-02-24 09:28:06 +09:00
54aef5fe6f Update CHANGELOG.md 2020-02-23 03:11:51 +09:00
d22148b418 12.21.0 2020-02-23 02:53:44 +09:00
5dc75c9cea Fix #6029 2020-02-23 02:34:54 +09:00
200e82decb Fix #6063 2020-02-23 02:28:07 +09:00
50359dbaf4 Resolve #6053 2020-02-22 06:57:54 +09:00
7165f21a62 Fix style 2020-02-22 06:54:35 +09:00
8aab828c65 Better featured injection 2020-02-22 06:49:12 +09:00
c9f8c12f5b 🍕 2020-02-22 06:43:46 +09:00
a347f8fa49 🎨 2020-02-22 06:40:48 +09:00
2d76bdd0f8 Fix bug 2020-02-22 06:36:15 +09:00
c5cdd56edb 🎨 2020-02-22 03:51:31 +09:00
6901ab39ed Merge pull request #6058 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-22 03:02:58 +09:00
b851b7f431 12.20.0 2020-02-22 02:38:37 +09:00
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
813de15e85 🎨 2020-02-22 02:30:41 +09:00
fa33181fa9 Update particle.vue 2020-02-22 02:26:01 +09:00
d4324dc0cb Reaction particle 2020-02-22 01:20:58 +09:00
ccc27bcc14 Fix #6057 (#6061) 2020-02-22 01:03:50 +09:00
014c1673c6 Update README.md [AUTOGEN] 2020-02-21 22:39:06 +09:00
3a3319ff52 Merge pull request #6056 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-21 20:11:43 +09:00
5b54ec8fb5 Update README.md [AUTOGEN] 2020-02-21 18:37:07 +09:00
e690556286 patch #6039 (#6052) 2020-02-21 17:16:51 +09:00
660956917f Merge pull request #6034 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-02-21 11:18:52 +09:00
3a5201747b 🎨 2020-02-21 09:17:33 +09:00
b338e8a83f 🎨 2020-02-21 09:11:35 +09:00
5584d56b6a Clean up 2020-02-21 08:36:18 +09:00
c925498120 Improve usability 2020-02-21 07:21:27 +09:00
75615cf503 Update style.scss 2020-02-21 07:11:25 +09:00
39f708b0fc 複数タブで開いてるときに動作がおかしい問題を修正 2020-02-21 03:51:41 +09:00
ac32077221 12.19.0 2020-02-21 00:36:17 +09:00
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
c7c08b7511 Resolve #6043 2020-02-21 00:28:45 +09:00
7de915d47b Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-02-20 23:27:48 +09:00
9107547501 Update CHANGELOG.md 2020-02-20 23:27:32 +09:00
95dc76ca19 Fix comments 2020-02-20 23:26:35 +09:00
49c2a9b372 ボリュームが0のときサウンドを鳴らさないように 2020-02-20 23:17:17 +09:00
b378cabfc7 Fix bug 2020-02-20 23:11:09 +09:00
4263dbef31 Fix #6026 2020-02-20 23:07:20 +09:00
32fc6ae2eb Fix bug 2020-02-20 23:07:03 +09:00
238cb0077f Fix bug 2020-02-20 23:02:55 +09:00
f5a06b6494 Fix #6036 2020-02-20 22:54:26 +09:00
2c01329085 Update README.md [AUTOGEN] 2020-02-20 14:10:06 +09:00
502de89ab1 12.18.1 2020-02-20 13:41:28 +09:00
128de6750c New translations ja-JP.yml (Spanish) (#6027) 2020-02-20 13:41:16 +09:00
e59e2d9f0b Resolve #6028 2020-02-20 13:38:40 +09:00
2504b8391b Better validation 2020-02-20 13:33:41 +09:00
330ea7d210 12.18.0 2020-02-20 07:30:43 +09:00
1edd173a29 Add sounds 2020-02-20 07:29:34 +09:00
98d873a7f9 Update search-by-tag.ts 2020-02-20 07:19:27 +09:00
09175b84df Fix #6016 2020-02-20 07:18:40 +09:00
177e19632a Fix #6016 2020-02-20 07:18:16 +09:00
8e6207f3e9 Remove header transition 2020-02-20 06:42:20 +09:00
ff3a97f6cf Fix #5943 2020-02-20 06:38:19 +09:00
b8e155ab40 🎨 2020-02-20 06:08:54 +09:00
b8e7df198d Improve sound 2020-02-20 06:08:49 +09:00
168 changed files with 3328 additions and 1088 deletions

View File

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

View File

@ -21,6 +21,7 @@ jobs:
- 5432:5432 - 5432:5432
env: env:
POSTGRES_DB: test-misskey POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis: redis:
image: redis:alpine image: redis:alpine
ports: ports:
@ -40,3 +41,13 @@ jobs:
run: yarn build run: yarn build
- name: Test - name: Test
run: yarn 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,83 @@
ChangeLog ChangeLog
========= =========
12.22.0 (2020/02/23)
-------------------
### ✨Improvements
* Web UI のデザインを大幅に変更
* 通知のポップアップ表示を廃止
* リモートユーザーであっても投稿数、フォロー・フォロワー数を表示
* リモートユーザーであるという警告を投稿ページでも表示
* ユーザー名のサジェストをアクティブであるユーザー順に表示
* 通知音の音量スライダーなど、スライダーへのデザイン適用
* リアクション設定機能を刷新
* 同じホットキーが連続で発動しないように
* 同じノートを何回リノートしても一回として数えるように
* ElasticSearch で認証ができるように
### 🐛Fixes
* ユーザー名を設定していないユーザーのノートページのタイトルが「のノート」になる問題を修正
* ソーシャルアカウントでの連携ログインができない問題を修正
* アプリ認証画面でログインしたときに正常に遷移できない問題を修正
* オブジェクトストレージでS3のvirtual-host形式のサポートなど
* GIF 画像のバッジの色合いを修正
* ートのテキストがMFMの使い方によってははみ出る問題を修正
* ダークテーマ利用時にセレクトが使いにくくなる問題を修正
* ボリュームを0にしてもサウンドが鳴動している問題を修正
* 検索窓のスタイルが適用されていなかった問題を修正
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) 12.17.0 (2020/02/20)
------------------- -------------------
### ✨Improvements ### ✨Improvements

View File

@ -89,6 +89,9 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://avatars1.githubusercontent.com/u/30769358?s=460&v=4" alt="mei23" width="100"></td> <td><img src="https://avatars1.githubusercontent.com/u/30769358?s=460&v=4" alt="mei23" width="100"></td>
<td><img src="https://avatars2.githubusercontent.com/u/20679825?s=460&v=4" alt="acid-chicken" width="100"></td> <td><img src="https://avatars2.githubusercontent.com/u/20679825?s=460&v=4" alt="acid-chicken" width="100"></td>
<td><img src="https://avatars2.githubusercontent.com/u/6533808?s=460&v=4" alt="rinsuki" width="100"></td> <td><img src="https://avatars2.githubusercontent.com/u/6533808?s=460&v=4" alt="rinsuki" width="100"></td>
<td><img src="https://avatars0.githubusercontent.com/u/7973572?s=460&v=4" alt="tamaina" width="100"></td>
<td><img src="https://avatars1.githubusercontent.com/u/7106976?s=460&v=4" alt="Xeltica" width="100"></td>
<td><img src="https://avatars1.githubusercontent.com/u/17376330?s=460&v=4" alt="u1-liquid" width="100"></td>
</tr> </tr>
<tr> <tr>
<td align="center"><a href="https://github.com/syuilo">@syuilo</a></td> <td align="center"><a href="https://github.com/syuilo">@syuilo</a></td>
@ -96,6 +99,9 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td align="center"><a href="https://github.com/mei23">@mei23</a></td> <td align="center"><a href="https://github.com/mei23">@mei23</a></td>
<td align="center"><a href="https://github.com/acid-chicken">@acid-chicken</a></td> <td align="center"><a href="https://github.com/acid-chicken">@acid-chicken</a></td>
<td align="center"><a href="https://github.com/rinsuki">@rinsuki</a></td> <td align="center"><a href="https://github.com/rinsuki">@rinsuki</a></td>
<td align="center"><a href="https://github.com/tamaina">@tamaina</a></td>
<td align="center"><a href="https://github.com/Xeltica">@Xeltica</a></td>
<td align="center"><a href="https://github.com/u1-liquid">@u1-liquid</a></td>
</tr> </tr>
</table> </table>
@ -110,7 +116,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/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/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://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> </tr><tr>
<td><a href="https://www.patreon.com/user?u=20832595">Roujo</a></td> <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> <td><a href="https://www.patreon.com/user?u=27956229">Oliver Maximilian Seidel</a></td>
@ -119,9 +124,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=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/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/Nesakko">Nesakko</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
</tr></table> </tr></table>
<table><tr> <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://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://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> <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 +135,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://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/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/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> </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/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=557245">mkatze</a></td>
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y</a></td> <td><a href="https://www.patreon.com/user?u=23915207">kabo2468y</a></td>
@ -141,56 +145,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/user?u=16869916">見当かなみ</a></td>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</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/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> </tr></table>
<table><tr> <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/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://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/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/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/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/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/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/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> </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=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/user?u=17866454">sikyosyounin</a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</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=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=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=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/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/takimura">takimura</a></td>
<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
</tr></table> </tr></table>
<table><tr> <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/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/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/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/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/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/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> </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=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/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/noellabo">noellabo</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/Corset">CG</a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic</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/user?u=24641572">uroco @99</a></td>
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
</tr></table> </tr></table>
<table><tr> <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://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://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> </tr><tr>
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td> <td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
<td><a href="https://www.patreon.com/user?u=23932002">nenohi</a></td> <td><a href="https://www.patreon.com/user?u=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> </tr></table>
**Last updated:** Wed, 05 Feb 2020 00:42:12 UTC **Last updated:** Tue, 17 Mar 2020 18:57:08 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
[backer-url]: #backers [backer-url]: #backers

View File

@ -28,7 +28,10 @@ users: "Benutzer"
addUser: "Benutzer hinzufügen" addUser: "Benutzer hinzufügen"
favorite: "Favoriten" favorite: "Favoriten"
favorites: "Favoriten" favorites: "Favoriten"
unfavorite: "Aus Favoriten entfernen"
pin: "Anheften" pin: "Anheften"
unpin: "Lösen"
copyContent: "Inhalt kopieren"
copyLink: "Link kopieren" copyLink: "Link kopieren"
delete: "Löschen" delete: "Löschen"
addToList: "Zur Liste hinzufügen" addToList: "Zur Liste hinzufügen"
@ -40,18 +43,655 @@ youGotNewFollower: "Sie haben einen neuen Follower"
receiveFollowRequest: "Follow Request erhalten." receiveFollowRequest: "Follow Request erhalten."
followRequestAccepted: "FollowRequestAkzeptiert" followRequestAccepted: "FollowRequestAkzeptiert"
mentions: "Erwähnungen" mentions: "Erwähnungen"
directNotes: "Direktnachrichten"
importAndExport: "Importieren und Exportieren"
import: "Importieren"
export: "Exportieren"
files: "Dateien"
download: "Download"
driveFileDeleteConfirm: "Möchtest du die Datei \"{name}\" löschen? Die zugehörige Notiz wird ebenso verschwinden."
unfollowConfirm: "Möchtest du {name} nicht mehr folgen?"
lists: "Listen"
noLists: "Keine Liste!"
note: "Notiz"
notes: "Notizen"
following: "Folgen"
followers: "Folgende"
followsYou: "Folgt dir"
createList: "Liste erstellen"
manageLists: "Liste verwalten"
error: "Ein Problem ist aufgetreten"
retry: "Wiederholen"
enterListName: "Listennamen eingeben"
privacy: "Privatsphäre"
makeFollowManuallyApprove: "Folgeanfragen benötigen Bestätigung"
defaultNoteVisibility: "Die Standardsichtbarkeit"
follow: "Folgen"
followRequest: "Follower-Anfragen"
followRequests: "Follower-Anfragen"
unfollow: "Nicht mehr folgen"
followRequestPending: "Ausstehend"
enterEmoji: "Gib ein Emoji ein"
renote: "Renote"
unrenote: "Renote zurücknehmen"
quote: "Zitieren"
pinnedNote: "Angepinnte Notiz"
you: "Du"
clickToShow: "Klicke zum den Inhalt anzusehen"
sensitive: "Dieser Inhalt ist NSFW"
add: "Hinzufügen"
reaction: "Reaktionen"
reactionSettingDescription: "Weisen Sie Ihre lieblings reaktionen zu, die Sie in den Reaktionenswähler stecken möchten."
rememberNoteVisibility: "Notizsichtbarkeit merken"
renameFile: "Datei umbenennen"
attachCancel: "Anhängen abbrechen"
markAsSensitive: "Als sensitiv markieren"
unmarkAsSensitive: "Markierung als sensitiv zurücknehmen"
enterFileName: "Dateinamen eingeben"
mute: "Stummschalten"
unmute: "Stummschaltung aufheben"
block: "Blockieren"
unblock: "Blockierung aufheben"
suspend: "Sperren"
unsuspend: "Sperrung aufheben"
blockConfirm: "Möchtest du diesen Account wirklich blockieren?"
unblockConfirm: "Möchtest du diese Blockierung wirklich aufheben?"
suspendConfirm: "Möchtest du diesen Account wirklich sperren?"
unsuspendConfirm: "Möchtest du die Sperrung dieses Accounts wirklich aufheben?"
selectList: "Wähle eine Liste aus"
customEmojis: "Benutzerdefinierte Emojis"
emojiName: "Emojiname"
emojiUrl: "Emoji-URL"
addEmoji: "Emoji hinzufügen"
flagAsBot: "Als Bot markieren"
flagAsCat: "Als Katze markieren"
autoAcceptFollowed: "Folgeanfragen automatisch akzeptieren"
addAcount: "Benutzerkonto hinzufügen"
loginFailed: "Login fehlgeschlagen"
general: "Allgemein"
wallpaper: "Hintergrund"
removeWallpaper: "Hintergrund entfernen"
searchWith: "Suche: {q}"
youHaveNoLists: "Du hast keine Listen"
followConfirm: "Möchtest du {name} wirklich folgen?"
selectUser: "Benutzer wählen" selectUser: "Benutzer wählen"
recipient: "Empfänger"
annotation: "Anmerkung"
federation: "Föderation"
instances: "Instanz" instances: "Instanz"
latestStatus: "Neuester Status"
storageUsage: "Speicherplatzverbrauch"
charts: "Charts"
perHour: "Pro Stunde"
perDay: "Pro Tag"
blockThisInstance: "Diese Instanz blockieren"
version: "Version"
metadata: "Metadaten"
withNFiles: "{n} Datei(en)"
jobQueue: "Job-Warteschlange"
cpuAndMemory: "CPU und Arbeitsspeicher"
network: "Netzwerk"
disk: "Festplatte"
instanceInfo: "Instanzinformationen"
statistics: "Statistiken"
clearQueue: "Warteschlange leeren"
clearQueueConfirmTitle: "Möchtest du die Warteschlange wirklich leeren?"
clearQueueConfirmText: "Jegliche Notizen, die sich noch in der Warteschlange befinden, werden hierdurch nicht föderiert. Diese Aktion wird normalerweise NICHT benötigt."
clearCachedFiles: "Cache leeren"
blockedInstances: "Blockierte Instanzen"
blockedInstancesDescription: "Gib den Hostnamen der Instanz an, die blockiert werden soll. Blockierte Instanzen können nicht mehr mit dieser kommunizieren."
muteAndBlock: "Stummgeschaltet / Blockiert"
mutedUsers: "Stummgestellte Benutzer" mutedUsers: "Stummgestellte Benutzer"
blockedUsers: "Blockierte Benutzer" blockedUsers: "Blockierte Benutzer"
noUsers: "Keine Benutzer" noUsers: "Keine Benutzer"
editProfile: "Profil bearbeiten"
noteDeleteConfirm: "Möchtest du diese Notiz wirklich löschen?"
pinLimitExceeded: "Du kannst nicht noch mehr Notizen anpinnen."
intro: "Misskey Installation abgeschlossen! Lass uns nun ein Administratorkonto erstellen."
done: "Fertig"
processing: "In Bearbeitung"
preview: "Vorschau"
default: "Standard"
noCustomEmojis: "Es existieren keine Emojis"
noJobs: "Es gibt keine Jobs"
federating: "Föderiert"
blocked: "Blockiert"
suspended: "Gesperrt"
all: "Alles"
changePassword: "Passwort ändern"
security: "Sicherheit"
retypedNotMatch: "Eingaben stimmen nicht überein."
currentPassword: "Momentanes Passwort"
newPassword: "Neues Passwort"
newPasswordRetype: "Neues Passwort (wiederholen)"
attachFile: "Datei anhängen"
more: "Mehr!"
featured: "Hervorgehoben"
usernameOrUserId: "Benutzername oder Benutzer-ID"
noSuchUser: "Benutzer nicht gefunden"
announcements: "Ankündigungen"
imageUrl: "Bild-URL"
remove: "Löschen" remove: "Löschen"
removed: "Erfolgreich gelöscht"
removeAreYouSure: "Möchtest du \"{x}\" wirklich löschen?"
saved: "Gespeichert"
messaging: "Nachrichten"
upload: "Hochladen"
fromDrive: "Aus Drive"
fromUrl: "Von einer URL"
explore: "Erkunden"
games: "Misskey Spiele"
messageRead: "Gelesen"
recentUsedEmojis: "Kürzlich genutzte Emojis"
noMoreHistory: "Kein weiterer Verlauf vorhanden"
nUsersRead: "Von {n} gelesen"
agreeTo: "Ich stimme {0} zu"
tos: "Nutzungsbedingungen"
start: "Anfangen"
home: "Startseite"
activity: "Aktivität"
images: "Bilder"
birthday: "Geburtstag"
yearsOld: "{age} Jahre alt"
registeredDate: "Registierdatum"
location: "Ort"
theme: "Farbthemen"
lightThemes: "Helle Farbthemen"
darkThemes: "Dunkle Farbthemen"
drive: "Drive"
selectFile: "Datei auswählen"
selectFiles: "Dateien auswählen"
renameFolder: "Ordner umbenennen"
createFolder: "Ordner erstellen"
deleteFolder: "Ordner löschen"
addFile: "Datei hinzufügen"
emptyDrive: "Drive ist leer"
emptyFolder: "Der Ordner ist leer"
copyUrl: "URL kopieren"
rename: "Umbenennen"
avatar: "Profilbild"
banner: "Banner"
nsfw: "Dieser Inhalt ist NSFW"
disconnectedFromServer: "Verbindung zum Server wurde getrennt"
reload: "Aktualisieren"
doNothing: "Ignorieren"
watch: "Beobachten"
unwatch: "Nicht mehr beobachten"
accept: "Akzeptieren"
reject: "Ablehnen"
instanceName: "Name der Instanz"
instanceDescription: "Beschreibung der Instanz"
maintainerName: "Betreiber"
maintainerEmail: "Betreiberemail"
tosUrl: "URL der Nutzungsbedingungen"
thisYear: "Dieses Jahr"
thisMonth: "Dieser Monat"
today: "Heute"
pages: "Seiten"
integration: "Integration"
connectSerice: "Verbinden"
disconnectSerice: "Trennen"
registration: "Registrieren"
enableRegistration: "Registration neuer Benutzer erlauben"
invite: "Einladen"
driveCapacityPerLocalAccount: "Drivekapazität pro lokales Benutzerkonto"
inMb: "In Megabytes"
iconUrl: "Icon-URL"
bannerUrl: "Banner-URL"
basicInfo: "Basisdaten"
pinnedUsers: "Angepinnte Benutzer"
pinnedUsersDescription: "Gib einen Benutzernamen pro Zeile ein. Diese werden im \"Erkunden\" Tab angezeigt."
recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA aktivieren"
antennas: "Antennen"
manageAntennas: "Antennen verwalten"
name: "Name"
antennaSource: "Antennenquelle"
antennaKeywords: "Schlüsselwörter, die beobachtet werden sollen"
antennaExcludeKeywords: "Schlüsselwörter, die ignoriert werden sollen"
antennaKeywordsDescription: "Mit Leerzeichen für eine \"UND\"-Verknüpfung trennen, durch Zeilenumbrüche für eine \"ODER\"-Verknüpfung trennen."
serviceworker: "ServiceWorker"
enableServiceworker: "ServiceWorker aktivieren"
antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben"
caseSensitive: "Groß-/Kleinschreibung unterscheiden"
withReplies: "Antworten beinhalten"
connectedTo: "Mit folgenden Benutzerkonten verknüpft"
notesAndReplies: "Notizen und Antworten"
withFiles: "Dateien beinhalten"
popularUsers: "Beliebte Benutzer"
recentlyUpdatedUsers: "Vor kurzem aktive Benutzer"
recentlyRegisteredUsers: "Vor kurzem registrierte Benutzer"
recentlyDiscoveredUsers: "Vor kurzem gefundene Benutzer"
exploreUsersCount: "Es gibt {count} Benutzer"
exploreFediverse: "Das Fediverse erkunden"
userList: "Listen"
aboutMisskey: "Über Misskey"
resetPassword: "Passwort zurücksetzen"
newPasswordIs: "Das neue Passwort ist \"{password}\""
posted: "Gesendet"
autoReloadWhenDisconnected: "Automatisch aktualisieren wenn die Serververbindung getrennt wird"
autoNoteWatch: "Notiz automatisch beobachten"
autoNoteWatchDescription: "Werde über Notizen, auf die du reagiert oder geantwortet hast, informiert"
reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren"
share: "Teilen"
notFound: "Nicht gefunden"
notFoundDescription: "Es konnte keine Seite unter dieser URL gefunden werden."
uploadFolder: "Standardordner für Uploads"
cacheClear: "Cache leeren"
markAsReadAllNotifications: "Alle Benachrichtigungen als gelesen markieren"
markAsReadAllUnreadNotes: "Alle Notizen als gelesen markieren"
invites: "Einladen"
title: "Betreff"
text: "Text"
enable: "Aktivieren"
next: "Weiter"
retype: "Erneut eingeben"
noteOf: "Notiz von {user}"
inviteToGroup: "Zu Gruppe einladen"
maxNoteTextLength: "Maximale Länge von Notizen"
useOsNativeEmojis: "Eingebaute Emojis des Betriebssystems benutzen"
noGroups: "Keine Gruppen vorhanden"
joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene."
noHistory: "Kein Verlauf"
disableAnimatedMfm: "MFM, die Animationen enthalten, deaktivieren"
category: "Kategorie"
tags: "Schlagwörter"
createAccount: "Benutzerkonto erstellen"
existingAcount: "Bestehendes Benutzerkonto"
regenerate: "Regenerieren"
fontSize: "Schriftgröße"
noFollowRequests: "Du hast keine Follow-Anfragen"
openImageInNewTab: "Bilder in neuem Tab öffnen"
dashboard: "Dashboard"
local: "Lokal"
total: "Gesamt"
weekOverWeekChanges: "Wöchentlich"
dayOverDayChanges: "Täglich"
accessibility: "Barrierefreiheit"
clinetSettings: "Client-Einstellungen"
accountSettings: "Benutzerkontoeinstellungen"
promotion: "Hervorgehoben"
promote: "Hervorheben"
numberOfDays: "Anzahl der Tage"
hideThisNote: "Diese Notiz verstecken"
serverLogs: "Serverprotokolle"
deleteAll: "Alle löschen"
sounds: "Töne"
listen: "Anhören"
none: "Keine"
volume: "Lautstärke"
details: "Details"
chooseEmoji: "Wähle ein Emoji"
_sfx: _sfx:
note: "Notizen"
noteMy: "Meine Notizen"
notification: "Benachrichtigungen" notification: "Benachrichtigungen"
chat: "Nachrichten"
chatBg: "Nachrichten (Hintergrund)"
antenna: "Antennen"
_ago:
unknown: "Unbekannt"
future: "Zukunft"
justNow: "Gerade eben"
secondsAgo: "vor {n} Sekunde(n)"
minutesAgo: "vor {n} Minute(n)"
hoursAgo: "vor {n} Stunde(n)"
daysAgo: "vor {n} Tag(en)"
weeksAgo: "vor {n} Woche(n)"
monthsAgo: "vor {n} Monat(en)"
yearsAgo: "vor {n} Jahr(en)"
_time:
second: "Sekunde"
minute: "Minute"
hour: "Stunde"
_permissions:
"read:messaging": "Nachrichten lesen"
"write:messaging": "Nachrichten schicken oder löschen"
"read:reactions": "Reaktionen sehen"
"write:reactions": "Reaktionen hinzufügen und bearbeiten"
_weekday:
sunday: "Sonntag"
monday: "Montag"
tuesday: "Dienstag"
wednesday: "Mittwoch"
thursday: "Donnerstag"
friday: "Freitag"
saturday: "Samstag"
_widgets: _widgets:
memo: "Memo"
notifications: "Benachrichtigungen" notifications: "Benachrichtigungen"
timeline: "Zeitleiste" timeline: "Zeitleiste"
calendar: "Kalender"
trends: "Trends"
clock: "Uhr"
rss: "RSS-Reader"
activity: "Aktivität"
photos: "Fotos"
_cw: _cw:
hide: "Ausblenden"
show: "Zeige mehr" show: "Zeige mehr"
chars: "{count} Zeichen"
files: "{count} Dateien"
poll: "Umfrage"
_poll:
noOnlyOneChoice: "Mindestens zwei Antwortmöglichkeiten werden benötigt."
choiceN: "Auswahl {n}"
noMore: "Du kannst keine weiteren Auswahlen hinzufügen"
canMultipleVote: "Mehrfachantworten erlauben"
expiration: "Abstimmung endet am"
infinite: "Nie"
at: "Beenden am..."
after: "Beenden nach..."
deadlineDate: "Enddatum"
deadlineTime: "Stunde"
duration: "Laufzeit"
votesCount: "{n} Stimmen"
totalVotes: "Insgesamt {n} Stimmen"
vote: "Abstimmen"
showResult: "Ergebnis anzeigen"
voted: "Abgestimmt"
closed: "Beendet"
remainingDays: "{d} Tage {h} Stunden verbleibend"
remainingHours: "{h} Stunden {m} Minuten verbleibend"
remainingMinutes: "{m} Minuten {s} Sekunden verbleibend"
remainingSeconds: "{s} Sekunden verbleibend"
_visibility:
public: "Öffentlich"
publicDescription: "Deine Notiz wird global sichtbar sein"
home: "Startseite"
followers: "Folgende"
followersDescription: "Nur für Follower sichtbar"
specified: "Direkt"
specifiedDescription: "Nur für erwähnte Benutzer sichtbar"
localOnly: "Nur Lokal"
_postForm:
replyPlaceholder: "Dieser Notiz antworten..."
quotePlaceholder: "Diese Notiz zitieren..."
_placeholders:
a: "Was machst du momentan?"
b: "Was ist um dich herum los?"
c: "Was geht dir durch den Kopf?"
d: "Was möchtest du sagen?"
e: "Fang an zu schreiben..."
f: "Ich warte darauf, dass du schreibst..."
_profile: _profile:
name: "Name"
username: "Benutzername" username: "Benutzername"
description: "Über mich"
youCanIncludeHashtags: "Du kannst auch Hashtags in deiner Beschreibung verwenden."
metadata: "Andere Informationen"
metadataLabel: "Name"
metadataContent: "Inhalt"
_exportOrImport:
allNotes: "Alle Notizen"
followingList: "Folgen"
muteList: "Stummschalten"
blockingList: "Blockieren"
userLists: "Listen"
_charts:
federationInstancesIncDec: "Unterschied in der Anzahl von förderierenden Instanzen"
federationInstancesTotal: "Anzahl aller föderierenden Instanzen"
usersIncDec: "Unterschied in der Anzahl von Benutzern"
usersTotal: "Anzahl aller Benutzer"
activeUsers: "Aktive Benutzer"
notesIncDec: "Unterschied in der Anzahl von Notizen"
localNotesIncDec: "Unterschied in der Anzahl von lokalen Notizen"
notesTotal: "Anzahl aller Notizen"
filesIncDec: "Unterschied in der Anzahl von Dateien"
filesTotal: "Anzahl aller Dateien"
storageUsageIncDec: "Unterschied in der Höhe der Speichernutzung"
storageUsageTotal: "Gesamte Speichernutzung"
_instanceCharts:
requests: "Anfragen"
users: "Unterschied in der Anzahl von Benutzern"
usersTotal: "Anzahl aller Benutzer"
notes: "Unterschied in der Anzahl von Notizen"
notesTotal: "Anzahl aller Notizen"
ff: "Unterschied in der Anzahl von Followern"
ffTotal: "Gesamtanzahl der Follower"
cacheSize: "Unterschied in der Größe des Caches"
cacheSizeTotal: "Gesamtgröße des Caches"
files: "Unterschied in der Anzahl der Dateien"
filesTotal: "Gesamtanzahl der Dateien"
_timelines:
home: "Startseite"
local: "Lokal"
global: "Global"
_pages:
content: "Inhalt"
more-details: "Mehr Informationen"
title: "Titel"
url: "Seiten-URL"
summary: "Zusammenfassung"
alignCenter: "Mittig ausrichten"
hide-title-when-pinned: "Seitentitel wenn angepinnt ausblenden"
font: "Schriftart"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
chooseBlock: "Block hinzufügen"
selectType: "Wähle einen Typ"
enterVariableName: "Gib einen Namen für deine Variable ein"
the-variable-name-is-already-used: "Der Variablenname wird bereits verwendet"
content-blocks: "Inhalt"
input-blocks: "Eingabe"
special-blocks: "Speziell"
post-from-post-form: "Diesen Inhalt senden"
posted-from-post-form: "Erfolgreich gesendet!"
blocks:
text: "Text"
textarea: "Textfeld"
section: "Abschnitt"
image: "Bilder"
button: "Knopf"
if: "Falls"
_if:
variable: "Variable"
post: "Neue Notiz anfertigen"
_post:
text: "Inhalt"
textInput: "Texteingabe"
_textInput:
text: "Titel"
default: "Standardwert"
textareaInput: "Eingabe des mehrzeiligen Textfelds"
_textareaInput:
text: "Titel"
default: "Standardwert"
numberInput: "Nummereingabe"
_numberInput:
text: "Titel"
default: "Standardwert"
switch: "Fallunterscheidung"
_switch:
text: "Titel"
default: "Standardwert"
counter: "Zähler"
_counter:
text: "Titel"
inc: "Erhöhen um"
_button:
text: "Titel"
colored: "Farbig"
action: "Aktion, die beim Knopfdruck ausgeführt werden soll"
_action:
dialog: "Zeige ein Dialogfenster"
_dialog:
content: "Inhalt"
resetRandom: "Setze den Zufallswert zurück"
pushEvent: "Sende ein Event"
_pushEvent:
event: "Eventname"
message: "Nachricht, die bei Aktivierung gezeigt werden soll"
variable: "Variable, die gesendet werden soll"
no-variable: "Keine"
radioButton: "Optionsfeld"
_radioButton:
title: "Titel"
values: "Auswahlmöglichkeiten (getrennt durch Zeilenumbrüche)"
default: "Standardwert"
script:
categories:
logical: "Logische Operationen"
operation: "Berechnungen"
comparison: "Vergleiche"
random: "Zufällig"
value: "Werte"
fn: "Funktionen"
text: "Textoperationen"
convert: "Konvertierung"
list: "Listen"
blocks:
text: "Text"
multiLineText: "Text (Mehrzeilig)"
textList: "Textliste"
_textList:
info: "Trenne jeden Eintrag mit einem Zeilenumbruch"
strLen: "Textlänge"
_strLen:
arg1: "Text"
strPick: "Zeichen extrahieren"
_strPick:
arg1: "Text"
arg2: "Zeichenposition"
strReplace: "Textersetzung"
_strReplace:
arg1: "Text"
arg2: "Zu ersetzender Text"
arg3: "Ersetzen mit"
strReverse: "Text umkehren"
_strReverse:
arg1: "Text"
join: "Text zusammenfügen"
_join:
arg1: "Listen"
arg2: "Trennzeichen"
add: "Addieren"
_add:
arg1: "A"
arg2: "B"
subtract: "Subtrahieren"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiplizieren"
_multiply:
arg1: "A"
arg2: "B"
divide: "Teilen"
_divide:
arg1: "A"
arg2: "B"
mod: "Rest"
_mod:
arg1: "A"
arg2: "B"
round: "Runden"
_round:
arg1: "Nummer"
eq: "A und B sind gleich"
_eq:
arg1: "A"
arg2: "B"
notEq: "A und B sind nicht gleich"
_notEq:
arg1: "A"
arg2: "B"
and: "A UND B"
_and:
arg1: "A"
arg2: "B"
or: "A ODER B"
_or:
arg1: "A"
arg2: "B"
lt: "< A ist kleiner als B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A ist größer als B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A ist kleiner als oder gleich B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A ist größer als oder gleich B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Kondition"
_if:
arg1: "Falls"
arg2: "Wenn wahr"
arg3: "Sonst"
not: "NICHT"
_not:
arg1: "NICHT"
random: "Zufällig"
_random:
arg1: "Warscheinlichkeit"
rannum: "Zufallsnummer"
_rannum:
arg1: "Minimum"
arg2: "Maximum"
randomPick: "Zufallswahl aus Liste"
_randomPick:
arg1: "Listen"
dailyRandom: "Zufallswahl (Läuft für einen Tag)"
_dailyRandom:
arg1: "Warscheinlichkeit"
dailyRannum: "Zufallsnummer (Läuft für einen Tag)"
_dailyRannum:
arg1: "Minimum"
arg2: "Maximum"
dailyRandomPick: "Zufallswahl aus Liste (Läuft für einen Tag)"
_dailyRandomPick:
arg1: "Listen"
seedRandom: "Zufällig (mit Startwert / Seed)"
_seedRandom:
arg1: "Startwert / Seed"
arg2: "Warscheinlichkeit"
seedRannum: "Zufallsnummer (mit Startwert / Seed)"
_seedRannum:
arg1: "Startwert / Seed"
arg2: "Minimum"
arg3: "Maximum"
seedRandomPick: "Zufallswahl aus Liste (mit Startwert / Seed)"
_seedRandomPick:
arg1: "Startwert / Seed"
arg2: "Listen"
DRPWPM: "Zufallswahl aus gewichteter Liste (Läuft für einen Tag)"
_DRPWPM:
arg1: "Textliste"
pick: "Aus einer Liste wählen"
_pick:
arg1: "Listen"
arg2: "Position"
listLen: "Listenlänge abrufen"
_listLen:
arg1: "Listen"
number: "Nummer"
stringToNumber: "Text zu Nummer"
_stringToNumber:
arg1: "Text"
numberToString: "Nummer zu Text"
_numberToString:
arg1: "Nummer"
splitStrByLine: "Text nach Zeilenumbrüchen aufteilen"
_splitStrByLine:
arg1: "Text"
fn: "Funktionen"
_fn:
arg1: "Ausgabe"
for: "Wiederholen"
_for:
arg1: "Anzahl der Wiederholungen"
arg2: "Aktion"
types:
string: "Text"
number: "Nummer"
array: "Listen"
stringArray: "Textliste"
enviromentVariables: "Umgebungsvariable"
pageVariables: "Seitenelement"

View File

@ -1,6 +1,6 @@
--- ---
_lang_: "English" _lang_: "English"
introMisskey: "Welcome! Misskey is an open source and also decentralized microblogging service.\nWrite the \"notes\" to share what is happening now, or send out your own words to everyone 📡\nWith the \"reactions\", you can add your feelings to everyone's notes faster than anyone 👍\nLet's explore the new world 🚀" introMisskey: "Welcome! Misskey is an open source, and also a decentralized microblogging service.\nCreate \"notes\" to share what is happening now, or to share it with everyone around you 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes 👍\nLet's explore a new world 🚀"
monthAndDay: "{month}/{day}" monthAndDay: "{month}/{day}"
search: "Search" search: "Search"
notifications: "Notifications" notifications: "Notifications"
@ -84,7 +84,7 @@ clickToShow: "Click to show"
sensitive: "NSFW" sensitive: "NSFW"
add: "Add" add: "Add"
reaction: "Reaction" reaction: "Reaction"
reactionSettingDescription: "Customize reaction picker emojis (separated by line breaks)" reactionSettingDescription: "Assign your favorite reactions which want to pin in reaction picker."
rememberNoteVisibility: "Remember note visibility settings" rememberNoteVisibility: "Remember note visibility settings"
renameFile: "Rename file" renameFile: "Rename file"
attachCancel: "Remove attachment" attachCancel: "Remove attachment"
@ -168,6 +168,7 @@ intro: "Installation of Misskey has been finished! Please create an admin user."
done: "Done" done: "Done"
processing: "Processing" processing: "Processing"
preview: "Preview" preview: "Preview"
default: "Default"
noCustomEmojis: "There are no emojis" noCustomEmojis: "There are no emojis"
customEmojisOfRemote: "Emojis from other instances" customEmojisOfRemote: "Emojis from other instances"
noJobs: "There are no jobs" noJobs: "There are no jobs"
@ -239,6 +240,8 @@ avatar: "Avatar"
banner: "Banner" banner: "Banner"
nsfw: "NSFW" nsfw: "NSFW"
disconnectedFromServer: "Connection to the server was inturrupted" disconnectedFromServer: "Connection to the server was inturrupted"
reload: "Refresh"
doNothing: "Ignore"
reloadConfirm: "Would you like to retry?" reloadConfirm: "Would you like to retry?"
watch: "Watch" watch: "Watch"
unwatch: "Undo Watch" unwatch: "Undo Watch"
@ -283,7 +286,8 @@ antennas: "Antennas"
manageAntennas: "Manage Antennas" manageAntennas: "Manage Antennas"
name: "Name" name: "Name"
antennaSource: "Antenna source" 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." antennaKeywordsDescription: "Separate with spaces for AND condition. Separate with line breaks for OR."
notifyAntenna: "Notify newer notes" notifyAntenna: "Notify newer notes"
withFileAntenna: "Filter only notes with file attached" withFileAntenna: "Filter only notes with file attached"
@ -328,7 +332,7 @@ unregister: "Unregister"
passwordLessLogin: "Set up password-less login" passwordLessLogin: "Set up password-less login"
resetPassword: "Reset password" resetPassword: "Reset password"
newPasswordIs: "The new password is \"{password}\"" newPasswordIs: "The new password is \"{password}\""
post: "Notes" post: "Post"
posted: "Posted!" posted: "Posted!"
autoReloadWhenDisconnected: "Auto reload when disconnected with server" autoReloadWhenDisconnected: "Auto reload when disconnected with server"
autoNoteWatch: "Watch note automatically" autoNoteWatch: "Watch note automatically"
@ -419,16 +423,35 @@ hideThisNote: "Hide this note"
showFeaturedNotesInTimeline: "Show Featured notes in Timeline" showFeaturedNotesInTimeline: "Show Featured notes in Timeline"
objectStorage: "Object Storage" objectStorage: "Object Storage"
useObjectStorage: "Use object storage" useObjectStorage: "Use object storage"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "URL prefix used for construct URL to object (media) referencing. Specify its URL if you are using a CDN or Proxy, otherwise specify the address that publicly accessible according to the guide of service that you're going to use. i.g 'https://<bucket>.s3.amazonaws.com' for AWS S3, and 'https://storage.googleapis.com/<bucket>' for GCS."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Please specify the bucket name used on configured service."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Files will stored under the directory of this prefix."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify the endpoint as '<host>' or '<host>:<port>' according to the guide of service that you're going to use."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not have distinction about regions, leave it blank or fill with 'us-east-1'."
objectStorageUseSSL: "Use SSL"
objectStorageUseSSLDesc: "Turn off this if you are not going to use HTTPS for API connection"
serverLogs: "Server logs" serverLogs: "Server logs"
deleteAll: "Delete all" deleteAll: "Delete all"
showFixedPostForm: "Display the posting form at the top of the timeline" showFixedPostForm: "Display the posting form at the top of the timeline"
newNoteRecived: "You've got a new note" newNoteRecived: "You've got a new note"
useNotificationsPopup: "Display notification list in popup" sounds: "Sounds"
listen: "Listen"
none: "None" none: "None"
volume: "Volume"
details: "Details"
chooseEmoji: "Choose an emoji"
_sfx: _sfx:
note: "Notes" note: "New note"
noteMy: "My note"
notification: "Notifications" notification: "Notifications"
chat: "Messaging" chat: "Messaging"
chatBg: "Messaging (Background)"
antenna: "Antenna Reception"
_ago: _ago:
unknown: "Unknown" unknown: "Unknown"
future: "Future" future: "Future"
@ -449,11 +472,11 @@ _tutorial:
title: "How to use Misskey" title: "How to use Misskey"
step1_1: "Welcome!" step1_1: "Welcome!"
step1_2: "This page is called \"timeline\". It shows chronologically ordered \"notes\" of people who you \"follow\"." step1_2: "This page is called \"timeline\". It shows chronologically ordered \"notes\" of people who you \"follow\"."
step1_3: "Your timeline is currently empty, since you have not posed any notes or followed anyone yet." step1_3: "Your timeline is currently empty, since you have not posted any notes or followed anyone yet."
step2_1: "Let's finish setting up your profile before writing a note or following anyone." step2_1: "Let's finish setting up your profile before writing a note or following anyone."
step2_2: "Providing some information about who you are will make it easier for others to follow you back." step2_2: "Providing some information about who you are will make it easier for others to follow you back."
step3_1: "Finished setting up your profile?" step3_1: "Finished setting up your profile?"
step3_2: "The next step is to post a note. You can do this by pressing a pencil icon on the screen." step3_2: "The next step is to post a note. You can do this by pressing the pencil icon on the screen."
step3_3: "Fill in the modal and press the button on the right top to post." step3_3: "Fill in the modal and press the button on the right top to post."
step3_4: "Have nothing to say? Try \"just setting up my msky\"!" step3_4: "Have nothing to say? Try \"just setting up my msky\"!"
step4_1: "Finished posting your first note?" step4_1: "Finished posting your first note?"
@ -464,7 +487,7 @@ _tutorial:
step5_4: "If the other user has a lock icon next to their name, that user will have to manually approve your follow request." step5_4: "If the other user has a lock icon next to their name, that user will have to manually approve your follow request."
step6_1: "Now you will be able to see other users' notes on your timeline." step6_1: "Now you will be able to see other users' notes on your timeline."
step6_2: "You can also put \"reactions\" on other people's notes to quickly respond." step6_2: "You can also put \"reactions\" on other people's notes to quickly respond."
step6_3: "To attach a \"reaction\", press \"+\" mark on other user's note and choose an emoji you'd like to react with." step6_3: "To attach a \"reaction\", press the \"+\" mark on another user's note and choose an emoji you'd like to react with."
step7_1: "Congratulations! You have now finished Misskey's basic tutorial." step7_1: "Congratulations! You have now finished Misskey's basic tutorial."
step7_2: "If you would like to learn more about Misskey, try the {help} section." step7_2: "If you would like to learn more about Misskey, try the {help} section."
step7_3: "Good luck and have fun! 🚀" step7_3: "Good luck and have fun! 🚀"

View File

@ -84,7 +84,7 @@ clickToShow: "Click para ver"
sensitive: "Marcado como sensible" sensitive: "Marcado como sensible"
add: "Añadir" add: "Añadir"
reaction: "Reacción" reaction: "Reacción"
reactionSettingDescription: "Elegir las reacciones mostradas en el seleccionador de reacciones, separadas por una nueva linea" reactionSettingDescription: "Asigne sus reacción favoritas que desean anclar en el selector de reacciones."
rememberNoteVisibility: "Recordar visibilidad" rememberNoteVisibility: "Recordar visibilidad"
renameFile: "Renombrar archivo" renameFile: "Renombrar archivo"
attachCancel: "Quitar adjunto" attachCancel: "Quitar adjunto"
@ -168,6 +168,7 @@ intro: "¡La instalación de Misskey ha terminado! Crea el usuario administrador
done: "Terminado" done: "Terminado"
processing: "Procesando" processing: "Procesando"
preview: "Vista previa" preview: "Vista previa"
default: "Predeterminado"
noCustomEmojis: "No hay emojis personalizados" noCustomEmojis: "No hay emojis personalizados"
customEmojisOfRemote: "Emojis remotos" customEmojisOfRemote: "Emojis remotos"
noJobs: "No hay trabajos" noJobs: "No hay trabajos"
@ -239,6 +240,8 @@ avatar: "Avatar"
banner: "Banner" banner: "Banner"
nsfw: "Marcado como sensible" nsfw: "Marcado como sensible"
disconnectedFromServer: "Desconectado del servidor" disconnectedFromServer: "Desconectado del servidor"
reload: "Recargar"
doNothing: "No hacer nada"
reloadConfirm: "¿Desea recargar?" reloadConfirm: "¿Desea recargar?"
watch: "Ver" watch: "Ver"
unwatch: "Dejar de ver" unwatch: "Dejar de ver"
@ -283,7 +286,8 @@ antennas: "Antenas"
manageAntennas: "Administrar antenas" manageAntennas: "Administrar antenas"
name: "Nombre" name: "Nombre"
antennaSource: "Origen de la antena" 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" antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR"
notifyAntenna: "Notificar nueva nota" notifyAntenna: "Notificar nueva nota"
withFileAntenna: "Sólo notas con archivos adjuntados" withFileAntenna: "Sólo notas con archivos adjuntados"
@ -419,16 +423,35 @@ hideThisNote: "Ocultar esta nota"
showFeaturedNotesInTimeline: "Mostrar notas destacadas en la línea de tiempo" showFeaturedNotesInTimeline: "Mostrar notas destacadas en la línea de tiempo"
objectStorage: "Almacenamiento de objetos" objectStorage: "Almacenamiento de objetos"
useObjectStorage: "Usar almacenamiento de objetos" useObjectStorage: "Usar almacenamiento de objetos"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "Prefijo de URL utilizado para construir URL para hacer referencia a objetos (medios). Especifique su URL si está utilizando un CDN o Proxy; de lo contrario, especifique la dirección a la que se puede acceder públicamente de acuerdo con la guía de servicio que va a utilizar. i.g 'https://<bucket>.s3.amazonaws.com' para AWS S3 y 'https://storage.googleapis.com/<bucket>' para GCS."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Especifique el nombre del depósito utilizado en el servicio configurado."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Los archivos se almacenarán en el directorio de este prefijo."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Deje esto en blanco si está utilizando AWS S3; de lo contrario, especifique el punto final como '<host>' o '<host>: <port>' de acuerdo con la guía de servicio que va a utilizar."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Especifique una región como 'xx-east-1'. Si su servicio no tiene distinción sobre regiones, déjelo en blanco o complete con 'us-east-1'."
objectStorageUseSSL: "Usar SSL"
objectStorageUseSSLDesc: "Desactive esto si no va a usar HTTPS para la conexión API"
serverLogs: "Registros del servidor" serverLogs: "Registros del servidor"
deleteAll: "Eliminar todos" deleteAll: "Eliminar todos"
showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo" showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo"
newNoteRecived: "Tienes una nota nuevo" newNoteRecived: "Tienes una nota nuevo"
useNotificationsPopup: "Mostrar lista de notificaciones en ventana emergente" sounds: "Sonidos"
listen: "Escuchar"
none: "Ninguna" none: "Ninguna"
volume: "Volumen"
details: "Detalles"
chooseEmoji: "Elije un emoji"
_sfx: _sfx:
note: "Notas" note: "Notas"
noteMy: "Nota (a mí mismo)"
notification: "Notificaciones" notification: "Notificaciones"
chat: "Chat" chat: "Chat"
chatBg: "Chat (Fondo)"
antenna: "Antena receptora"
_ago: _ago:
unknown: "Desconocido" unknown: "Desconocido"
future: "Futuro" future: "Futuro"

View File

@ -40,9 +40,9 @@ sendMessage: "Envoyer un message"
copyUsername: "Copier le nom d'utilisateur" copyUsername: "Copier le nom d'utilisateur"
reply: "Répondre" reply: "Répondre"
loadMore: "Voir plus" loadMore: "Voir plus"
youGotNewFollower: "Vous a suivi" youGotNewFollower: "Vous a abonnés"
receiveFollowRequest: "Demande de suivi reçue" receiveFollowRequest: "Demande de abonnés reçue"
followRequestAccepted: "Suivre la demande acceptée" followRequestAccepted: "L'abonne la demande acceptée"
mentions: "Mentions" mentions: "Mentions"
directNotes: "Messages directs" directNotes: "Messages directs"
importAndExport: "Import et export" importAndExport: "Import et export"
@ -51,7 +51,7 @@ export: "Exporter"
files: "Fichier·s" files: "Fichier·s"
download: "Télécharger" download: "Télécharger"
driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées." driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées."
unfollowConfirm: "Êtes-vous sûr·e ne plus vouloir suivre {name} ?" unfollowConfirm: "Êtes-vous sûr·de ne plus vouloir abonne {name} ?"
exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive." exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive."
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps." importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
lists: "Listes" lists: "Listes"
@ -84,7 +84,7 @@ clickToShow: "Cliquer pour afficher"
sensitive: "Contenu sensible" sensitive: "Contenu sensible"
add: "Ajouter" add: "Ajouter"
reaction: "Réactions" reaction: "Réactions"
reactionSettingDescription: "Personnaliser les émojis à afficher dans le sélecteur de réactions, délimités par les sauts de ligne." reactionSettingDescription: "Attribuez vos réactions préférées qui souhaitent épingler le sélecteur de réaction."
rememberNoteVisibility: "Se souvenir de la visibilité des notes" rememberNoteVisibility: "Se souvenir de la visibilité des notes"
renameFile: "Renommer le ficher" renameFile: "Renommer le ficher"
attachCancel: "Enlever le fichier attaché" attachCancel: "Enlever le fichier attaché"
@ -119,9 +119,9 @@ wallpaper: "Arrière plan"
removeWallpaper: "Supprimer l'arrière plan" removeWallpaper: "Supprimer l'arrière plan"
searchWith: "Recherche : {q}" searchWith: "Recherche : {q}"
youHaveNoLists: "Vous n'avez aucune liste" youHaveNoLists: "Vous n'avez aucune liste"
followConfirm: "Désirez-vous suivre {name} ?" followConfirm: "Désirez-vous abonne {name} ?"
proxyAccount: "Compte proxy" proxyAccount: "Compte proxy"
proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la suit. Le compte proxy va donc le·la suivre pour que ses notes soient acheminées." proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la abonné. Le compte proxy va donc le·la abonne pour que ses notes soient acheminées."
host: "Hôte" host: "Hôte"
selectUser: "Sélectionner un·e utilisateur·rice" selectUser: "Sélectionner un·e utilisateur·rice"
recipient: "Correspondant·e" recipient: "Correspondant·e"
@ -168,6 +168,7 @@ intro: "L'installation de Misskey est terminée! Créons le compte administrateu
done: "Terminé" done: "Terminé"
processing: "Traitement en cours" processing: "Traitement en cours"
preview: "Prévisualisation" preview: "Prévisualisation"
default: "Par défaut"
noCustomEmojis: "Il a pas démoji" noCustomEmojis: "Il a pas démoji"
customEmojisOfRemote: "Émojis l'instance distante" customEmojisOfRemote: "Émojis l'instance distante"
noJobs: "Il n'y a aucune tâche planifiée" noJobs: "Il n'y a aucune tâche planifiée"
@ -239,6 +240,8 @@ avatar: "Avatar"
banner: "Bannière" banner: "Bannière"
nsfw: "Contenu sensible" nsfw: "Contenu sensible"
disconnectedFromServer: "Déconnecté du serveur" disconnectedFromServer: "Déconnecté du serveur"
reload: "Rafraîchir"
doNothing: "Ignorer"
reloadConfirm: "Voulez-vous recharger?" reloadConfirm: "Voulez-vous recharger?"
watch: "Surveiller" watch: "Surveiller"
unwatch: "Ne plus surveiller" unwatch: "Ne plus surveiller"
@ -283,7 +286,8 @@ antennas: "Antenne"
manageAntennas: "Gestion d'antenne" manageAntennas: "Gestion d'antenne"
name: "Nom" name: "Nom"
antennaSource: "Recevoir la source" 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." 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" notifyAntenna: "Notifier les nouvelles notes"
withFileAntenna: "Notes uniquement avec fichiers joints" withFileAntenna: "Notes uniquement avec fichiers joints"
@ -309,11 +313,11 @@ userList: "Listes"
about: "Informations" about: "Informations"
aboutMisskey: "À propos de Misskey" aboutMisskey: "À propos de Misskey"
aboutMisskeyText: "Misskey est un logiciel open source, développé par syuilo depuis 2014." aboutMisskeyText: "Misskey est un logiciel open source, développé par syuilo depuis 2014."
misskeyMembers: "Il est développé et maintenu par les membres répertoriés ici:" misskeyMembers: "Il est développé et maintenu par les membres listés ci-dessous :"
misskeySource: "Le code source est disponible ici:" misskeySource: "Le code source est disponible ici:"
misskeyTranslation: "Aidez-nous avec votre contribution à traduire Misskey:" misskeyTranslation: "Aidez-nous avec votre contribution à traduire Misskey:"
misskeyDonate: "Vous pouvez contribuer au développement de Misskey en faisant un don ici:" misskeyDonate: "Vous pouvez contribuer au développement de Misskey en faisant un don ici:"
morePatrons: "Nous apprécions vraiment le soutien de nombreux autres les soutiens non répertoriés ici. Merci beaucoup à tous! 🥰" morePatrons: "Nous apprécions vraiment le soutien de nombreuses autres personnes non mentionnées ici. Merci à toutes et à tous ! 🥰"
patrons: "Supporteurs" patrons: "Supporteurs"
administrator: "Administrateur" administrator: "Administrateur"
token: "Jeton" token: "Jeton"
@ -328,7 +332,7 @@ unregister: "Se désinscrire"
passwordLessLogin: "Connectez-vous sans mot de passe" passwordLessLogin: "Connectez-vous sans mot de passe"
resetPassword: "Réinitialiser mot de passe" resetPassword: "Réinitialiser mot de passe"
newPasswordIs: "Votre nouveau mot de passe est \"{password}\"" newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
post: "Notes" post: "Publier"
posted: "Publié !" posted: "Publié !"
autoReloadWhenDisconnected: "Rechargement automatique lorsque le serveur se déconnecte" autoReloadWhenDisconnected: "Rechargement automatique lorsque le serveur se déconnecte"
autoNoteWatch: "Surveiller automatique pour les notes" autoNoteWatch: "Surveiller automatique pour les notes"
@ -401,7 +405,7 @@ createAccount: "Créer compte"
existingAcount: "Comptes existants" existingAcount: "Comptes existants"
regenerate: "Régénérer" regenerate: "Régénérer"
fontSize: "Taille de la police" fontSize: "Taille de la police"
noFollowRequests: "Vous n'avez aucune demandes d'abonnement en attente" noFollowRequests: "Vous navez aucune demande dabonnement en attente"
openImageInNewTab: "Ouvrir l'image dans un nouvel onglet" openImageInNewTab: "Ouvrir l'image dans un nouvel onglet"
dashboard: "Tableau de bord" dashboard: "Tableau de bord"
local: "Local" local: "Local"
@ -416,19 +420,38 @@ promotion: "Promu"
promote: "Promouvoir" promote: "Promouvoir"
numberOfDays: "Nombre de jours" numberOfDays: "Nombre de jours"
hideThisNote: "Masquer cette note" hideThisNote: "Masquer cette note"
showFeaturedNotesInTimeline: "Afficher les notes en vedette dans Fil d'actualité" showFeaturedNotesInTimeline: "Afficher les notes en vedette dans fil d'actualité"
objectStorage: "Stockage d'objets" objectStorage: "Stockage d'objets"
useObjectStorage: "Utiliser le stockage d'objets" useObjectStorage: "Utiliser le stockage d'objets"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "Préfixe d'URL utilisé pour construire l'URL vers le référencement d'objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l'adresse accessible au public selon le guide de service que vous allez utiliser. i.g 'https://<bucket>.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/<bucket>' pour GCS."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Les fichiers seront stockés sous le répertoire de ce préfixe."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Laissez ce champ vide si vous utilisez AWS S3, sinon spécifiez le point de terminaison comme '<host>' ou '<host>: <port>' selon le guide de service que vous allez utiliser."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Spécifiez une région comme 'xx-east-1'. Si votre service ne fait pas de distinction entre les régions, laissez-le vide ou remplissez 'us-east-1'."
objectStorageUseSSL: "Utiliser SSL"
objectStorageUseSSLDesc: "Désactivez-le si vous n'utilisez pas HTTPS pour la connexion API"
serverLogs: "Journaux serveur" serverLogs: "Journaux serveur"
deleteAll: "Supprimer tout" deleteAll: "Supprimer tout"
showFixedPostForm: "Afficher le formulaire en haut du fil d'actualité" showFixedPostForm: "Afficher le formulaire en haut du fil d'actualité"
newNoteRecived: "Vous avez un nouveau note" newNoteRecived: "Vous avez une nouvelle note"
useNotificationsPopup: "Afficher la liste des notifications dans une fenêtre contextuelle" sounds: "Sons"
listen: "Écouter"
none: "Rien" none: "Rien"
volume: "Volume"
details: "Détails"
chooseEmoji: "Choisissez des emojis"
_sfx: _sfx:
note: "Notes" note: "Nouvelle note"
noteMy: "Ma note"
notification: "Notifications" notification: "Notifications"
chat: "Discuter" chat: "Discuter"
chatBg: "Discuter (De fond)"
antenna: "Réception d'antenne"
_ago: _ago:
unknown: "Inconnu" unknown: "Inconnu"
future: "Futur" future: "Futur"
@ -448,16 +471,26 @@ _time:
_tutorial: _tutorial:
title: "Comment utiliser Misskey" title: "Comment utiliser Misskey"
step1_1: "Bienvenue," 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_2: "Cette page est appelée \"fil d'actualité\". Elle montre les \"notes\" des personnes que vous \"abonne\" 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." step1_3: "Vous n'avez pas encore publier de notes et personne n'est encore abonnements, vous ne devriez donc rien voir dans fil d'actualité."
step2_1: "Finissons de créer votre profil avant d'écrire une note ou de suivre quelqu'un." step2_1: "Finissons de créer votre profil avant d'écrire une note ou de abonne quelqu'un."
step2_2: "En fournissant quelques informations sur vous, il sera plus facile pour les autres de vous suivre." step2_2: "En fournissant quelques informations sur vous, il sera plus facile pour les autres de vous abonne."
step3_1: "Vous avez fini de créer votre profil ?" 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_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_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\"." 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_1: "Avez-vous posté votre première notes ?"
step4_2: "Votre première note est maintenant affichée sur votre timeline." step4_2: "Votre première note est maintenant affiché sur votre fil d'actualité."
step5_1: "Ensuite, abonnons-nous d'autres personnes et rendre la fil d'actualité en savoir plus vivante."
step5_2: "{featured} affiche les notes en tendance sur cette instance. {explore} vous permet de trouver les utilisateur·rice·s en tendance. Essayez de vous abonner aux gens que vous aimez !"
step5_3: "Pour abonne un utilisateur, cliquez sur l'icône de l'utilisateur pour afficher la page de l'utilisateur et appuyez sur le bouton \"Abonnement\"."
step5_4: "Selon l'utilisateur, il peut s'écouler un certain temps avant que la abonner ne soit approuvé."
step6_1: "Si vous voyez la notes d'un autre utilisateur sur fil d'actualité, c'était réussi."
step6_2: "Vous pouvez ajouter des \"réactions\" aux notes des autres afin de pouvoir facilement dire votre émotion."
step6_3: "Pour ajouter une réaction, cliquez sur la marque \"+\" sur la note et sélectionnez la réaction souhaitée."
step7_1: "Tu as bien travaillé ! Ceci conclut l'explication de l'utilisation de base de Misskey."
step7_2: "Pour plus d'informations sur Misskey, voir {help}."
step7_3: "Alors, profitez de Misskey 🚀"
_2fa: _2fa:
alreadyRegistered: "Cette étape à déjà été complétée" alreadyRegistered: "Cette étape à déjà été complétée"
registerDevice: "Sinscrire l'appareil" registerDevice: "Sinscrire l'appareil"

View File

@ -35,6 +35,8 @@ unpin: "ピン留め解除"
copyContent: "内容をコピー" copyContent: "内容をコピー"
copyLink: "リンクをコピー" copyLink: "リンクをコピー"
delete: "削除" delete: "削除"
deleteAndEdit: "削除して編集"
deleteAndEditConfirm: "このートを削除してもう一度編集しますかこのートへのリアクション、Renote、返信も全て削除されます。"
addToList: "リストに追加" addToList: "リストに追加"
sendMessage: "メッセージを送信" sendMessage: "メッセージを送信"
copyUsername: "ユーザー名をコピー" copyUsername: "ユーザー名をコピー"
@ -84,9 +86,8 @@ clickToShow: "クリックして表示"
sensitive: "閲覧注意" sensitive: "閲覧注意"
add: "追加" add: "追加"
reaction: "リアクション" reaction: "リアクション"
reactionSettingDescription: "リアクションピッカーに表示するリアクションを改行で区切って設定します。" reactionSettingDescription: "リアクションピッカーに表示するリアクションを設定します。"
rememberNoteVisibility: "公開範囲を記憶する" rememberNoteVisibility: "公開範囲を記憶する"
renameFile: "ファイル名を変更"
attachCancel: "添付取り消し" attachCancel: "添付取り消し"
markAsSensitive: "閲覧注意にする" markAsSensitive: "閲覧注意にする"
unmarkAsSensitive: "閲覧注意を解除する" unmarkAsSensitive: "閲覧注意を解除する"
@ -116,6 +117,7 @@ loginFailed: "ログインに失敗しました"
showOnRemote: "リモートで表示" showOnRemote: "リモートで表示"
general: "全般" general: "全般"
wallpaper: "壁紙" wallpaper: "壁紙"
setWallpaper: "壁紙を設定"
removeWallpaper: "壁紙を削除" removeWallpaper: "壁紙を削除"
searchWith: "検索: {q}" searchWith: "検索: {q}"
youHaveNoLists: "リストがありません" youHaveNoLists: "リストがありません"
@ -168,6 +170,7 @@ intro: "Misskeyのインストールが完了しました管理者アカウ
done: "完了" done: "完了"
processing: "処理中" processing: "処理中"
preview: "プレビュー" preview: "プレビュー"
default: "デフォルト"
noCustomEmojis: "絵文字はありません" noCustomEmojis: "絵文字はありません"
customEmojisOfRemote: "リモートの絵文字" customEmojisOfRemote: "リモートの絵文字"
noJobs: "ジョブはありません" noJobs: "ジョブはありません"
@ -203,10 +206,13 @@ messaging: "チャット"
upload: "アップロード" upload: "アップロード"
fromDrive: "ドライブから" fromDrive: "ドライブから"
fromUrl: "URLから" fromUrl: "URLから"
uploadFromUrl: "URLアップロード"
uploadFromUrlDescription: "アップロードしたいファイルのURL"
uploadFromUrlRequested: "アップロードをリクエストしました"
uploadFromUrlMayTakeTime: "アップロードが完了するまで時間がかかる場合があります。"
explore: "みつける" explore: "みつける"
games: "Misskey Games" games: "Misskey Games"
messageRead: "既読" messageRead: "既読"
recentUsedEmojis: "最近使用した絵文字"
noMoreHistory: "これより過去の履歴はありません" noMoreHistory: "これより過去の履歴はありません"
startMessaging: "チャットを開始" startMessaging: "チャットを開始"
nUsersRead: "{n}人が読みました" nUsersRead: "{n}人が読みました"
@ -222,17 +228,30 @@ yearsOld: "{age}歳"
registeredDate: "登録日" registeredDate: "登録日"
location: "場所" location: "場所"
theme: "テーマ" theme: "テーマ"
themeForLightMode: "ライトモードで使うテーマ"
themeForDarkMode: "ダークモードで使うテーマ"
light: "ライト"
dark: "ダーク"
lightThemes: "明るいテーマ" lightThemes: "明るいテーマ"
darkThemes: "暗いテーマ" darkThemes: "暗いテーマ"
syncDeviceDarkMode: "デバイスのダークモードと同期する"
drive: "ドライブ" drive: "ドライブ"
fileName: "ファイル名"
selectFile: "ファイルを選択" selectFile: "ファイルを選択"
selectFiles: "ファイルを選択" selectFiles: "ファイルを選択"
renameFolder: "フォルダー名を変更" renameFile: "ファイル名を変更"
folderName: "フォルダー名"
createFolder: "フォルダーを作成" createFolder: "フォルダーを作成"
renameFolder: "フォルダー名を変更"
deleteFolder: "フォルダーを削除" deleteFolder: "フォルダーを削除"
addFile: "ファイルを追加" addFile: "ファイルを追加"
emptyDrive: "ドライブは空です" emptyDrive: "ドライブは空です"
emptyFolder: "フォルダーは空です" emptyFolder: "フォルダーは空です"
unableToDelete: "削除できません"
inputNewFileName: "新しいファイル名を入力してください"
inputNewFolderName: "新しいフォルダ名を入力してください"
circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。"
hasChildFilesOrFolders: "このフォルダは空でないため、削除できません。"
copyUrl: "URLをコピー" copyUrl: "URLをコピー"
rename: "名前を変更" rename: "名前を変更"
avatar: "アイコン" avatar: "アイコン"
@ -286,6 +305,7 @@ manageAntennas: "アンテナの管理"
name: "名前" name: "名前"
antennaSource: "受信ソース" antennaSource: "受信ソース"
antennaKeywords: "受信キーワード" antennaKeywords: "受信キーワード"
antennaExcludeKeywords: "除外キーワード"
antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります" antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
notifyAntenna: "新しいノートを通知する" notifyAntenna: "新しいノートを通知する"
withFileAntenna: "ファイルが添付されたノートのみ" withFileAntenna: "ファイルが添付されたノートのみ"
@ -385,13 +405,14 @@ strongPassword: "強いパスワード"
passwordMatched: "一致しました" passwordMatched: "一致しました"
passwordNotMatched: "一致していません" passwordNotMatched: "一致していません"
signinWith: "{x}でログイン" signinWith: "{x}でログイン"
signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
tapSecurityKey: "セキュリティーキーにタッチ" tapSecurityKey: "セキュリティーキーにタッチ"
or: "もしくは" or: "もしくは"
uiLanguage: "UIの表示言語" uiLanguage: "UIの表示言語"
groupInvited: "グループに招待されました" groupInvited: "グループに招待されました"
aboutX: "{x}について" aboutX: "{x}について"
useOsNativeEmojis: "OSネイティブの絵文字を使用" useOsNativeEmojis: "OSネイティブの絵文字を使用"
noGroups: "グループがありません" youHaveNoGroups: "グループがありません"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。" joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
noHistory: "履歴はありません" noHistory: "履歴はありません"
disableAnimatedMfm: "動きのあるMFMを無効にする" disableAnimatedMfm: "動きのあるMFMを無効にする"
@ -421,15 +442,30 @@ hideThisNote: "このノートを非表示"
showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示する" showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示する"
objectStorage: "オブジェクトストレージ" objectStorage: "オブジェクトストレージ"
useObjectStorage: "オブジェクトストレージを使用" 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: "サーバーログ" serverLogs: "サーバーログ"
deleteAll: "全て削除" deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する" showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
newNoteRecived: "新しいノートがあります" newNoteRecived: "新しいノートがあります"
useNotificationsPopup: "通知一覧をポップアップで表示"
sounds: "サウンド" sounds: "サウンド"
listen: "聴く" listen: "聴く"
none: "なし" none: "なし"
volume: "音量" volume: "音量"
details: "詳細"
chooseEmoji: "絵文字を選択"
unableToProcess: "操作を完了できません"
recentUsed: "最近使用"
_sfx: _sfx:
note: "ノート" note: "ノート"
@ -437,6 +473,7 @@ _sfx:
notification: "通知" notification: "通知"
chat: "チャット" chat: "チャット"
chatBg: "チャット(バックグラウンド)" chatBg: "チャット(バックグラウンド)"
antenna: "アンテナ受信"
_ago: _ago:
unknown: "謎" unknown: "謎"
@ -655,45 +692,39 @@ _pages:
newPage: "ページの作成" newPage: "ページの作成"
editPage: "ページの編集" editPage: "ページの編集"
readPage: "ソースを表示中" readPage: "ソースを表示中"
page-created: "ページを作成しました" created: "ページを作成しました"
page-updated: "ページを更新しました" updated: "ページを更新しました"
name-already-exists: "指定されたページURLは既に存在しています" deleted: "ページを削除しました"
title-invalid-name: "不正なページURLす" nameAlreadyExists: "指定されたページURLは既に存在しています"
text-invalid-name: "空白でないか確認してください" invalidNameTitle: "不正なページURLです"
invalidNameText: "空白でないか確認してください"
editThisPage: "このページを編集" editThisPage: "このページを編集"
viewSource: "ソースを表示" viewSource: "ソースを表示"
viewPage: "ページを見る" viewPage: "ページを見る"
like: "いいね" like: "いいね"
unlike: "いいね解除" unlike: "いいね解除"
liked-pages: "いいねしたページ" my: "自分のページ"
my-pages: "自分のページ" liked: "いいねしたページ"
inspector: "インスペクター" inspector: "インスペクター"
content: "ページブロック" content: "ページブロック"
variables: "変数" variables: "変数"
variables-info: "変数を使うことで動的なページを作成できます。テキスト内で <b>{ 変数名 }</b> と書くとそこに変数の値を埋め込めます。例えば <b>Hello { thing } world!</b> というテキストで、変数(thing)の値が <b>ai</b> だった場合、テキストは <b>Hello ai world!</b> になります。"
variables-info2: "変数の評価(値を算出すること)は上から下に行われるので、ある変数の中で自分より下の変数を参照することはできません。例えば上から <b>A、B、C</b> と3つの変数を定義したとき、<b>C</b>の中で<b>A</b>や<b>B</b>を参照することはできますが、<b>A</b>の中で<b>B</b>や<b>C</b>を参照することはできません。"
variables-info3: "ユーザーからの入力を受け取るには、ページに「ユーザー入力」ブロックを設置し、「変数名」に入力を格納したい変数名を設定します(変数は自動で作成されます)。その変数を使ってユーザー入力に応じた動作を行えます。"
variables-info4: "関数を使うと、値の算出処理を再利用可能な形にまとめることができます。関数を作るには、「関数」タイプの変数を作成します。関数にはスロット(引数)を設定することができ、スロットの値は関数内で変数として利用可能です。また、AiScript標準で関数を引数に取る関数(高階関数と呼ばれます)も存在します。関数は予め定義しておくほかに、このような高階関数のスロットに即席でセットすることもできます。"
more-details: "詳しい説明"
title: "タイトル" title: "タイトル"
url: "ページURL" url: "ページURL"
summary: "ページの要約" summary: "ページの要約"
alignCenter: "中央寄せ" alignCenter: "中央寄せ"
hide-title-when-pinned: "ピン留めされているときにタイトルを非表示" hideTitleWhenPinned: "ピン留めされているときにタイトルを非表示"
font: "フォント" font: "フォント"
fontSerif: "セリフ" fontSerif: "セリフ"
fontSansSerif: "サンセリフ" fontSansSerif: "サンセリフ"
set-eye-catching-image: "アイキャッチ画像を設定" eyeCatchingImageSet: "アイキャッチ画像を設定"
remove-eye-catching-image: "アイキャッチ画像を削除" eyeCatchingImageRemove: "アイキャッチ画像を削除"
chooseBlock: "ブロックを追加" chooseBlock: "ブロックを追加"
selectType: "種類を選択" selectType: "種類を選択"
enterVariableName: "変数名を決めてください" enterVariableName: "変数名を決めてください"
the-variable-name-is-already-used: "その変数名は既に使われています" variableNameIsAlreadyUsed: "その変数名は既に使われています"
content-blocks: "コンテンツ" contentBlocks: "コンテンツ"
input-blocks: "入力" inputBlocks: "入力"
special-blocks: "特殊" specialBlocks: "特殊"
post-from-post-form: "この内容を投稿"
posted-from-post-form: "投稿しました"
blocks: blocks:
text: "テキスト" text: "テキスト"
textarea: "テキストエリア" textarea: "テキストエリア"

View File

@ -26,11 +26,40 @@ signup: "ನೋಂದಣಿ"
uploading: "ಅಪ್‌ಲೋಡಾಗುತ್ತಿದೆ" uploading: "ಅಪ್‌ಲೋಡಾಗುತ್ತಿದೆ"
save: "ಉಳಿಸಿ" save: "ಉಳಿಸಿ"
users: "ಬಳಕೆದಾರ" 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: "ನಿದರ್ಶನ" instances: "ನಿದರ್ಶನ"
remove: "ಅಳಿಸು"
_sfx: _sfx:
notification: "ಅಧಿಸೂಚನೆಗಳು" notification: "ಅಧಿಸೂಚನೆಗಳು"
_widgets: _widgets:
notifications: "ಅಧಿಸೂಚನೆಗಳು" notifications: "ಅಧಿಸೂಚನೆಗಳು"
timeline: "ಸಮಯಸಾಲು" timeline: "ಸಮಯಸಾಲು"
_cw:
show: "ಇನ್ನಷ್ಟು ನೋಡು"
_profile: _profile:
username: "ಬಳಕೆಹೆಸರು" username: "ಬಳಕೆಹೆಸರು"

View File

@ -84,7 +84,7 @@ clickToShow: "클릭하여 보기"
sensitive: "열람주의" sensitive: "열람주의"
add: "추가" add: "추가"
reaction: "리액션" reaction: "리액션"
reactionSettingDescription: "리액션 선택에 표시할 리액션을 줄바꿈으로 구분해 설정합니다." reactionSettingDescription: "리액션 선택 상자에 표시할 리액션을 설정합니다."
rememberNoteVisibility: "공개 범위를 기억하기" rememberNoteVisibility: "공개 범위를 기억하기"
renameFile: "파일 이름 변경" renameFile: "파일 이름 변경"
attachCancel: "첨부 취소" attachCancel: "첨부 취소"
@ -168,6 +168,7 @@ intro: "Misskey의 설치가 완료되었습니다! 관리자 계정을 생성
done: "완료" done: "완료"
processing: "처리중" processing: "처리중"
preview: "미리보기" preview: "미리보기"
default: "기본값"
noCustomEmojis: "이모지가 없습니다" noCustomEmojis: "이모지가 없습니다"
customEmojisOfRemote: "다른 인스턴스들의 이모지" customEmojisOfRemote: "다른 인스턴스들의 이모지"
noJobs: "작업이 없습니다" noJobs: "작업이 없습니다"
@ -239,6 +240,8 @@ avatar: "아바타"
banner: "배너" banner: "배너"
nsfw: "열람주의" nsfw: "열람주의"
disconnectedFromServer: "서버와의 연결이 끊어졌습니다" disconnectedFromServer: "서버와의 연결이 끊어졌습니다"
reload: "새로고침"
doNothing: "무시하기"
reloadConfirm: "새로고침 하시겠습니까?" reloadConfirm: "새로고침 하시겠습니까?"
watch: "지켜보기" watch: "지켜보기"
unwatch: "지켜보기 해제" unwatch: "지켜보기 해제"
@ -284,6 +287,7 @@ manageAntennas: "안테나 관리"
name: "이름" name: "이름"
antennaSource: "받을 소스" antennaSource: "받을 소스"
antennaKeywords: "받을 키워드" antennaKeywords: "받을 키워드"
antennaExcludeKeywords: "제외할 키워드"
antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다" antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
notifyAntenna: "새로운 노트를 알림" notifyAntenna: "새로운 노트를 알림"
withFileAntenna: "파일이 첨부된 노트만" withFileAntenna: "파일이 첨부된 노트만"
@ -419,16 +423,35 @@ hideThisNote: "이 노트를 숨기기"
showFeaturedNotesInTimeline: "타임라인에 추천 노트를 표시" showFeaturedNotesInTimeline: "타임라인에 추천 노트를 표시"
objectStorage: "오브젝트 스토리지" objectStorage: "오브젝트 스토리지"
useObjectStorage: "오브젝트 스토리지를 사용" useObjectStorage: "오브젝트 스토리지를 사용"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "오브젝트 (미디어) 참조 URL 을 만들 때 사용되는 URL입니다. CDN 또는 프록시를 사용하는 경우 그 URL을 지정하고, 그 외의 경우 사용할 서비스의 가이드에 따라 공개적으로 액세스 할 수 있는 주소를 지정해 주세요. 예를 들어, AWS S3의 경우 'https://<bucket>.s3.amazonaws.com', GCS등의 경우 'https://storage.googleapis.com/<bucket>' 와 같이 지정합니다."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "사용 서비스의 bucket명을 지정해주세요."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "이 Prefix 의 디렉토리 아래에 파일이 저장됩니다."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "AWS S3의 경우 공란, 다른 서비스의 경우 각 서비스의 가이드에 맞게 endpoint를 설정해주세요. '<host>' 혹은 '<host>:<port>' 와 같이 지정합니다."
objectStorageRegion: "Region"
objectStorageRegionDesc: "'xx-east-1'와 같이 region을 지정해주세요. 사용하는 서비스에 region 개념이 없는 경우, 비워 두거나 'us-east-1'으로 설정해 주세요."
objectStorageUseSSL: "SSL 사용"
objectStorageUseSSLDesc: "API 호출시 HTTPS 를 사용하지 않는 경우 OFF 로 설정해 주세요"
serverLogs: "서버 로그" serverLogs: "서버 로그"
deleteAll: "모두 삭제" deleteAll: "모두 삭제"
showFixedPostForm: "타임라인 상단에 글 작성란을 표시" showFixedPostForm: "타임라인 상단에 글 작성란을 표시"
newNoteRecived: "새 노트가 있습니다" newNoteRecived: "새 노트가 있습니다"
useNotificationsPopup: "알림 목록을 팝업으로 표시" sounds: "소리"
listen: "듣기"
none: "없음" none: "없음"
volume: "음량"
details: "자세히"
chooseEmoji: "이모지 선택"
_sfx: _sfx:
note: "노트" note: "노트"
noteMy: "내 노트"
notification: "알림" notification: "알림"
chat: "대화" chat: "대화"
chatBg: "대화 (백그라운드)"
antenna: "안테나 수신"
_ago: _ago:
unknown: "알 수 없음" unknown: "알 수 없음"
future: "미래" future: "미래"

View File

@ -1,2 +1,36 @@
--- ---
_lang_: "Русский язык" _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

@ -11,7 +11,7 @@ ok: "OK"
gotIt: "我明白了" gotIt: "我明白了"
cancel: "取消" cancel: "取消"
enterUsername: "输入用户名" enterUsername: "输入用户名"
renotedBy: "由 {user} 转" renotedBy: "由 {user} 转"
noNotes: "没有投稿" noNotes: "没有投稿"
noNotifications: "无通知" noNotifications: "无通知"
instance: "实例" instance: "实例"
@ -84,7 +84,7 @@ clickToShow: "点击以显示"
sensitive: "阅读注意" sensitive: "阅读注意"
add: "添加" add: "添加"
reaction: "反应" reaction: "反应"
reactionSettingDescription: "快速选择回应中的自定义表情符号,以换行符分隔。" reactionSettingDescription: "选择您想要固定在反应选择器中的反应。"
rememberNoteVisibility: "记录公开范围" rememberNoteVisibility: "记录公开范围"
renameFile: "重命名文件" renameFile: "重命名文件"
attachCancel: "删除附件" attachCancel: "删除附件"
@ -168,6 +168,7 @@ intro: "Misskey的部署结束啦填写管理员账号吧"
done: "完成" done: "完成"
processing: "处理中" processing: "处理中"
preview: "预览" preview: "预览"
default: "默认"
noCustomEmojis: "无自定义Emoji" noCustomEmojis: "无自定义Emoji"
customEmojisOfRemote: "远程Emoji" customEmojisOfRemote: "远程Emoji"
noJobs: "没有任务" noJobs: "没有任务"
@ -239,6 +240,8 @@ avatar: "头像"
banner: "Banner" banner: "Banner"
nsfw: "阅读注意" nsfw: "阅读注意"
disconnectedFromServer: "已从服务器断开连接" disconnectedFromServer: "已从服务器断开连接"
reload: "重新加载"
doNothing: "什么都不做"
reloadConfirm: "确定要重新加载吗" reloadConfirm: "确定要重新加载吗"
watch: "关注" watch: "关注"
unwatch: "取消关注" unwatch: "取消关注"
@ -280,11 +283,19 @@ enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可
recaptchaSiteKey: "网站密钥" recaptchaSiteKey: "网站密钥"
recaptchaSecretKey: "reCAPTCHA 密钥" recaptchaSecretKey: "reCAPTCHA 密钥"
antennas: "天线" antennas: "天线"
manageAntennas: "天线管理"
name: "名称" name: "名称"
antennaSource: "接收来源"
antennaKeywords: "包含关键字"
antennaExcludeKeywords: "排除关键字"
antennaKeywordsDescription: "使用空格分隔会产生AND规范并且使用换行符分隔会产生OR规范" antennaKeywordsDescription: "使用空格分隔会产生AND规范并且使用换行符分隔会产生OR规范"
notifyAntenna: "通知新帖子"
withFileAntenna: "仅带有附件的帖子"
serviceworker: "ServiceWorker" serviceworker: "ServiceWorker"
enableServiceworker: "启用ServiceWorker" enableServiceworker: "启用ServiceWorker"
antennaUsersDescription: "指定用户名,用换行符分隔"
caseSensitive: "区分大小写" caseSensitive: "区分大小写"
withReplies: "包括回复"
connectedTo: "您的账号已连到接以下社交账号" connectedTo: "您的账号已连到接以下社交账号"
notesAndReplies: "帖子与回复" notesAndReplies: "帖子与回复"
withFiles: "附件" withFiles: "附件"
@ -405,21 +416,42 @@ dayOverDayChanges: "与前一日相比"
accessibility: "辅助功能" accessibility: "辅助功能"
clinetSettings: "客户端设置" clinetSettings: "客户端设置"
accountSettings: "账户设置" accountSettings: "账户设置"
promotion: "推广"
promote: "推广"
numberOfDays: "天数" numberOfDays: "天数"
hideThisNote: "隐藏这条帖子" hideThisNote: "隐藏这条帖子"
showFeaturedNotesInTimeline: "在时间上显示热门推荐" showFeaturedNotesInTimeline: "在时间线上显示热门推荐"
objectStorage: "对象存储" objectStorage: "对象存储"
useObjectStorage: "使用对象存储" useObjectStorage: "使用对象存储"
objectStorageBaseUrl: "基本网址"
objectStorageBaseUrlDesc: "供参考的URL。如果使用CDN或Proxy则其URL为S3\"https://<bucket>.s3.amazonaws.com\"、GCS等\"https://storage-googleapis.proxy.ustclug.org/<bucket>\"。"
objectStorageBucket: "存储桶"
objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。"
objectStoragePrefix: "前缀"
objectStoragePrefixDesc: "它将存储在此前缀的目录下。"
objectStorageEndpoint: "端点"
objectStorageEndpointDesc: "S3默认情况下为空否则请为每个服务指定端点。 指定为“<host>”或“<host>:<port>”。"
objectStorageRegion: "可用区"
objectStorageRegionDesc: "指定一个可用区例如“xx-east-1”。 如果您的对象存储服务没有可用区概念请将其留空或填写“us-east-1”。"
objectStorageUseSSL: "使用SSL"
objectStorageUseSSLDesc: "如果不使用https进行API连接请关闭。"
serverLogs: "服务器日志" serverLogs: "服务器日志"
deleteAll: "删除全部" deleteAll: "删除全部"
showFixedPostForm: "在时间线顶部显示帖子表单" showFixedPostForm: "在时间线顶部显示帖子表单"
newNoteRecived: "有新的帖子" newNoteRecived: "有新的帖子"
useNotificationsPopup: "在弹出窗口中显示通知列表" sounds: "声音"
listen: "听"
none: "空" none: "空"
volume: "音量"
details: "详情"
chooseEmoji: "选择表情符号"
_sfx: _sfx:
note: "帖子" note: "帖子"
noteMy: "我的笔记"
notification: "通知" notification: "通知"
chat: "聊天" chat: "聊天"
chatBg: "聊天背景"
antenna: "天线接收"
_ago: _ago:
unknown: "未知" unknown: "未知"
future: "未来" future: "未来"
@ -449,11 +481,25 @@ _tutorial:
step3_4: "不知道说些什么好吗那就写下「Misskey我来啦」这样的话吧。" step3_4: "不知道说些什么好吗那就写下「Misskey我来啦」这样的话吧。"
step4_1: "将你的话语发布出去了吗?" step4_1: "将你的话语发布出去了吗?"
step4_2: "太棒了!现在你可以在你的时间线中看到你刚刚发布的帖子了。" step4_2: "太棒了!现在你可以在你的时间线中看到你刚刚发布的帖子了。"
step5_1: "接下来,关注其他人来使时间线更生动吧。"
step5_2: "{featured}将向您展示热门趋势的帖子。 {explore}将让您找到热门用户。 尝试关注您喜欢的人!"
step5_3: "要关注其他用户,请单击他的头像,然后在他的个人资料上按下“关注”按钮。"
step5_4: "如果用户的名称旁边有锁定图标,则该用户需要手动批准您的关注请求。"
step6_1: "现在,您将可以在时间线上看到其他用户的帖子。"
step6_2: "您还可以在其他人的帖子上进行「反应」,以快速做出简单回复。"
step6_3: "在他人的贴子上按下「+」图标,即可选择想要的表情来进行「反应」。"
step7_1: "对Misskey基本操作的简单介绍到此结束了。 辛苦了!"
step7_2: "如果你想了解更多有关Misskey的信息请参见{help}。"
step7_3: "接下来享受Misskey带来的乐趣吧🚀" step7_3: "接下来享受Misskey带来的乐趣吧🚀"
_2fa: _2fa:
alreadyRegistered: "此设备已被注册" alreadyRegistered: "此设备已被注册"
registerDevice: "注册设备" registerDevice: "注册设备"
registerKey: "注册密钥" registerKey: "注册密钥"
step1: "首先,在您的设备上安装二步验证应用程序,例如{a}或{b}。"
step2: "然后,扫描屏幕上显示的二维码。"
step3: "输入您的应用提供的动态口令以完成设置。"
step4: "从现在开始,任何登录操作都将要求您提供动态口令。"
securityKeyInfo: "您可以设置使用支持FIDO2的硬件安全密钥、指纹或设备上的PIN来保护您的登录过程。"
_permissions: _permissions:
"read:account": "查看账户信息" "read:account": "查看账户信息"
"write:account": "更改帐户信息" "write:account": "更改帐户信息"
@ -465,8 +511,11 @@ _permissions:
"write:favorites": "编辑收藏夹" "write:favorites": "编辑收藏夹"
"read:following": "查看关注信息" "read:following": "查看关注信息"
"write:following": "关注/取消关注" "write:following": "关注/取消关注"
"read:messaging": "查看消息"
"write:messaging": "撰写或删除消息"
"read:mutes": "查看屏蔽列表" "read:mutes": "查看屏蔽列表"
"write:mutes": "编辑屏蔽列表" "write:mutes": "编辑屏蔽列表"
"write:notes": "撰写或删除帖子"
"read:notifications": "查看通知" "read:notifications": "查看通知"
"write:notifications": "管理通知" "write:notifications": "管理通知"
"read:reactions": "查看回应" "read:reactions": "查看回应"
@ -479,10 +528,14 @@ _permissions:
"read:user-groups": "查看用户组" "read:user-groups": "查看用户组"
"write:user-groups": "操作用户组" "write:user-groups": "操作用户组"
_auth: _auth:
shareAccess: "您要授权允许“{name}”访问您的帐户吗?"
permissionAsk: "这个应用程序需要以下权限" permissionAsk: "这个应用程序需要以下权限"
_antennaSources: _antennaSources:
all: "所有帖子" all: "所有帖子"
homeTimeline: "已关注用户的帖子" homeTimeline: "已关注用户的帖子"
users: "来自特定用户的帖子"
userList: "来自特定清单中的帖子"
userGroup: "来自特定组中用户的帖子"
_weekday: _weekday:
sunday: "星期日" sunday: "星期日"
monday: "星期一" monday: "星期一"
@ -531,6 +584,7 @@ _poll:
remainingSeconds: "{s}秒后截止" remainingSeconds: "{s}秒后截止"
_visibility: _visibility:
public: "公开" public: "公开"
publicDescription: "您的帖子将出现在全局时间线上"
home: "首页" home: "首页"
homeDescription: "仅发送至首页的时间线" homeDescription: "仅发送至首页的时间线"
followers: "关注者" followers: "关注者"
@ -569,7 +623,13 @@ _charts:
usersTotal: "用户总数" usersTotal: "用户总数"
activeUsers: "活跃用户数" activeUsers: "活跃用户数"
notesIncDec: "帖子:增加/减少" notesIncDec: "帖子:增加/减少"
localNotesIncDec: "本地帖子量增减"
remoteNotesIncDec: "远程帖子量增减"
notesTotal: "帖子总数" notesTotal: "帖子总数"
filesIncDec: "文件总数增减"
filesTotal: "合计文件总数"
storageUsageIncDec: "存储空间用量增减"
storageUsageTotal: "合计存储空间用量"
_instanceCharts: _instanceCharts:
requests: "请求" requests: "请求"
users: "用户数量:增加/减少" users: "用户数量:增加/减少"
@ -579,6 +639,9 @@ _instanceCharts:
ff: "关注/被关注:数量变化" ff: "关注/被关注:数量变化"
ffTotal: "关注/被关注:总数" ffTotal: "关注/被关注:总数"
cacheSize: "缓存大小:增加/减少" cacheSize: "缓存大小:增加/减少"
cacheSizeTotal: "合计缓存大小"
files: "文件总数增减"
filesTotal: "合计文件总数"
_timelines: _timelines:
home: "首页" home: "首页"
local: "本地" local: "本地"

View File

@ -1,2 +1,391 @@
--- ---
_lang_: "中文(繁体)" _lang_: "中文(繁体)"
monthAndDay: "{month}月 {day}日"
search: "搜尋"
notifications: "通知"
username: "用戶名"
password: "密碼"
fetchingAsApObject: "從Fediverse尋找中..."
ok: "OK"
gotIt: "知道了"
cancel: "取消"
enterUsername: "輸入用戶名"
renotedBy: "由{user}轉發"
noNotes: "沒有筆記"
noNotifications: "沒有通知"
settings: "設定"
profile: "個人檔案"
timeline: "時間軸"
noAccountDescription: "此用戶還沒有自我介紹"
login: "登入"
loggingIn: "登入中"
logout: "登出"
signup: "註冊"
uploading: "上傳中"
save: "保存"
users: "用戶"
addUser: "新增用戶"
favorite: "收藏"
favorites: "收藏"
unfavorite: "取消收藏"
pin: "置頂"
unpin: "取消置頂"
copyContent: "複製內容"
copyLink: "複製連結"
delete: "刪除"
addToList: "添加至清單"
sendMessage: "發送訊息"
copyUsername: "複製用戶名"
reply: "回覆"
loadMore: "載入更多"
youGotNewFollower: "您有新的追隨者"
receiveFollowRequest: "收到追隨請求"
followRequestAccepted: "追隨請求已接受"
mentions: "提及"
importAndExport: "匯入 / 匯出"
import: "匯入"
export: "匯出"
files: "檔案"
download: "下載"
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?附加此檔案的筆記也會跟著不見。"
unfollowConfirm: "確定要取消對{name}的追隨嗎?"
exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。"
importRequested: "已請求匯入。這可能會花一點時間"
lists: "清單"
noLists: "沒有清單"
note: "筆記"
notes: "筆記"
following: "關注中"
followers: "追隨者"
followsYou: "追隨你的人"
createList: "建立清單"
manageLists: "管理清單"
error: "發生錯誤"
retry: "重試"
enterListName: "輸入清單名稱"
privacy: "隱私"
makeFollowManuallyApprove: "手動審核追隨請求"
defaultNoteVisibility: "預設的筆記隱私權"
follow: "追隨"
followRequest: "追隨請求"
followRequests: "追隨請求"
unfollow: "取消追隨"
followRequestPending: "追隨許可批准中"
enterEmoji: "輸入表情符號"
renote: "轉發筆記"
unrenote: "取消轉發筆記"
quote: "引用"
pinnedNote: "已置頂筆記"
you: "您"
clickToShow: "點擊查看"
sensitive: "敏感內容"
add: "新增"
reaction: "反應"
rememberNoteVisibility: "記住筆記隱私設定"
renameFile: "重新命名檔案"
attachCancel: "移除附件"
markAsSensitive: "標記為敏感內容"
unmarkAsSensitive: "取消標記為敏感內容"
enterFileName: "請輸入檔案名稱"
mute: "禁言"
unmute: "解除禁言"
block: "封鎖"
unblock: "解除封鎖"
suspend: "凍結"
unsuspend: "解凍"
blockConfirm: "確定要封鎖此用戶?"
unblockConfirm: "確定解除封鎖此用戶?"
suspendConfirm: "確定凍結此帳號?"
unsuspendConfirm: "確定解凍此帳號?"
selectList: "選擇清單"
customEmojis: "自訂表情符號"
emojiName: "表情符號名稱"
emojiUrl: "表情符號URL"
addEmoji: "新增表情符號"
flagAsBot: "此帳戶是Bot"
flagAsCat: "此帳戶是Cat"
autoAcceptFollowed: "自動許可關注"
addAcount: "新增帳戶"
loginFailed: "登入失敗"
general: "一般"
wallpaper: "壁紙"
removeWallpaper: "移除壁紙"
searchWith: "搜尋: {q}"
youHaveNoLists: "你沒有任何清單"
followConfirm: "你真的要關注{name}嗎?"
host: "主機"
selectUser: "選擇用戶"
recipient: "收件人"
annotation: "註解"
federation: "整合"
latestStatus: "最後狀態"
storageUsage: "已使用容量"
charts: "圖表"
perHour: "每小時"
perDay: "每日"
operations: "操作"
software: "軟體"
version: "版本"
withNFiles: "{n}個檔案"
monitor: "監視器"
network: "網路"
statistics: "統計"
clearQueue: "清除佇列"
clearQueueConfirmTitle: "確定要清除佇列嗎?"
clearCachedFiles: "清除快取資料"
muteAndBlock: "禁言 / 封鎖"
mutedUsers: "已禁言用戶"
blockedUsers: "已封鎖用戶"
noUsers: "無用戶"
editProfile: "編輯個人檔案"
noteDeleteConfirm: "確定刪除此筆記嗎?"
pinLimitExceeded: "不能再置頂更多筆記了"
intro: "Misskey安裝作業完成請創立管理員用戶"
done: "完成"
processing: "處理中"
preview: "預覽"
noCustomEmojis: "沒有表情符號"
federating: "整合檢索中"
blocked: "已封鎖"
suspended: "已凍結"
all: "全部"
subscribing: "訂閱中"
notResponding: "沒有回應"
changePassword: "修改密碼"
security: "安全性"
retypedNotMatch: "不相符的輸入內容"
currentPassword: "現在的密碼"
newPassword: "新的密碼"
newPasswordRetype: "新的密碼(再輸入一次)"
attachFile: "添加附件"
more: "更多!"
featured: "精選"
usernameOrUserId: "用戶名或用戶ID"
noSuchUser: "用戶不存在"
announcements: "公告"
imageUrl: "圖片URL"
remove: "刪除"
removed: "成功移除"
removeAreYouSure: "確定要刪掉「{x}」嗎?"
saved: "已保存"
messaging: "傳送訊息"
upload: "上傳"
fromDrive: "從雲端"
fromUrl: "從URL"
explore: "探索"
games: "Misskey 遊戲"
messageRead: "已讀"
recentUsedEmojis: "最近使用的表情符號"
noMoreHistory: "沒有更多歷史紀錄"
startMessaging: "開始傳送訊息"
nUsersRead: "{n}人已讀"
tos: "使用條款"
start: "開始"
home: "首頁"
activity: "動態"
birthday: "生日"
yearsOld: "{age}歲"
registeredDate: "註冊日期"
location: "位置"
theme: "外觀主題"
lightThemes: "明亮主題"
darkThemes: "灰暗主題"
drive: "雲端硬碟"
selectFile: "選擇檔案"
selectFiles: "選擇檔案"
renameFolder: "重新命名資料夾"
createFolder: "新增資料夾"
deleteFolder: "刪除資料夾"
addFile: "添加檔案"
emptyDrive: "雲端硬碟為空"
emptyFolder: "空的資料夾"
copyUrl: "複製URL"
rename: "重新命名"
avatar: "頭像"
banner: "橫幅"
nsfw: "敏感內容"
disconnectedFromServer: "與伺服器中斷連線"
reload: "重新載入"
doNothing: "無視"
reloadConfirm: "確定要重新嘗試嗎?"
watch: "關注"
unwatch: "取消關注"
accept: "接受"
reject: "拒絕"
maintainerName: "管理員名稱"
maintainerEmail: "管理員信箱"
tosUrl: "服務條款URL"
thisYear: "今年"
thisMonth: "本月"
today: "本日"
dayX: "{day}天"
monthX: "{month}月"
yearX: "{year}年"
pages: "頁面"
connectSerice: "連線"
disconnectSerice: "中斷連線"
enableLocalTimeline: "開啟本地時間軸"
enableGlobalTimeline: "開啟全球時間軸"
registration: "註冊"
enableRegistration: "開啟新用戶註冊"
invite: "邀請"
proxyRemoteFiles: "代理遠程檔案"
driveCapacityPerLocalAccount: "每個本地用戶的雲端容量"
inMb: "以Mbps為單位"
iconUrl: "圖像URL"
bannerUrl: "橫幅圖片URL"
basicInfo: "基本資訊"
pinnedUsers: "置頂用戶"
recaptcha: "reCAPTCHA"
enableRecaptcha: "啟用 reCAPTCHA"
recaptchaSiteKey: "網站金鑰"
recaptchaSecretKey: "金鑰"
name: "名稱"
serviceworker: "ServiceWorker"
enableServiceworker: "開啟 ServiceWorker"
caseSensitive: "區分大小寫"
notesAndReplies: "貼文與回覆"
withFiles: "附件"
silence: "禁言"
silenceConfirm: "確定要禁言此用戶嗎?"
unsilenceConfirm: "確定要解除禁言嗎?"
popularUsers: "熱門用戶"
recentlyUpdatedUsers: "最近發文的用戶"
recentlyRegisteredUsers: "新加入用戶"
recentlyDiscoveredUsers: "最近發現的用戶"
userList: "清單"
passwordLessLogin: "設置無密碼登入"
resetPassword: "重置密碼"
newPasswordIs: "新密碼為「{password}」"
post: "投稿"
posted: "投稿完成"
autoReloadWhenDisconnected: "和伺服器斷線時自動重新載入"
autoNoteWatch: "自動關注筆記"
autoNoteWatchDescription: "收到反應或回覆過的筆記的通知"
reduceUiAnimation: "減少介面的動態視覺"
share: "分享"
notFound: "找不到"
notFoundDescription: "找不到與指定URL回應的頁面"
uploadFolder: "預設上傳資料夾"
cacheClear: "清除暫存"
markAsReadAllNotifications: "標記所有通知為已讀"
markAsReadAllUnreadNotes: "標記所有筆記為已讀"
markAsReadAllTalkMessages: "標記所有訊息為已讀"
help: "幫助"
inputMessageHere: "在此輸入訊息"
close: "關閉"
group: "群組"
groups: "群組"
createGroup: "創建群組"
joinedGroups: "群組成員"
invites: "邀請"
groupName: "群組名稱"
members: "成員"
transfer: "轉讓"
messagingWithUser: "傳送訊息給其他用戶"
messagingWithGroup: "發送訊息至群組"
title: "標題"
text: "文字"
enable: "啟用"
next: "下一步"
retype: "重新輸入"
noteOf: "{user}的筆記"
inviteToGroup: "邀請至群組"
maxNoteTextLength: "筆記的字數限制"
quoteAttached: "引用"
quoteQuestion: "是否要引用?"
noMessagesYet: "沒有訊息"
newMessageExists: "有新的訊息"
onlyOneFileCanBeAttached: "只能添加一個附件"
signinRequired: "請先登入"
invitationCode: "邀請碼"
checking: "確認中"
available: "可用的"
unavailable: "不可用的"
usernameInvalidFormat: "可使用大小寫英文字母、數字和底線"
tooShort: "過短"
none: "無"
volume: "音量"
details: "詳細資訊"
_sfx:
note: "筆記"
noteMy: "我的筆記"
notification: "通知"
chat: "傳送訊息"
_ago:
unknown: "未知"
future: "未來"
justNow: "剛剛"
secondsAgo: "{n}秒前"
minutesAgo: "{n}分鐘前"
hoursAgo: "{n}小時前"
daysAgo: "{n}天前"
weeksAgo: "{n}周前"
monthsAgo: "{n}個月前"
yearsAgo: "{n}年前"
_time:
second: "秒"
minute: "分鐘"
hour: "小時"
day: "天"
_tutorial:
title: "Misskey使用方法"
step1_1: "歡迎!"
step1_2: "此為「時間軸」頁面,它會按照時間順序顯示你「追隨」的人的「筆記」"
step1_3: "由於你沒有發布任何筆記,也沒有追隨任何人,所以你的時間軸目前是空的。"
step2_1: "在發文或追隨其他人之前先讓我們設定一下個人資料吧。"
step2_2: "提供一些關於自己的資訊來讓其他人更有追隨你的意願。"
step3_1: "個人資料都打理好了嗎?"
step3_2: "下一步讓我們來試試看發個文,按一下畫面上的鉛筆圖示來開始"
step3_3: "輸入完內容後,按視窗右上角的按鈕來發文"
step3_4: "不知道該寫什麼內容嗎試試看「開始使用Misskey了」如何。"
step4_1: "筆記發出去了嗎?"
step4_2: "如果你的筆記有顯示在時間軸上,就代表已經發文成功。"
step5_1: "現在試試看追隨其他人來讓你的時間軸變得更生動吧。"
step5_3: "想要追隨其他人,只要點擊他們的頭像並按「追隨」即可。"
step5_4: "如果使用者的名字旁有鎖頭的圖示,代表他們需要手動核准你的追隨請求。"
step6_1: "現在你可以在時間軸上看到其他用戶的貼文"
step6_2: "你也可以在其他人的貼文上進行「反應」來表達簡單的回覆。"
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
step7_1: "以上為Misskey的基本操作說明教學在此告一段落。辛苦了。"
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
_widgets:
notifications: "通知"
timeline: "時間軸"
activity: "動態"
_cw:
show: "載入更多"
_poll:
deadlineTime: "小時"
_visibility:
home: "首頁"
followers: "追隨者"
_profile:
name: "名稱"
username: "用戶名"
_exportOrImport:
followingList: "關注中"
muteList: "禁言"
blockingList: "封鎖"
userLists: "清單"
_timelines:
home: "首頁"
_pages:
script:
categories:
list: "清單"
blocks:
_join:
arg1: "清單"
_randomPick:
arg1: "清單"
_dailyRandomPick:
arg1: "清單"
_seedRandomPick:
arg2: "清單"
_pick:
arg1: "清單"
_listLen:
arg1: "清單"
types:
array: "清單"

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", "name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>", "author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.17.0", "version": "12.23.0",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",
@ -21,7 +21,7 @@
"gulp": "gulp build", "gulp": "gulp build",
"clean": "gulp clean", "clean": "gulp clean",
"cleanall": "gulp cleanall", "cleanall": "gulp cleanall",
"lint": "gulp lint", "lint": "tslint 'src/**/*.ts'",
"test": "cross-env TS_NODE_FILES=true gulp test", "test": "cross-env TS_NODE_FILES=true gulp test",
"format": "gulp format" "format": "gulp format"
}, },
@ -250,7 +250,6 @@
"vue-meta": "2.3.2", "vue-meta": "2.3.2",
"vue-prism-component": "1.1.1", "vue-prism-component": "1.1.1",
"vue-router": "3.1.5", "vue-router": "3.1.5",
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.4.5", "vue-svg-inline-loader": "1.4.5",
"vue-template-compiler": "2.6.11", "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; 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 getVbyList(h: ASN1S, currentIndex: Idx<ASN1ObjectString>, nthList: Mutable<Nth[]>, checkingTag?: string, removeUnusedbits?: boolean): ASN1V;
public static hextooidstr(hex: ASN1OIDV): OID; public static hextooidstr(hex: ASN1OIDV): OID;
@ -620,9 +621,7 @@ declare module 'jsrsasign' {
public encrypt(text: string): HexString | null; public encrypt(text: string): HexString | null;
public encryptOAEP(text: string, hash?: string, hashLen?: number): HexString | null; public encryptOAEP(text: string, hash?: string | ((s: string) => string), hashLen?: number): HexString | null;
public encryptOAEP(text: string, hash?: (s: string) => string, hashLen?: number): HexString | null;
//// RSA PRIVATE //// RSA PRIVATE
@ -638,9 +637,7 @@ declare module 'jsrsasign' {
public decrypt(ctext: HexString): string; public decrypt(ctext: HexString): string;
public decryptOAEP(ctext: HexString, hash?: string, hashLen?: number): string | null; public decryptOAEP(ctext: HexString, hash?: string | ((s: string) => string), hashLen?: number): string | null;
public encryptOAEP(ctext: HexString, hash?: (s: string) => string, hashLen?: number): string | null;
//// RSA PEM //// RSA PEM

View File

@ -51,11 +51,7 @@
<fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span> <fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</router-link> </router-link>
<template v-if="$store.getters.isSignedIn"> <template v-if="$store.getters.isSignedIn">
<button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.state.device.useNotificationsPopup"> <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>
</button>
<router-link class="item notifications" active-class="active" to="/my/notifications" ref="notificationButton" v-else>
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span> <fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i> <i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
</router-link> </router-link>
@ -149,17 +145,12 @@
<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 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-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-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button>
<button v-if="$store.getters.isSignedIn && $store.state.device.useNotificationsPopup" 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 && !$store.state.device.useNotificationsPopup" 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> <button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
</div> </div>
<button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button> <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"/> <stream-indicator v-if="$store.getters.isSignedIn"/>
</div> </div>
</template> </template>
@ -173,7 +164,7 @@ import { v4 as uuid } from 'uuid';
import i18n from './i18n'; import i18n from './i18n';
import { host, instanceName } from './config'; import { host, instanceName } from './config';
import { search } from './scripts/search'; import { search } from './scripts/search';
import contains from './scripts/contains'; import { isDeviceDarkmode } from './scripts/is-device-darkmode';
import MkToast from './components/toast.vue'; import MkToast from './components/toast.vue';
const DESKTOP_THRESHOLD = 1100; const DESKTOP_THRESHOLD = 1100;
@ -183,7 +174,6 @@ export default Vue.extend({
components: { components: {
XClock: () => import('./components/header-clock.vue').then(m => m.default), 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), MkButton: () => import('./components/ui/button.vue').then(m => m.default),
XDraggable: () => import('vuedraggable'), XDraggable: () => import('vuedraggable'),
}, },
@ -194,7 +184,6 @@ export default Vue.extend({
pageKey: 0, pageKey: 0,
showNav: false, showNav: false,
searching: false, searching: false,
notificationsOpen: false,
accounts: [], accounts: [],
lists: [], lists: [],
connection: null, connection: null,
@ -226,29 +215,20 @@ export default Vue.extend({
watch:{ watch:{
$route(to, from) { $route(to, from) {
this.pageKey++; this.pageKey++;
this.notificationsOpen = false;
this.showNav = false; this.showNav = false;
this.canBack = (window.history.length > 0 && !['index'].includes(to.name)); 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() { isDesktop() {
if (this.isDesktop) this.adjustWidgetsWidth(); if (this.isDesktop) this.adjustWidgetsWidth();
} }
}, },
created() { created() {
if (this.$store.state.device.syncDeviceDarkMode) {
this.$store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
}
if (this.$store.getters.isSignedIn) { if (this.$store.getters.isSignedIn) {
this.connection = this.$root.stream.useSharedConnection('main'); this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('notification', this.onNotification); this.connection.on('notification', this.onNotification);
@ -565,18 +545,7 @@ export default Vue.extend({
}); });
} }
const audio = new Audio(`/assets/sounds/${this.$store.state.device.sfxNotification}.mp3`); this.$root.sound('notification');
audio.volume = this.$store.state.device.sfxVolume;
audio.play();
},
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;
}, },
widgetFunc(id) { widgetFunc(id) {
@ -628,18 +597,6 @@ export default Vue.extend({
</script> </script>
<style lang="scss" scoped> <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-enter-active,
.nav-leave-active { .nav-leave-active {
opacity: 1; opacity: 1;
@ -666,7 +623,7 @@ export default Vue.extend({
$header-height: 60px; $header-height: 60px;
$nav-width: 250px; $nav-width: 250px;
$nav-icon-only-width: 74px; $nav-icon-only-width: 74px;
$main-width: 700px; $main-width: 650px;
$ui-font-size: 1em; $ui-font-size: 1em;
$nav-icon-only-threshold: 1300px; $nav-icon-only-threshold: 1300px;
$nav-hide-threshold: 700px; $nav-hide-threshold: 700px;
@ -989,17 +946,21 @@ export default Vue.extend({
> main { > main {
width: $main-width; width: $main-width;
min-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) { @media (max-width: $side-hide-threshold) {
min-width: 0; min-width: 0;
} }
> .content { > .content {
padding: 16px; > * {
box-sizing: border-box; &:not(.full) {
padding: var(--margin) 0;
}
@media (max-width: 500px) { &:not(.naked) {
padding: 8px; background: var(--pageBg);
}
} }
} }
@ -1037,6 +998,7 @@ export default Vue.extend({
> .widgets { > .widgets {
box-sizing: border-box; box-sizing: border-box;
margin-left: var(--margin);
@media (max-width: $side-hide-threshold) { @media (max-width: $side-hide-threshold) {
display: none; display: none;
@ -1189,34 +1151,5 @@ export default Vue.extend({
} }
} }
} }
> .notifications {
position: fixed;
top: 32px;
left: 0;
right: 0;
margin: 0 auto;
padding: 8px 8px 0 8px;
z-index: 10001;
width: 350px;
height: 400px;
box-sizing: border-box;
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: auto;
@media (max-width: 800px) {
width: 320px;
height: 350px;
}
@media (max-width: 500px) {
width: 290px;
height: 310px;
}
}
} }
</style> </style>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
<template> <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"> <template v-for="(item, i) in items">
<slot :item="item" :i="i"></slot> <slot :item="item" :i="i"></slot>
<div class="separator" :key="item.id + '_date'" v-if="showDate(i, item)"> <div class="separator" :key="item.id + '_date'" v-if="showDate(i, item)">
@ -9,7 +9,7 @@
</p> </p>
</div> </div>
</template> </template>
</sequential-entrance> </component>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -27,7 +27,8 @@ export default Vue.extend({
}, },
direction: { direction: {
type: String, type: String,
required: false required: false,
default: 'down'
}, },
reversed: { reversed: {
type: Boolean, type: Boolean,
@ -63,12 +64,38 @@ export default Vue.extend({
}, },
focus() { focus() {
this.$refs.list.focus(); this.$slots.default[0].elm.focus();
} }
} }
}); });
</script> </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> <style lang="scss" scoped>
.sqadhkmv { .sqadhkmv {
> .separator { > .separator {
@ -82,8 +109,6 @@ export default Vue.extend({
line-height: 32px; line-height: 32px;
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
border-radius: 64px;
background: var(--dateLabelBg);
color: var(--dateLabelFg); color: var(--dateLabelFg);
> span { > span {

View File

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

View File

@ -139,9 +139,9 @@ export default Vue.extend({
rename() { rename() {
this.$root.dialog({ this.$root.dialog({
title: this.$t('contextmenu.rename-file'), title: this.$t('renameFile'),
input: { input: {
placeholder: this.$t('contextmenu.input-new-file-name'), placeholder: this.$t('inputNewFileName'),
default: this.file.name, default: this.file.name,
allowEmpty: false allowEmpty: false
} }

View File

@ -137,14 +137,14 @@ export default Vue.extend({
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
this.$root.dialog({ this.$root.dialog({
title: this.$t('unable-to-process'), title: this.$t('unableToProcess'),
text: this.$t('circular-reference-detected') text: this.$t('circularReferenceFolder')
}); });
break; break;
default: default:
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('unhandled-error') text: this.$t('error')
}); });
} }
}); });
@ -177,9 +177,9 @@ export default Vue.extend({
rename() { rename() {
this.$root.dialog({ this.$root.dialog({
title: this.$t('contextmenu.rename-folder'), title: this.$t('renameFolder'),
input: { input: {
placeholder: this.$t('contextmenu.input-new-folder-name'), placeholder: this.$t('inputNewFolderName'),
default: this.folder.name default: this.folder.name
} }
}).then(({ canceled, result: name }) => { }).then(({ canceled, result: name }) => {
@ -206,14 +206,14 @@ export default Vue.extend({
case 'b0fc8a17-963c-405d-bfbc-859a487295e1': case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
title: this.$t('unable-to-delete'), title: this.$t('unableToDelete'),
text: this.$t('has-child-files-or-folders') text: this.$t('hasChildFilesOrFolders')
}); });
break; break;
default: default:
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('unable-to-delete') text: this.$t('unableToDelete')
}); });
} }
}); });

View File

@ -263,14 +263,14 @@ export default Vue.extend({
switch (err) { switch (err) {
case 'detected-circular-definition': case 'detected-circular-definition':
this.$root.dialog({ this.$root.dialog({
title: this.$t('unable-to-process'), title: this.$t('unableToProcess'),
text: this.$t('circular-reference-detected') text: this.$t('circularReferenceFolder')
}); });
break; break;
default: default:
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('unhandled-error') text: this.$t('error')
}); });
} }
}); });
@ -284,9 +284,9 @@ export default Vue.extend({
urlUpload() { urlUpload() {
this.$root.dialog({ this.$root.dialog({
title: this.$t('url-upload'), title: this.$t('uploadFromUrl'),
input: { input: {
placeholder: this.$t('url-of-file') placeholder: this.$t('uploadFromUrlDescription')
} }
}).then(({ canceled, result: url }) => { }).then(({ canceled, result: url }) => {
if (canceled) return; if (canceled) return;
@ -296,17 +296,17 @@ export default Vue.extend({
}); });
this.$root.dialog({ this.$root.dialog({
title: this.$t('url-upload-requested'), title: this.$t('uploadFromUrlRequested'),
text: this.$t('may-take-time') text: this.$t('uploadFromUrlMayTakeTime')
}); });
}); });
}, },
createFolder() { createFolder() {
this.$root.dialog({ this.$root.dialog({
title: this.$t('create-folder'), title: this.$t('createFolder'),
input: { input: {
placeholder: this.$t('folder-name') placeholder: this.$t('folderName')
} }
}).then(({ canceled, result: name }) => { }).then(({ canceled, result: name }) => {
if (canceled) return; if (canceled) return;
@ -321,9 +321,9 @@ export default Vue.extend({
renameFolder(folder) { renameFolder(folder) {
this.$root.dialog({ this.$root.dialog({
title: this.$t('contextmenu.rename-folder'), title: this.$t('renameFolder'),
input: { input: {
placeholder: this.$t('contextmenu.input-new-folder-name'), placeholder: this.$t('inputNewFolderName'),
default: folder.name default: folder.name
} }
}).then(({ canceled, result: name }) => { }).then(({ canceled, result: name }) => {
@ -349,14 +349,14 @@ export default Vue.extend({
case 'b0fc8a17-963c-405d-bfbc-859a487295e1': case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
title: this.$t('unable-to-delete'), title: this.$t('unableToDelete'),
text: this.$t('has-child-files-or-folders') text: this.$t('hasChildFilesOrFolders')
}); });
break; break;
default: default:
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('unable-to-delete') text: this.$t('unableToDelete')
}); });
} }
}); });

View File

@ -2,12 +2,11 @@
<x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }"> <x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }">
<div class="omfetrab"> <div class="omfetrab">
<header> <header>
<button v-for="category in categories" <button v-for="(category, i) in categories"
class="_button" class="_button"
:title="category.text"
@click="go(category)" @click="go(category)"
:class="{ active: category.isActive }" :class="{ active: category.isActive }"
:key="category.text" :key="i"
> >
<fa :icon="category.icon" fixed-width/> <fa :icon="category.icon" fixed-width/>
</button> </button>
@ -15,7 +14,7 @@
<div class="emojis"> <div class="emojis">
<template v-if="categories[0].isActive"> <template v-if="categories[0].isActive">
<header class="category"><fa :icon="faHistory" fixed-width/> {{ $t('recentUsedEmojis') }}</header> <header class="category"><fa :icon="faHistory" fixed-width/> {{ $t('recentUsed') }}</header>
<div class="list"> <div class="list">
<button v-for="(emoji, i) in ($store.state.device.recentEmojis || [])" <button v-for="(emoji, i) in ($store.state.device.recentEmojis || [])"
class="_button" class="_button"
@ -27,9 +26,10 @@
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/> <img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
</button> </button>
</div> </div>
<header class="category"><fa :icon="faAsterisk" fixed-width/> {{ $t('customEmojis') }}</header>
</template> </template>
<header class="category"><fa :icon="categories.find(x => x.isActive).icon" fixed-width/> {{ categories.find(x => x.isActive).text }}</header>
<template v-if="categories.find(x => x.isActive).name"> <template v-if="categories.find(x => x.isActive).name">
<div class="list"> <div class="list">
<button v-for="emoji in emojilist.filter(e => e.category === categories.find(x => x.isActive).name)" <button v-for="emoji in emojilist.filter(e => e.category === categories.find(x => x.isActive).name)"
@ -92,47 +92,38 @@ export default Vue.extend({
customEmojis: {}, customEmojis: {},
faGlobe, faHistory, faGlobe, faHistory,
categories: [{ categories: [{
text: this.$t('customEmoji'),
icon: faAsterisk, icon: faAsterisk,
isActive: true isActive: true
}, { }, {
name: 'people', name: 'people',
text: this.$t('people'),
icon: faLaugh, icon: faLaugh,
isActive: false isActive: false
}, { }, {
name: 'animals_and_nature', name: 'animals_and_nature',
text: this.$t('animals-and-nature'),
icon: faLeaf, icon: faLeaf,
isActive: false isActive: false
}, { }, {
name: 'food_and_drink', name: 'food_and_drink',
text: this.$t('food-and-drink'),
icon: faUtensils, icon: faUtensils,
isActive: false isActive: false
}, { }, {
name: 'activity', name: 'activity',
text: this.$t('activity'),
icon: faFutbol, icon: faFutbol,
isActive: false isActive: false
}, { }, {
name: 'travel_and_places', name: 'travel_and_places',
text: this.$t('travel-and-places'),
icon: faCity, icon: faCity,
isActive: false isActive: false
}, { }, {
name: 'objects', name: 'objects',
text: this.$t('objects'),
icon: faDice, icon: faDice,
isActive: false isActive: false
}, { }, {
name: 'symbols', name: 'symbols',
text: this.$t('symbols'),
icon: faHeart, icon: faHeart,
isActive: false isActive: false
}, { }, {
name: 'flags', name: 'flags',
text: this.$t('flags'),
icon: faFlag, icon: faFlag,
isActive: false isActive: false
}] }]

View File

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

View File

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

View File

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

View File

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

View File

@ -40,7 +40,8 @@ export default Vue.extend({
}, },
data() { data() {
return { return {
gridInnerStyle: {} gridInnerStyle: {},
sizeWaiting: false
} }
}, },
mounted() { mounted() {
@ -50,12 +51,22 @@ export default Vue.extend({
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.size); window.removeEventListener('resize', this.size);
}, },
activated() {
this.size();
},
methods: { methods: {
previewable(file) { previewable(file) {
return file.type.startsWith('video') || file.type.startsWith('image'); return file.type.startsWith('video') || file.type.startsWith('image');
}, },
size() { size() {
// for Safari bug // for Safari bug
if (this.sizeWaiting) return;
this.sizeWaiting = true;
window.requestAnimationFrame(() => {
this.sizeWaiting = false;
if (this.$refs.gridOuter) { if (this.$refs.gridOuter) {
let height = 287; let height = 287;
const parent = this.$props.parentElement || this.$parent.$el; const parent = this.$props.parentElement || this.$parent.$el;
@ -70,6 +81,7 @@ export default Vue.extend({
} else { } else {
this.gridInnerStyle = {}; this.gridInnerStyle = {};
} }
});
} }
} }
}); });

View File

@ -1,6 +1,6 @@
<template> <template>
<x-popup :source="source" :no-center="noCenter" :fixed="fixed" :width="width" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap"> <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)"> <template v-for="(item, i) in items.filter(item => item !== undefined)">
<div v-if="item === null" class="divider" :key="i"></div> <div v-if="item === null" class="divider" :key="i"></div>
<span v-else-if="item.type === 'label'" class="label item" :key="i"> <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> <i v-if="item.indicate"><fa :icon="faCircle"/></i>
</button> </button>
</template> </template>
</sequential-entrance> </div>
</x-popup> </x-popup>
</template> </template>
@ -91,7 +91,7 @@ export default Vue.extend({
mounted() { mounted() {
if (this.viaKeyboard) { if (this.viaKeyboard) {
this.$nextTick(() => { 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; font-size: 0.8em;
} }
::v-deep > code {
word-break: break-all;
}
::v-deep .title { ::v-deep .title {
text-align: center; text-align: center;
border-bottom: solid 1px var(--divider); border-bottom: solid 1px var(--divider);

View File

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

View File

@ -79,14 +79,14 @@
<div class="deleted" v-if="appearNote.deletedAt != null">{{ $t('deleted') }}</div> <div class="deleted" v-if="appearNote.deletedAt != null">{{ $t('deleted') }}</div>
</div> </div>
</article> </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> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; 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 { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse'; import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array'; import { sum, unique } from '../../prelude/array';
import i18n from '../i18n'; import i18n from '../i18n';
@ -142,7 +142,7 @@ export default Vue.extend({
replies: [], replies: [],
showContent: false, showContent: false,
hideThisNote: false, hideThisNote: false,
faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan
}; };
}, },
@ -392,7 +392,7 @@ export default Vue.extend({
}] }]
source: this.$refs.renoteButton, source: this.$refs.renoteButton,
viaKeyboard viaKeyboard
}).then(this.focus); });
}, },
renoteDirectly() { renoteDirectly() {
@ -460,6 +460,22 @@ export default Vue.extend({
}); });
}, },
delEdit() {
this.$root.dialog({
type: 'warning',
text: this.$t('deleteAndEditConfirm'),
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) return;
this.$root.api('notes/delete', {
noteId: this.appearNote.id
});
this.$root.post({ initialNote: this.appearNote, renote: this.appearNote.renote, reply: this.appearNote.reply });
});
},
toggleFavorite(favorite: boolean) { toggleFavorite(favorite: boolean) {
this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
noteId: this.appearNote.id noteId: this.appearNote.id
@ -489,6 +505,11 @@ export default Vue.extend({
noteId: this.appearNote.id noteId: this.appearNote.id
}); });
menu = [{ menu = [{
type: 'link',
icon: faInfoCircle,
text: this.$t('details'),
to: '/notes/' + this.appearNote.id
}, null, {
icon: faCopy, icon: faCopy,
text: this.$t('copyContent'), text: this.$t('copyContent'),
action: this.copyContent action: this.copyContent
@ -542,6 +563,11 @@ export default Vue.extend({
), ),
...(this.appearNote.userId == this.$store.state.i.id ? [ ...(this.appearNote.userId == this.$store.state.i.id ? [
null, null,
{
icon: faEdit,
text: this.$t('deleteAndEdit'),
action: this.delEdit
},
{ {
icon: faTrashAlt, icon: faTrashAlt,
text: this.$t('delete'), text: this.$t('delete'),
@ -679,6 +705,7 @@ export default Vue.extend({
.note { .note {
position: relative; position: relative;
transition: box-shadow 0.1s ease; transition: box-shadow 0.1s ease;
overflow: hidden;
&.max-width_500px { &.max-width_500px {
font-size: 0.9em; font-size: 0.9em;
@ -744,14 +771,6 @@ export default Vue.extend({
opacity: 1; opacity: 1;
} }
> *:first-child {
border-radius: var(--radius) var(--radius) 0 0;
}
> *:last-child {
border-radius: 0 0 var(--radius) var(--radius);
}
> .info { > .info {
display: flex; display: flex;
align-items: center; align-items: center;
@ -779,6 +798,11 @@ export default Vue.extend({
padding-top: 8px; padding-top: 8px;
} }
> .reply-to {
opacity: 0.7;
padding-bottom: 0;
}
> .renote { > .renote {
display: flex; display: flex;
align-items: center; align-items: center;
@ -932,5 +956,9 @@ export default Vue.extend({
} }
} }
} }
> .reply {
border-top: solid 1px var(--divider);
}
} }
</style> </style>

View File

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

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="mk-notifications" :class="{ page }"> <div class="mk-notifications">
<x-list class="notifications" :items="items" v-slot="{ item: notification }"> <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-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="notification" :class="{ _panel: page }" :key="notification.id"/> <x-notification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
</x-list> </x-list>
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching"> <button class="_panel _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template> <template v-if="moreFetching"><mk-loading inline/></template>
</button> </button>
<p class="empty" v-if="empty">{{ $t('noNotifications') }}</p> <p class="empty" v-if="empty">{{ $t('noNotifications') }}</p>
@ -18,7 +18,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n'; import i18n from '../i18n';
import paging from '../scripts/paging'; import paging from '../scripts/paging';
import XNotification from './notification.vue'; import XNotification from './notification.vue';
@ -43,11 +42,6 @@ export default Vue.extend({
type: String, type: String,
required: false required: false
}, },
page: {
type: Boolean,
required: false,
default: false
}
}, },
data() { data() {
@ -60,7 +54,6 @@ export default Vue.extend({
includeTypes: this.type ? [this.type] : undefined includeTypes: this.type ? [this.type] : undefined
}) })
}, },
faSpinner
}; };
}, },
@ -94,35 +87,10 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-notifications { .mk-notifications {
&.page {
> .notifications { > .notifications {
> ::v-deep * { > ::v-deep * {
margin-bottom: var(--margin); //margin-bottom: var(--margin);
} margin-bottom: 0;
}
}
&:not(.page) {
> .notifications {
> ::v-deep * {
margin-bottom: 8px;
}
> .notification {
background: var(--panel);
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
}
> .more {
display: block;
width: 100%;
padding: 16px;
> [data-icon] {
margin-right: 4px;
} }
} }

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 { faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n'; import i18n from '../i18n';
import { erase } from '../../prelude/array'; import { erase } from '../../prelude/array';
import { addTimespan } from '../../prelude/time'; import { addTime } from '../../prelude/time';
import { formatDateTimeString } from '../../misc/format-time-string'; import { formatDateTimeString } from '../../misc/format-time-string';
import MkInput from './ui/input.vue'; import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue'; import MkSelect from './ui/select.vue';
@ -73,7 +73,7 @@ export default Vue.extend({
choices: ['', ''], choices: ['', ''],
multiple: false, multiple: false,
expiration: 'infinite', 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', atTime: '00:00',
after: 0, after: 0,
unit: 'second', unit: 'second',

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="ulveipglmagnxfgvitaxyszerjwiqmwl"> <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> <div class="bg" ref="bg" v-if="show" @click="close()"></div>
</transition> </transition>
<div class="main" ref="main" @click.self="close()" @keydown="onKeydown"> <div class="main" ref="main" @click.self="close()" @keydown="onKeydown">
@ -17,7 +17,8 @@
:initial-note="initialNote" :initial-note="initialNote"
:instant="instant" :instant="instant"
@posted="onPosted" @posted="onPosted"
@cancel="onCanceled"/> @cancel="onCanceled"
style="border-radius: var(--radius);"/>
</transition> </transition>
</div> </div>
</div> </div>

View File

@ -300,6 +300,7 @@ export default Vue.extend({
}); });
} }
this.visibility = init.visibility; this.visibility = init.visibility;
this.localOnly = init.localOnly;
this.quoteId = init.renote ? init.renote.id : null; this.quoteId = init.renote ? init.renote.id : null;
} }
@ -586,7 +587,6 @@ export default Vue.extend({
<style lang="scss" scoped> <style lang="scss" scoped>
.gafaadew { .gafaadew {
background: var(--panel); background: var(--panel);
border-radius: var(--radius);
> header { > header {
z-index: 1000; z-index: 1000;

View File

@ -1,20 +1,9 @@
<template> <template>
<x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap"> <x-popup :source="source" ref="popup" @closed="() => { $emit('closed'); destroyDom(); }" v-hotkey.global="keymap">
<div class="rdfaahpb"> <div class="rdfaahpb">
<transition-group <div class="buttons" ref="buttons" :class="{ showFocus }">
name="reaction-fade" <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>
tag="div" </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>
<input class="text" v-model="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }"> <input class="text" v-model="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
</div> </div>
</x-popup> </x-popup>
@ -84,7 +73,7 @@ export default Vue.extend({
watch: { watch: {
focus(i) { focus(i) {
this.$refs.buttons.children[i].elm.focus(); this.$refs.buttons.children[i].focus();
} }
}, },
@ -129,21 +118,7 @@ export default Vue.extend({
}, },
choose() { choose() {
this.$refs.buttons.children[this.focus].elm.click(); this.$refs.buttons.children[this.focus].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)
}, },
} }
}); });

View File

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

View File

@ -1,5 +1,5 @@
<template> <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"/> <x-reaction v-for="(count, reaction) in note.reactions" :reaction="reaction" :count="count" :is-initial="initialReactions.has(reaction)" :note="note" :key="reaction"/>
</div> </div>
</template> </template>
@ -32,7 +32,7 @@ export default Vue.extend({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-reactions-viewer { .tdflqwzn {
margin: 4px -2px 0 -2px; margin: 4px -2px 0 -2px;
&:empty { &: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> <template>
<x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }"> <x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }">
<template #header>{{ $t('login') }}</template> <template #header>{{ $t('login') }}</template>
<x-signin :auto-set="autoSet" @login="onLogin"/> <mk-signin :auto-set="autoSet" @login="onLogin"/>
</x-window> </x-window>
</template> </template>
@ -9,13 +9,13 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../i18n'; import i18n from '../i18n';
import XWindow from './window.vue'; import XWindow from './window.vue';
import XSignin from './signin.vue'; import MkSignin from './signin.vue';
export default Vue.extend({ export default Vue.extend({
i18n, i18n,
components: { components: {
XSignin, MkSignin,
XWindow, XWindow,
}, },

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

@ -155,7 +155,7 @@ export default Vue.extend({
if (err === null) return; if (err === null) return;
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('login-failed') text: this.$t('signinFailed')
}); });
this.signing = false; this.signing = false;
}); });
@ -176,7 +176,7 @@ export default Vue.extend({
}).catch(() => { }).catch(() => {
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('login-failed') text: this.$t('signinFailed')
}); });
this.challengeData = null; this.challengeData = null;
this.totpLogin = false; this.totpLogin = false;

View File

@ -53,11 +53,7 @@ export default Vue.extend({
(this.$refs.tl as any).prepend(note); (this.$refs.tl as any).prepend(note);
if (this.sound) { if (this.sound) {
const audio = new Audio(note.userId === this.$store.state.i.id this.$root.sound(note.userId === this.$store.state.i.id ? 'noteMy' : 'note');
? `/assets/sounds/${this.$store.state.device.sfxNoteMy}.mp3`
: `/assets/sounds/${this.$store.state.device.sfxNote}.mp3`);
audio.volume = this.$store.state.device.sfxVolume;
audio.play();
} }
}; };

View File

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

View File

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

View File

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

@ -152,12 +152,17 @@ export default Vue.extend({
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 16px;
height: 32px; height: 32px;
background: var(--panel); background: none;
border: none; border: none;
border-radius: 0; border-radius: 0;
outline: none; outline: none;
box-shadow: none; box-shadow: none;
color: var(--fg); color: var(--fg);
option,
optgroup {
background: var(--bg);
}
} }
> .prefix, > .prefix,

View File

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

View File

@ -53,6 +53,7 @@ export default Vue.extend({
return { return {
u: null, u: null,
show: false, show: false,
closed: false,
top: 0, top: 0,
left: 0, left: 0,
}; };
@ -68,6 +69,7 @@ export default Vue.extend({
{ userId: this.user }; { userId: this.user };
this.$root.api('users/show', query).then(user => { this.$root.api('users/show', query).then(user => {
if (this.closed) return;
this.u = user; this.u = user;
this.show = true; this.show = true;
}); });
@ -83,6 +85,7 @@ export default Vue.extend({
methods: { methods: {
close() { close() {
this.closed = true;
this.show = false; this.show = false;
if (this.$refs.content) (this.$refs.content as any).style.pointerEvents = 'none'; 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> <button class="_button" @click="close()"><fa :icon="faTimes"/></button>
</div> </div>
<sequential-entrance class="users"> <div class="users">
<router-link v-for="(item, i) in items" class="user" :key="item.id" :to="extract ? extract(item) : item | userPage"> <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"/> <mk-avatar :user="extract ? extract(item) : item" class="avatar" :disable-link="true"/>
<div class="body"> <div class="body">
<mk-user-name :user="extract ? extract(item) : item" class="name"/> <mk-user-name :user="extract ? extract(item) : item" class="name"/>
<mk-acct :user="extract ? extract(item) : item" class="acct"/> <mk-acct :user="extract ? extract(item) : item" class="acct"/>
</div> </div>
</router-link> </router-link>
</sequential-entrance> </div>
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching"> <button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>

View File

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

View File

@ -3,8 +3,10 @@ import Vue from 'vue';
import userPreview from './user-preview'; import userPreview from './user-preview';
import autocomplete from './autocomplete'; import autocomplete from './autocomplete';
import size from './size'; import size from './size';
import particle from './particle';
Vue.directive('autocomplete', autocomplete); Vue.directive('autocomplete', autocomplete);
Vue.directive('userPreview', userPreview); Vue.directive('userPreview', userPreview);
Vue.directive('user-preview', userPreview); Vue.directive('user-preview', userPreview);
Vue.directive('size', size); 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

@ -39,11 +39,15 @@ export default {
self.hideTimer = setTimeout(self.close, 500); self.hideTimer = setTimeout(self.close, 500);
}); });
self.checkTimer = setInterval(() => {
if (!document.body.contains(el)) self.close();
}, 1000);
document.body.appendChild(self.tag.$el); 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', () => { el.addEventListener('mouseover', () => {
@ -66,9 +70,6 @@ export default {
unbind(el, binding, vn) { unbind(el, binding, vn) {
const self = el._userPreviewDirective_; const self = el._userPreviewDirective_;
clearTimeout(self.showTimer);
clearTimeout(self.hideTimer);
clearInterval(self.checkTimer); clearInterval(self.checkTimer);
self.close();
} }
}; };

View File

@ -18,7 +18,7 @@ import PostFormDialog from './components/post-form-dialog.vue';
import Dialog from './components/dialog.vue'; import Dialog from './components/dialog.vue';
import Menu from './components/menu.vue'; import Menu from './components/menu.vue';
import { router } from './router'; import { router } from './router';
import { applyTheme, lightTheme } from './theme'; import { applyTheme, lightTheme, builtinThemes } from './theme';
Vue.use(Vuex); Vue.use(Vuex);
Vue.use(VueHotkey); Vue.use(VueHotkey);
@ -81,14 +81,14 @@ if (lang == null) {
// Detect the user agent // Detect the user agent
const ua = navigator.userAgent.toLowerCase(); 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 // Get the <head> element
const head = document.getElementsByTagName('head')[0]; const head = document.getElementsByTagName('head')[0];
// If mobile, insert the viewport meta tag // If mobile, insert the viewport meta tag
if (isMobile || window.innerWidth <= 1024) { if (isMobile || window.innerWidth <= 1024) {
const viewport = document.getElementsByName("viewport").item(0); const viewport = document.getElementsByName('viewport').item(0);
viewport.setAttribute('content', viewport.setAttribute('content',
`${viewport.getAttribute('content')},minimum-scale=1,maximum-scale=1,user-scalable=no`); `${viewport.getAttribute('content')},minimum-scale=1,maximum-scale=1,user-scalable=no`);
head.appendChild(viewport); head.appendChild(viewport);
@ -136,6 +136,14 @@ document.body.innerHTML = '<div id="app"></div>';
const os = new MiOS(); const os = new MiOS();
os.init(async () => { 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' in window && os.store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト // 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') { if (Notification.permission === 'default') {
@ -155,6 +163,12 @@ os.init(async () => {
isMobile: isMobile isMobile: isMobile
}; };
}, },
watch: {
'$store.state.device.darkMode'() {
const themes = builtinThemes.concat(this.$store.state.device.themes);
applyTheme(themes.find(x => x.id === (this.$store.state.device.darkMode ? this.$store.state.device.darkTheme : this.$store.state.device.lightTheme)));
}
},
methods: { methods: {
api: os.api, api: os.api,
signout: os.signout, signout: os.signout,
@ -189,6 +203,14 @@ os.init(async () => {
if (cb) vm.$once('closed', cb); if (cb) vm.$once('closed', cb);
(vm as any).focus(); (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, router: router,
render: createEl => createEl(App) render: createEl => createEl(App)
@ -198,4 +220,96 @@ os.init(async () => {
// マウント // マウント
app.$mount('#app'); 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 { EventEmitter } from 'eventemitter3';
import initStore from './store'; import initStore from './store';
import { apiUrl, version, locale } from './config'; import { apiUrl, version } from './config';
import Progress from './scripts/loading'; import Progress from './scripts/loading';
import Stream from './scripts/stream'; import Stream from './scripts/stream';
@ -123,7 +123,12 @@ export default class MiOS extends EventEmitter {
}); });
} else { } else {
// Get token from localStorage // 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 => { fetchme(i, me => {
if (me) { if (me) {
@ -142,98 +147,6 @@ export default class MiOS extends EventEmitter {
@autobind @autobind
private initStream() { private initStream() {
this.stream = new Stream(this); 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
});
const audio = new Audio(`/assets/sounds/${this.store.state.device.sfxChatBg}.mp3`);
audio.volume = this.store.state.device.sfxVolume;
audio.play();
});
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();
});
}
} }
/** /**

View File

@ -14,6 +14,8 @@
<li><mk-link url="https://github.com/acid-chicken" class="at">@acid-chicken</mk-link></li> <li><mk-link url="https://github.com/acid-chicken" class="at">@acid-chicken</mk-link></li>
<li><mk-link url="https://github.com/tamaina" class="at">@tamaina</mk-link></li> <li><mk-link url="https://github.com/tamaina" class="at">@tamaina</mk-link></li>
<li><mk-link url="https://github.com/rinsuki" class="at">@rinsuki</mk-link></li> <li><mk-link url="https://github.com/rinsuki" class="at">@rinsuki</mk-link></li>
<li><mk-link url="https://github.com/Xeltica" class="at">@Xeltica</mk-link></li>
<li><mk-link url="https://github.com/u1-liquid" class="at">@u1-liquid</mk-link></li>
</ul> </ul>
<div style="margin-top: 1em;">📦 {{ $t('misskeySource') }}</div> <div style="margin-top: 1em;">📦 {{ $t('misskeySource') }}</div>
<mk-url url="https://github.com/syuilo/misskey"/> <mk-url url="https://github.com/syuilo/misskey"/>

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

@ -26,7 +26,7 @@
</div> </div>
<div class="signin" v-else> <div class="signin" v-else>
<h1>{{ $t('sign-in') }}</h1> <h1>{{ $t('sign-in') }}</h1>
<mk-signin/> <mk-signin @login="onLogin"/>
</div> </div>
</template> </template>
@ -34,11 +34,13 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../i18n'; import i18n from '../i18n';
import XForm from './auth.form.vue'; import XForm from './auth.form.vue';
import MkSignin from '../components/signin.vue';
export default Vue.extend({ export default Vue.extend({
i18n, i18n,
components: { components: {
XForm XForm,
MkSignin,
}, },
data() { data() {
return { return {
@ -83,6 +85,9 @@ export default Vue.extend({
if (this.session.app.callbackUrl) { if (this.session.app.callbackUrl) {
location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`; 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> <template>
<div> <div class="naked full">
<portal to="header"> <portal to="header">
<button @click="menu" class="_button _jmoebdiw_"> <button @click="menu" class="_button _jmoebdiw_">
<fa :icon="faCloud" style="margin-right: 8px;"/> <fa :icon="faCloud" style="margin-right: 8px;"/>

View File

@ -70,6 +70,10 @@ export default Vue.extend({
't': this.focus 't': this.focus
}; };
}, },
meta() {
return this.$store.state.instance.meta;
},
}, },
watch: { watch: {
@ -121,6 +125,7 @@ export default Vue.extend({
}, },
async choose(ev) { async choose(ev) {
if (this.meta == null) return;
this.menuOpened = true; this.menuOpened = true;
const [antennas, lists] = await Promise.all([ const [antennas, lists] = await Promise.all([
this.$root.api('antennas/list'), this.$root.api('antennas/list'),
@ -148,15 +153,15 @@ export default Vue.extend({
text: this.$t('_timelines.home'), text: this.$t('_timelines.home'),
icon: faHome, icon: faHome,
action: () => { this.setSrc('home') } action: () => { this.setSrc('home') }
}, { }, this.meta.disableLocalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.local'), text: this.$t('_timelines.local'),
icon: faComments, icon: faComments,
action: () => { this.setSrc('local') } action: () => { this.setSrc('local') }
}, { }, this.meta.disableLocalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.social'), text: this.$t('_timelines.social'),
icon: faShareAlt, icon: faShareAlt,
action: () => { this.setSrc('social') } action: () => { this.setSrc('social') }
}, { }, this.meta.disableGlobalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.global'), text: this.$t('_timelines.global'),
icon: faGlobe, icon: faGlobe,
action: () => { this.setSrc('global') } action: () => { this.setSrc('global') }
@ -196,7 +201,7 @@ export default Vue.extend({
> button { > button {
display: block; display: block;
margin: 0 auto; margin: 0 auto;
padding: 8px 12px; padding: 8px 16px;
border-radius: 32px; border-radius: 32px;
} }
} }

View File

@ -61,7 +61,7 @@ export default Vue.extend({
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('some-error') text: this.$t('error')
}); });
}); });
} }

View File

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

View File

@ -102,21 +102,20 @@
<div class="_content"> <div class="_content">
<mk-switch v-model="useObjectStorage">{{ $t('useObjectStorage') }}</mk-switch> <mk-switch v-model="useObjectStorage">{{ $t('useObjectStorage') }}</mk-switch>
<template v-if="useObjectStorage"> <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"> <div class="_inputs">
<mk-input v-model="objectStorageBucket" :disabled="!useObjectStorage">Bucket</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">Prefix</mk-input> <mk-input v-model="objectStoragePrefix" :disabled="!useObjectStorage">{{ $t('objectStoragePrefix') }}<template #desc>{{ $t('objectStoragePrefixDesc') }}</template></mk-input>
</div> </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"> <div class="_inputs">
<mk-input v-model="objectStorageRegion" :disabled="!useObjectStorage">Region</mk-input> <mk-input v-model="objectStorageRegion" :disabled="!useObjectStorage">{{ $t('objectStorageRegion') }}<template #desc>{{ $t('objectStorageRegionDesc') }}</template></mk-input>
<mk-input v-model="objectStoragePort" type="number" :disabled="!useObjectStorage">Port</mk-input>
</div> </div>
<div class="_inputs"> <div class="_inputs">
<mk-input v-model="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Access key</mk-input> <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> <mk-input v-model="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Secret key</mk-input>
</div> </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> </template>
</div> </div>
<div class="_footer"> <div class="_footer">
@ -346,6 +345,20 @@ export default Vue.extend({
}, },
methods: { methods: {
invite() {
this.$root.api('admin/invite').then(x => {
this.$root.dialog({
type: 'info',
text: x.code
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
});
},
addPinUser() { addPinUser() {
this.$root.new(MkUserSelect, {}).$once('selected', user => { this.$root.new(MkUserSelect, {}).$once('selected', user => {
this.pinnedUsers = this.pinnedUsers.trim(); this.pinnedUsers = this.pinnedUsers.trim();

View File

@ -113,7 +113,7 @@ export default Vue.extend({
if (items[0].kind == 'file') { if (items[0].kind == 'file') {
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('only-one-file-attached') text: this.$t('onlyOneFileCanBeAttached')
}); });
} }
} }
@ -138,7 +138,7 @@ export default Vue.extend({
e.preventDefault(); e.preventDefault();
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('only-one-file-attached') text: this.$t('onlyOneFileCanBeAttached')
}); });
return; return;
} }

View File

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

View File

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

View File

@ -5,7 +5,7 @@
<mk-button @click="start" primary class="start"><fa :icon="faPlus"/> {{ $t('startMessaging') }}</mk-button> <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" <router-link v-for="(message, i) in messages"
class="message _panel" class="message _panel"
:to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
@ -30,7 +30,7 @@
</div> </div>
</div> </div>
</router-link> </router-link>
</sequential-entrance> </div>
<div class="no-history" v-if="!fetching && messages.length == 0"> <div class="no-history" v-if="!fetching && messages.length == 0">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/> <img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div>{{ $t('noHistory') }}</div> <div>{{ $t('noHistory') }}</div>
@ -145,7 +145,7 @@ export default Vue.extend({
if (groups1.length === 0 && groups2.length === 0) { if (groups1.length === 0 && groups2.length === 0) {
this.$root.dialog({ this.$root.dialog({
type: 'warning', type: 'warning',
title: this.$t('noGroups'), title: this.$t('youHaveNoGroups'),
text: this.$t('joinOrCreateGroup'), text: this.$t('joinOrCreateGroup'),
}); });
return; return;

View File

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

View File

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

View File

@ -2,7 +2,10 @@
<section class="_card"> <section class="_card">
<div class="_title"><fa :icon="faLaugh"/> {{ $t('reaction') }}</div> <div class="_title"><fa :icon="faLaugh"/> {{ $t('reaction') }}</div>
<div class="_content"> <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>
<div class="_footer"> <div class="_footer">
<mk-button @click="save()" primary inline :disabled="!changed"><fa :icon="faSave"/> {{ $t('save') }}</mk-button> <mk-button @click="save()" primary inline :disabled="!changed"><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
@ -14,24 +17,26 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faLaugh, faSave, faEye } from '@fortawesome/free-regular-svg-icons'; 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 MkButton from '../../components/ui/button.vue';
import MkReactionPicker from '../../components/reaction-picker.vue'; import MkReactionPicker from '../../components/reaction-picker.vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { emojiRegexWithCustom } from '../../../misc/emoji-regex';
export default Vue.extend({ export default Vue.extend({
i18n, i18n,
components: { components: {
MkTextarea, MkInput,
MkButton, MkButton,
}, },
data() { data() {
return { return {
reactions: this.$store.state.settings.reactions.join('\n'), reactions: this.$store.state.settings.reactions.join(''),
changed: false, 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: { methods: {
save() { 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; this.changed = false;
}, },
preview(ev) { preview(ev) {
const picker = this.$root.new(MkReactionPicker, { const picker = this.$root.new(MkReactionPicker, {
source: ev.currentTarget || ev.target, source: ev.currentTarget || ev.target,
reactions: this.reactions.trim().split('\n'), reactions: this.splited,
showFocus: false, showFocus: false,
}); });
picker.$once('chosen', reaction => { picker.$once('chosen', reaction => {
picker.close(); 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> <template>
<div class="mk-note-page"> <div class="mk-note-page">
<portal to="avatar" v-if="note"><mk-avatar class="avatar" :user="note.user" :disable-preview="true"/></portal> <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"> <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> <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"/> <x-notes v-if="showNext" ref="next" :pagination="next"/>
<hr v-if="showNext"/> <hr v-if="showNext"/>
<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"/> <x-note :note="note" :key="note.id" :detail="true"/>
<div v-if="error"> <div v-if="error">
<mk-error @retry="fetch()"/> <mk-error @retry="fetch()"/>
</div> </div>
<mk-button v-if="hasPrev && !showPrev" @click="showPrev = true" primary style="margin: var(--margin) auto 0 auto;"><fa :icon="faChevronDown"/></mk-button> <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"/> <hr v-if="showPrev"/>
<x-notes v-if="showPrev" ref="prev" :pagination="prev" style="margin-top: var(--margin);"/> <x-notes v-if="showPrev" ref="prev" :pagination="prev" style="margin-top: var(--margin);"/>
</div> </div>
</transition>
</div> </div>
</template> </template>
@ -29,7 +33,7 @@ import i18n from '../i18n';
import Progress from '../scripts/loading'; import Progress from '../scripts/loading';
import XNote from '../components/note.vue'; import XNote from '../components/note.vue';
import XNotes from '../components/notes.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({ export default Vue.extend({
i18n, i18n,
@ -41,7 +45,7 @@ export default Vue.extend({
components: { components: {
XNote, XNote,
XNotes, XNotes,
MkButton, MkRemoteCaution,
}, },
data() { data() {
return { return {

View File

@ -69,7 +69,7 @@ export default Vue.extend({
async add() { async add() {
const { canceled, result: type } = await this.$root.dialog({ const { canceled, result: type } = await this.$root.dialog({
type: null, type: null,
title: this.$t('choose-block'), title: this.$t('_pages.chooseBlock'),
select: { select: {
groupedItems: this.getPageBlockList() groupedItems: this.getPageBlockList()
}, },

View File

@ -80,7 +80,7 @@ export default Vue.extend({
async add() { async add() {
const { canceled, result: type } = await this.$root.dialog({ const { canceled, result: type } = await this.$root.dialog({
type: null, type: null,
title: this.$t('choose-block'), title: this.$t('_pages.chooseBlock'),
select: { select: {
groupedItems: this.getPageBlockList() groupedItems: this.getPageBlockList()
}, },

View File

@ -212,7 +212,7 @@ export default Vue.extend({
async changeType() { async changeType() {
const { canceled, result: type } = await this.$root.dialog({ const { canceled, result: type } = await this.$root.dialog({
type: null, type: null,
title: this.$t('select-type'), title: this.$t('_pages.selectType'),
select: { select: {
groupedItems: this.getScriptBlockList(this.getExpectedType ? this.getExpectedType() : null) groupedItems: this.getScriptBlockList(this.getExpectedType ? this.getExpectedType() : null)
}, },

View File

@ -2,7 +2,7 @@
<div> <div>
<div class="gwbmwxkm _panel"> <div class="gwbmwxkm _panel">
<header> <header>
<div class="title"><fa :icon="faStickyNote"/> {{ readonly ? $t('readPage') : pageId ? $t('editPage') : $t('newPage') }}</div> <div class="title"><fa :icon="faStickyNote"/> {{ readonly ? $t('_pages.readPage') : pageId ? $t('_pages.editPage') : $t('_pages.newPage') }}</div>
<div class="buttons"> <div class="buttons">
<button class="_button" @click="del()" v-if="!readonly"><fa :icon="faTrashAlt"/></button> <button class="_button" @click="del()" v-if="!readonly"><fa :icon="faTrashAlt"/></button>
<button class="_button" @click="() => showOptions = !showOptions"><fa :icon="faCog"/></button> <button class="_button" @click="() => showOptions = !showOptions"><fa :icon="faCog"/></button>
@ -11,37 +11,37 @@
</header> </header>
<section> <section>
<router-link class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><fa :icon="faExternalLinkSquareAlt"/> {{ $t('view-page') }}</router-link> <router-link class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><fa :icon="faExternalLinkSquareAlt"/> {{ $t('_pages.viewPage') }}</router-link>
<mk-input v-model="title"> <mk-input v-model="title">
<span>{{ $t('title') }}</span> <span>{{ $t('_pages.title') }}</span>
</mk-input> </mk-input>
<template v-if="showOptions"> <template v-if="showOptions">
<mk-input v-model="summary"> <mk-input v-model="summary">
<span>{{ $t('summary') }}</span> <span>{{ $t('_pages.summary') }}</span>
</mk-input> </mk-input>
<mk-input v-model="name"> <mk-input v-model="name">
<template #prefix>{{ url }}/@{{ author.username }}/pages/</template> <template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
<span>{{ $t('url') }}</span> <span>{{ $t('_pages.url') }}</span>
</mk-input> </mk-input>
<mk-switch v-model="alignCenter">{{ $t('alignCenter') }}</mk-switch> <mk-switch v-model="alignCenter">{{ $t('_pages.alignCenter') }}</mk-switch>
<mk-select v-model="font"> <mk-select v-model="font">
<template #label>{{ $t('font') }}</template> <template #label>{{ $t('_pages.font') }}</template>
<option value="serif">{{ $t('fontSerif') }}</option> <option value="serif">{{ $t('_pages.fontSerif') }}</option>
<option value="sans-serif">{{ $t('fontSansSerif') }}</option> <option value="sans-serif">{{ $t('_pages.fontSansSerif') }}</option>
</mk-select> </mk-select>
<mk-switch v-model="hideTitleWhenPinned">{{ $t('hide-title-when-pinned') }}</mk-switch> <mk-switch v-model="hideTitleWhenPinned">{{ $t('_pages.hideTitleWhenPinned') }}</mk-switch>
<div class="eyeCatch"> <div class="eyeCatch">
<mk-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catching-image') }}</mk-button> <mk-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('_pages.eyeCatchingImageSet') }}</mk-button>
<div v-else-if="eyeCatchingImage"> <div v-else-if="eyeCatchingImage">
<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name"/> <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name"/>
<mk-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catching-image') }}</mk-button> <mk-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('_pages.eyeCatchingImageRemove') }}</mk-button>
</div> </div>
</div> </div>
</template> </template>
@ -53,7 +53,7 @@
</div> </div>
<mk-container :body-togglable="true"> <mk-container :body-togglable="true">
<template #header><fa :icon="faMagic"/> {{ $t('variables') }}</template> <template #header><fa :icon="faMagic"/> {{ $t('_pages.variables') }}</template>
<div class="qmuvgica"> <div class="qmuvgica">
<x-draggable tag="div" class="variables" v-show="variables.length > 0" :list="variables" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5"> <x-draggable tag="div" class="variables" v-show="variables.length > 0" :list="variables" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
<x-variable v-for="variable in variables" <x-variable v-for="variable in variables"
@ -70,22 +70,14 @@
</x-draggable> </x-draggable>
<mk-button @click="addVariable()" class="add" v-if="!readonly"><fa :icon="faPlus"/></mk-button> <mk-button @click="addVariable()" class="add" v-if="!readonly"><fa :icon="faPlus"/></mk-button>
<x-info><span v-html="$t('variables-info')"></span><a @click="() => moreDetails = true" style="display:block;">{{ $t('more-details') }}</a></x-info>
<template v-if="moreDetails">
<x-info><span v-html="$t('variables-info2')"></span></x-info>
<x-info><span v-html="$t('variables-info3')"></span></x-info>
<x-info><span v-html="$t('variables-info4')"></span></x-info>
</template>
</div> </div>
</mk-container> </mk-container>
<mk-container :body-togglable="true" :expanded="false"> <mk-container :body-togglable="true" :expanded="false">
<template #header><fa :icon="faCode"/> {{ $t('inspector') }}</template> <template #header><fa :icon="faCode"/> {{ $t('_pages.inspector') }}</template>
<div style="padding:0 32px 32px 32px;"> <div style="padding:0 32px 32px 32px;">
<mk-textarea :value="JSON.stringify(content, null, 2)" readonly tall>{{ $t('content') }}</mk-textarea> <mk-textarea :value="JSON.stringify(content, null, 2)" readonly tall>{{ $t('_pages.content') }}</mk-textarea>
<mk-textarea :value="JSON.stringify(variables, null, 2)" readonly tall>{{ $t('variables') }}</mk-textarea> <mk-textarea :value="JSON.stringify(variables, null, 2)" readonly tall>{{ $t('_pages.variables') }}</mk-textarea>
</div> </div>
</mk-container> </mk-container>
</div> </div>
@ -152,7 +144,6 @@ export default Vue.extend({
variables: [], variables: [],
aiScript: null, aiScript: null,
showOptions: false, showOptions: false,
moreDetails: false,
url, url,
faPlus, faICursor, faSave, faStickyNote, faMagic, faCog, faTrashAlt, faExternalLinkSquareAlt, faCode faPlus, faICursor, faSave, faStickyNote, faMagic, faCog, faTrashAlt, faExternalLinkSquareAlt, faCode
}; };
@ -243,14 +234,14 @@ export default Vue.extend({
if (err.info.param == 'name') { if (err.info.param == 'name') {
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
title: this.$t('title-invalid-name'), title: this.$t('_pages.invalidNameTitle'),
text: this.$t('text-invalid-name') text: this.$t('_pages.invalidNameText')
}); });
} }
} else if (err.code == 'NAME_ALREADY_EXISTS') { } else if (err.code == 'NAME_ALREADY_EXISTS') {
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('name-already-exists') text: this.$t('_pages.nameAlreadyExists')
}); });
} }
}; };
@ -262,7 +253,7 @@ export default Vue.extend({
this.currentName = this.name.trim(); this.currentName = this.name.trim();
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('page-updated') text: this.$t('_pages.updated')
}); });
}).catch(onError); }).catch(onError);
} else { } else {
@ -272,7 +263,7 @@ export default Vue.extend({
this.currentName = this.name.trim(); this.currentName = this.name.trim();
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('page-created') text: this.$t('_pages.created')
}); });
this.$router.push(`/my/pages/edit/${this.pageId}`); this.$router.push(`/my/pages/edit/${this.pageId}`);
}).catch(onError); }).catch(onError);
@ -282,7 +273,7 @@ export default Vue.extend({
del() { del() {
this.$root.dialog({ this.$root.dialog({
type: 'warning', type: 'warning',
text: this.$t('are-you-sure-delete'), text: this.$t('removeAreYouSure', { x: this.title.trim() }),
showCancelButton: true showCancelButton: true
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;
@ -291,7 +282,7 @@ export default Vue.extend({
}).then(() => { }).then(() => {
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('page-deleted') text: this.$t('_pages.deleted')
}); });
this.$router.push(`/my/pages`); this.$router.push(`/my/pages`);
}); });
@ -301,7 +292,7 @@ export default Vue.extend({
async add() { async add() {
const { canceled, result: type } = await this.$root.dialog({ const { canceled, result: type } = await this.$root.dialog({
type: null, type: null,
title: this.$t('chooseBlock'), title: this.$t('_pages.chooseBlock'),
select: { select: {
groupedItems: this.getPageBlockList() groupedItems: this.getPageBlockList()
}, },
@ -315,7 +306,7 @@ export default Vue.extend({
async addVariable() { async addVariable() {
let { canceled, result: name } = await this.$root.dialog({ let { canceled, result: name } = await this.$root.dialog({
title: this.$t('enterVariableName'), title: this.$t('_pages.enterVariableName'),
input: { input: {
type: 'text', type: 'text',
}, },
@ -328,7 +319,7 @@ export default Vue.extend({
if (this.aiScript.isUsedName(name)) { if (this.aiScript.isUsedName(name)) {
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',
text: this.$t('the-variable-name-is-already-used') text: this.$t('_pages.variableNameIsAlreadyUsed')
}); });
return; return;
} }
@ -348,7 +339,7 @@ export default Vue.extend({
getPageBlockList() { getPageBlockList() {
return [{ return [{
label: this.$t('content-blocks'), label: this.$t('_pages.contentBlocks'),
items: [ items: [
{ value: 'section', text: this.$t('_pages.blocks.section') }, { value: 'section', text: this.$t('_pages.blocks.section') },
{ value: 'text', text: this.$t('_pages.blocks.text') }, { value: 'text', text: this.$t('_pages.blocks.text') },
@ -356,7 +347,7 @@ export default Vue.extend({
{ value: 'textarea', text: this.$t('_pages.blocks.textarea') }, { value: 'textarea', text: this.$t('_pages.blocks.textarea') },
] ]
}, { }, {
label: this.$t('input-blocks'), label: this.$t('_pages.inputBlocks'),
items: [ items: [
{ value: 'button', text: this.$t('_pages.blocks.button') }, { value: 'button', text: this.$t('_pages.blocks.button') },
{ value: 'radioButton', text: this.$t('_pages.blocks.radioButton') }, { value: 'radioButton', text: this.$t('_pages.blocks.radioButton') },
@ -367,7 +358,7 @@ export default Vue.extend({
{ value: 'counter', text: this.$t('_pages.blocks.counter') } { value: 'counter', text: this.$t('_pages.blocks.counter') }
] ]
}, { }, {
label: this.$t('special-blocks'), label: this.$t('_pages.specialBlocks'),
items: [ items: [
{ value: 'if', text: this.$t('_pages.blocks.if') }, { value: 'if', text: this.$t('_pages.blocks.if') },
{ value: 'post', text: this.$t('_pages.blocks.post') } { value: 'post', text: this.$t('_pages.blocks.post') }

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<mk-container :body-togglable="true"> <mk-container :body-togglable="true">
<template #header><fa :icon="faEdit" fixed-width/>{{ $t('my-pages') }}</template> <template #header><fa :icon="faEdit" fixed-width/>{{ $t('_pages.my') }}</template>
<div class="rknalgpo my"> <div class="rknalgpo my">
<mk-button class="new" @click="create()"><fa :icon="faPlus"/></mk-button> <mk-button class="new" @click="create()"><fa :icon="faPlus"/></mk-button>
<mk-pagination :pagination="myPagesPagination" #default="{items}"> <mk-pagination :pagination="myPagesPagination" #default="{items}">
@ -11,7 +11,7 @@
</mk-container> </mk-container>
<mk-container :body-togglable="true"> <mk-container :body-togglable="true">
<template #header><fa :icon="faHeart" fixed-width/>{{ $t('liked-pages') }}</template> <template #header><fa :icon="faHeart" fixed-width/>{{ $t('_pages.liked') }}</template>
<div class="rknalgpo"> <div class="rknalgpo">
<mk-pagination :pagination="likedPagesPagination" #default="{items}"> <mk-pagination :pagination="likedPagesPagination" #default="{items}">
<mk-page-preview v-for="like in items" class="ckltabjg" :page="like.page" :key="like.page.id"/> <mk-page-preview v-for="like in items" class="ckltabjg" :page="like.page" :key="like.page.id"/>

View File

@ -8,8 +8,10 @@
<section class="_card"> <section class="_card">
<div class="_title"><fa :icon="faMusic"/> {{ $t('sounds') }}</div> <div class="_title"><fa :icon="faMusic"/> {{ $t('sounds') }}</div>
<div class="_content"> <div class="_content">
{{ $t('volume') }} <mk-range v-model="sfxVolume" :min="0" :max="1" :step="0.1">
<input type="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>
<div class="_content"> <div class="_content">
<mk-select v-model="sfxNote"> <mk-select v-model="sfxNote">
@ -37,6 +39,11 @@
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option> <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> <template #text><button class="_textButton" @click="listen(sfxChatBg)" v-if="sfxChatBg"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select> </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> </div>
</section> </section>
@ -56,7 +63,6 @@
<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template> <template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
</mk-switch> </mk-switch>
<mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch> <mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch>
<mk-switch v-model="useNotificationsPopup">{{ $t('useNotificationsPopup') }}</mk-switch>
</div> </div>
<div class="_content"> <div class="_content">
<mk-select v-model="lang"> <mk-select v-model="lang">
@ -80,12 +86,13 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { faImage, faCog, faMusic, faPlay } from '@fortawesome/free-solid-svg-icons'; import { faImage, faCog, faMusic, faPlay, faVolumeUp, faVolumeMute } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue'; import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue'; import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue'; import MkSwitch from '../../components/ui/switch.vue';
import MkSelect from '../../components/ui/select.vue'; import MkSelect from '../../components/ui/select.vue';
import MkRadio from '../../components/ui/radio.vue'; import MkRadio from '../../components/ui/radio.vue';
import MkRange from '../../components/ui/range.vue';
import XTheme from './theme.vue'; import XTheme from './theme.vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { langs } from '../../config'; import { langs } from '../../config';
@ -97,6 +104,10 @@ const sounds = [
'syuilo/pope1', 'syuilo/pope1',
'syuilo/pope2', 'syuilo/pope2',
'syuilo/waon', 'syuilo/waon',
'syuilo/popo',
'syuilo/triple',
'syuilo/poi1',
'syuilo/poi2',
'aisha/1', 'aisha/1',
'aisha/2', 'aisha/2',
'aisha/3', 'aisha/3',
@ -119,6 +130,7 @@ export default Vue.extend({
MkSwitch, MkSwitch,
MkSelect, MkSelect,
MkRadio, MkRadio,
MkRange
}, },
data() { data() {
@ -127,7 +139,7 @@ export default Vue.extend({
lang: localStorage.getItem('lang'), lang: localStorage.getItem('lang'),
fontSize: localStorage.getItem('fontSize'), fontSize: localStorage.getItem('fontSize'),
sounds, sounds,
faImage, faCog, faMusic, faPlay faImage, faCog, faMusic, faPlay, faVolumeUp, faVolumeMute
} }
}, },
@ -162,14 +174,9 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); } set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); }
}, },
useNotificationsPopup: {
get() { return this.$store.state.device.useNotificationsPopup; },
set(value) { this.$store.commit('device/set', { key: 'useNotificationsPopup', value }); }
},
sfxVolume: { sfxVolume: {
get() { return this.$store.state.device.sfxVolume; }, get() { return this.$store.state.device.sfxVolume; },
set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value }); } set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value: parseFloat(value, 10) }); }
}, },
sfxNote: { sfxNote: {
@ -196,6 +203,17 @@ export default Vue.extend({
get() { return this.$store.state.device.sfxChatBg; }, get() { return this.$store.state.device.sfxChatBg; },
set(value) { this.$store.commit('device/set', { key: 'sfxChatBg', value }); } 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: { watch: {

View File

@ -2,8 +2,30 @@
<section class="rfqxtzch _card"> <section class="rfqxtzch _card">
<div class="_title"><fa :icon="faPalette"/> {{ $t('theme') }}</div> <div class="_title"><fa :icon="faPalette"/> {{ $t('theme') }}</div>
<div class="_content"> <div class="_content">
<mk-select v-model="theme" :placeholder="$t('theme')"> <div class="darkMode" :class="{ disabled: syncDeviceDarkMode }">
<template #label>{{ $t('theme') }}</template> <div class="toggleWrapper">
<input type="checkbox" class="dn" id="dn" v-model="darkMode" :disabled="syncDeviceDarkMode"/>
<label for="dn" class="toggle">
<span class="before">{{ $t('light') }}</span>
<span class="after">{{ $t('dark') }}</span>
<span class="toggle__handler">
<span class="crater crater--1"></span>
<span class="crater crater--2"></span>
<span class="crater crater--3"></span>
</span>
<span class="star star--1"></span>
<span class="star star--2"></span>
<span class="star star--3"></span>
<span class="star star--4"></span>
<span class="star star--5"></span>
<span class="star star--6"></span>
</label>
</div>
</div>
</div>
<div class="_content">
<mk-select v-model="lightTheme">
<template #label>{{ $t('themeForLightMode') }}</template>
<optgroup :label="$t('lightThemes')"> <optgroup :label="$t('lightThemes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
@ -11,6 +33,18 @@
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
</mk-select> </mk-select>
<mk-select v-model="darkTheme">
<template #label>{{ $t('themeForDarkMode') }}</template>
<optgroup :label="$t('darkThemes')">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
<optgroup :label="$t('lightThemes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</mk-select>
</div>
<div class="_content">
<mk-switch v-model="syncDeviceDarkMode">{{ $t('syncDeviceDarkMode') }}</mk-switch>
</div> </div>
<div class="_content"> <div class="_content">
<mk-button primary v-if="wallpaper == null" @click="setWallpaper">{{ $t('setWallpaper') }}</mk-button> <mk-button primary v-if="wallpaper == null" @click="setWallpaper">{{ $t('setWallpaper') }}</mk-button>
@ -25,9 +59,11 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue'; import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue'; import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue'; import MkSelect from '../../components/ui/select.vue';
import MkSwitch from '../../components/ui/switch.vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { Theme, builtinThemes, applyTheme } from '../../theme'; import { Theme, builtinThemes, applyTheme } from '../../theme';
import { selectFile } from '../../scripts/select-file'; import { selectFile } from '../../scripts/select-file';
import { isDeviceDarkmode } from '../../scripts/is-device-darkmode';
export default Vue.extend({ export default Vue.extend({
i18n, i18n,
@ -36,6 +72,7 @@ export default Vue.extend({
MkInput, MkInput,
MkButton, MkButton,
MkSelect, MkSelect,
MkSwitch,
}, },
data() { data() {
@ -62,15 +99,44 @@ export default Vue.extend({
return this.themes.filter(t => t.base == 'light' || t.kind == 'light'); return this.themes.filter(t => t.base == 'light' || t.kind == 'light');
}, },
theme: { darkTheme: {
get() { return this.$store.state.device.theme; }, get() { return this.$store.state.device.darkTheme; },
set(value) { this.$store.commit('device/set', { key: 'theme', value }); } set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); }
},
lightTheme: {
get() { return this.$store.state.device.lightTheme; },
set(value) { this.$store.commit('device/set', { key: 'lightTheme', value }); }
},
darkMode: {
get() { return this.$store.state.device.darkMode; },
set(value) { this.$store.commit('device/set', { key: 'darkMode', value }); }
},
syncDeviceDarkMode: {
get() { return this.$store.state.device.syncDeviceDarkMode; },
set(value) { this.$store.commit('device/set', { key: 'syncDeviceDarkMode', value }); }
}, },
}, },
watch: { watch: {
theme() { darkTheme() {
applyTheme(this.themes.find(x => x.id === this.theme)); if (this.$store.state.device.darkMode) {
applyTheme(this.themes.find(x => x.id === this.darkTheme));
}
},
lightTheme() {
if (!this.$store.state.device.darkMode) {
applyTheme(this.themes.find(x => x.id === this.lightTheme));
}
},
syncDeviceDarkMode() {
if (this.$store.state.device.syncDeviceDarkMode) {
this.$store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
}
}, },
wallpaper() { wallpaper() {
@ -92,3 +158,230 @@ export default Vue.extend({
} }
}); });
</script> </script>
<style lang="scss" scoped>
.rfqxtzch {
> ._content {
> .darkMode {
position: relative;
padding: 32px 0;
&.disabled {
opacity: 0.7;
&, * {
cursor: not-allowed !important;
}
}
.toggleWrapper {
position: absolute;
top: 50%;
left: 50%;
overflow: hidden;
padding: 0 200px;
transform: translate3d(-50%, -50%, 0);
input {
position: absolute;
left: -99em;
}
}
.toggle {
cursor: pointer;
display: inline-block;
position: relative;
width: 90px;
height: 50px;
background-color: #83D8FF;
border-radius: 90px - 6;
transition: background-color 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
> .before, > .after {
position: absolute;
top: 15px;
font-size: 18px;
transition: color 1s ease;
}
> .before {
left: -70px;
color: var(--accent);
}
> .after {
right: -68px;
color: var(--fg);
}
}
.toggle__handler {
display: inline-block;
position: relative;
z-index: 1;
top: 3px;
left: 3px;
width: 50px - 6;
height: 50px - 6;
background-color: #FFCF96;
border-radius: 50px;
box-shadow: 0 2px 6px rgba(0,0,0,.3);
transition: all 400ms cubic-bezier(0.68, -0.55, 0.265, 1.55) !important;
transform: rotate(-45deg);
.crater {
position: absolute;
background-color: #E8CDA5;
opacity: 0;
transition: opacity 200ms ease-in-out !important;
border-radius: 100%;
}
.crater--1 {
top: 18px;
left: 10px;
width: 4px;
height: 4px;
}
.crater--2 {
top: 28px;
left: 22px;
width: 6px;
height: 6px;
}
.crater--3 {
top: 10px;
left: 25px;
width: 8px;
height: 8px;
}
}
.star {
position: absolute;
background-color: #ffffff;
transition: all 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
border-radius: 50%;
}
.star--1 {
top: 10px;
left: 35px;
z-index: 0;
width: 30px;
height: 3px;
}
.star--2 {
top: 18px;
left: 28px;
z-index: 1;
width: 30px;
height: 3px;
}
.star--3 {
top: 27px;
left: 40px;
z-index: 0;
width: 30px;
height: 3px;
}
.star--4,
.star--5,
.star--6 {
opacity: 0;
transition: all 300ms 0 cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.star--4 {
top: 16px;
left: 11px;
z-index: 0;
width: 2px;
height: 2px;
transform: translate3d(3px,0,0);
}
.star--5 {
top: 32px;
left: 17px;
z-index: 0;
width: 3px;
height: 3px;
transform: translate3d(3px,0,0);
}
.star--6 {
top: 36px;
left: 28px;
z-index: 0;
width: 2px;
height: 2px;
transform: translate3d(3px,0,0);
}
input:checked {
+ .toggle {
background-color: #749DD6;
> .before {
color: var(--fg);
}
> .after {
color: var(--accent);
}
.toggle__handler {
background-color: #FFE5B5;
transform: translate3d(40px, 0, 0) rotate(0);
.crater { opacity: 1; }
}
.star--1 {
width: 2px;
height: 2px;
}
.star--2 {
width: 4px;
height: 4px;
transform: translate3d(-5px, 0, 0);
}
.star--3 {
width: 2px;
height: 2px;
transform: translate3d(-7px, 0, 0);
}
.star--4,
.star--5,
.star--6 {
opacity: 1;
transform: translate3d(0,0,0);
}
.star--4 {
transition: all 300ms 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.star--5 {
transition: all 300ms 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.star--6 {
transition: all 300ms 400ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
}
}
}
}
}
</style>

View File

@ -3,8 +3,7 @@
<portal to="title" v-if="user"><mk-user-name :user="user" :nowrap="false" class="name"/></portal> <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> <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> <mk-remote-caution v-if="user.host != null" :href="user.url" style="margin-bottom: var(--margin)"/>
<transition :name="$store.state.device.animation ? 'zoom' : ''" mode="out-in" appear>
<div class="profile _panel" :key="user.id"> <div class="profile _panel" :key="user.id">
<div class="banner-container" :style="style"> <div class="banner-container" :style="style">
<div class="banner" ref="banner" :style="style"></div> <div class="banner" ref="banner" :style="style"></div>
@ -64,7 +63,7 @@
</dd> </dd>
</dl> </dl>
</div> </div>
<div class="status" v-if="user.host === null"> <div class="status">
<router-link :to="user | userPage()" :class="{ active: $route.name === 'user' }"> <router-link :to="user | userPage()" :class="{ active: $route.name === 'user' }">
<b>{{ user.notesCount | number }}</b> <b>{{ user.notesCount | number }}</b>
<span>{{ $t('notes') }}</span> <span>{{ $t('notes') }}</span>
@ -79,12 +78,11 @@
</router-link> </router-link>
</div> </div>
</div> </div>
</transition>
<router-view :user="user"></router-view> <router-view :user="user"></router-view>
<template v-if="$route.name == 'user'"> <template v-if="$route.name == 'user'">
<sequential-entrance class="pins"> <div class="pins">
<x-note v-for="(note, i) in user.pinnedNotes" class="note" :note="note" :key="note.id" :detail="true" :pinned="true"/> <x-note v-for="note in user.pinnedNotes" class="note" :note="note" :key="note.id" :detail="true" :pinned="true"/>
</sequential-entrance> </div>
<mk-container :body-togglable="true" class="content"> <mk-container :body-togglable="true" class="content">
<template #header><fa :icon="faImage"/>{{ $t('images') }}</template> <template #header><fa :icon="faImage"/>{{ $t('images') }}</template>
<div> <div>
@ -107,7 +105,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; 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 { faCalendarAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
import * as age from 's-age'; import * as age from 's-age';
import XUserTimeline from './index.timeline.vue'; import XUserTimeline from './index.timeline.vue';
@ -115,6 +113,7 @@ import XUserMenu from '../../components/user-menu.vue';
import XNote from '../../components/note.vue'; import XNote from '../../components/note.vue';
import MkFollowButton from '../../components/follow-button.vue'; import MkFollowButton from '../../components/follow-button.vue';
import MkContainer from '../../components/ui/container.vue'; import MkContainer from '../../components/ui/container.vue';
import MkRemoteCaution from '../../components/remote-caution.vue';
import Progress from '../../scripts/loading'; import Progress from '../../scripts/loading';
import parseAcct from '../../../misc/acct/parse'; import parseAcct from '../../../misc/acct/parse';
@ -124,6 +123,7 @@ export default Vue.extend({
XNote, XNote,
MkFollowButton, MkFollowButton,
MkContainer, MkContainer,
MkRemoteCaution,
XPhotos: () => import('./index.photos.vue').then(m => m.default), XPhotos: () => import('./index.photos.vue').then(m => m.default),
XActivity: () => import('./index.activity.vue').then(m => m.default), XActivity: () => import('./index.activity.vue').then(m => m.default),
}, },
@ -139,7 +139,7 @@ export default Vue.extend({
user: null, user: null,
error: null, error: null,
parallaxAnimationId: 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> <style lang="scss" scoped>
.mk-user-page { .mk-user-page {
> .remote-caution {
font-size: 0.8em;
padding: 16px;
margin-bottom: var(--margin);
> a {
margin-left: 4px;
color: var(--accent);
}
}
> .profile { > .profile {
position: relative; position: relative;
margin-bottom: var(--margin); margin-bottom: var(--margin);

View File

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

View File

@ -0,0 +1,3 @@
export function isDeviceDarkmode() {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}

View File

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

View File

@ -35,18 +35,21 @@ const defaultDeviceSettings = {
accounts: [], accounts: [],
recentEmojis: [], recentEmojis: [],
themes: [], themes: [],
theme: 'light', darkTheme: '8c539dc1-0fab-4d47-9194-39c508e9bfe1',
lightTheme: '4eea646f-7afa-4645-83e9-83af0333cd37',
darkMode: false,
syncDeviceDarkMode: true,
animation: true, animation: true,
animatedMfm: true, animatedMfm: true,
imageNewTab: false, imageNewTab: false,
showFixedPostForm: false, showFixedPostForm: false,
useNotificationsPopup: true,
sfxVolume: 0.3, sfxVolume: 0.3,
sfxNote: 'syuilo/down', sfxNote: 'syuilo/down',
sfxNoteMy: 'syuilo/up', sfxNoteMy: 'syuilo/up',
sfxNotification: 'syuilo/pope2', sfxNotification: 'syuilo/pope2',
sfxChat: 'syuilo/pope1', sfxChat: 'syuilo/pope1',
sfxChatBg: 'syuilo/waon', sfxChatBg: 'syuilo/waon',
sfxAntenna: 'syuilo/triple',
userData: {}, userData: {},
}; };
@ -100,6 +103,7 @@ export default (os: MiOS) => new Vuex.Store({
ctx.commit('settings/init', {}); ctx.commit('settings/init', {});
ctx.commit('deviceUser/init', {}); ctx.commit('deviceUser/init', {});
localStorage.removeItem('i'); localStorage.removeItem('i');
document.cookie = `igi=; path=/`;
}, },
async switchAccount(ctx, i) { async switchAccount(ctx, i) {

View File

@ -3,7 +3,7 @@
:root { :root {
--radius: 8px; --radius: 8px;
--marginFull: 16px; --marginFull: 16px;
--marginHalf: 8px; --marginHalf: 10px;
--margin: var(--marginFull); --margin: var(--marginFull);
@ -18,6 +18,7 @@
} }
html { html {
touch-action: manipulation;
background-color: var(--bg); background-color: var(--bg);
background-attachment: fixed; background-attachment: fixed;
background-size: cover; background-size: cover;
@ -199,6 +200,7 @@ hr {
._button { ._button {
appearance: none; appearance: none;
padding: 0; padding: 0;
margin: 0; // for Safari
background: none; background: none;
border: none; border: none;
cursor: pointer; cursor: pointer;
@ -228,7 +230,6 @@ hr {
@extend ._button; @extend ._button;
color: #fff; color: #fff;
background: var(--accent); background: var(--accent);
box-shadow: 0 6px 16px var(--accentShadow);
&:not(:disabled):hover { &:not(:disabled):hover {
background: var(--jkhztclx); 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 { ._panel {
@extend ._shadow;
position: relative; position: relative;
background: var(--panel); background: var(--panel);
border-radius: var(--radius); 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 { ._card {

View File

@ -13,8 +13,8 @@ export const lightTheme: Theme = require('./themes/_light.json5');
export const darkTheme: Theme = require('./themes/_dark.json5'); export const darkTheme: Theme = require('./themes/_dark.json5');
export const builtinThemes = [ export const builtinThemes = [
lightTheme, require('./themes/white.json5'),
darkTheme, require('./themes/black.json5'),
require('./themes/lavender.json5'), require('./themes/lavender.json5'),
require('./themes/halloween.json5'), require('./themes/halloween.json5'),
require('./themes/garden.json5'), require('./themes/garden.json5'),
@ -44,7 +44,7 @@ export function applyTheme(theme: Theme, persist = true) {
const _theme = JSON.parse(JSON.stringify(theme)); const _theme = JSON.parse(JSON.stringify(theme));
if (_theme.base) { if (_theme.base) {
const base = [lightTheme, darkTheme].find(x => x.id == _theme.base); const base = [lightTheme, darkTheme].find(x => x.id === _theme.base);
_theme.props = Object.assign({}, base.props, _theme.props); _theme.props = Object.assign({}, base.props, _theme.props);
} }

View File

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

View File

@ -20,6 +20,7 @@
panel: '#fff', panel: '#fff',
shadow: 'rgba(0, 0, 0, 0.1)', shadow: 'rgba(0, 0, 0, 0.1)',
header: 'rgba(255, 255, 255, 0.75)', header: 'rgba(255, 255, 255, 0.75)',
pageBg: '@bg',
navBg: '@panel', navBg: '@panel',
navFg: '@fg', navFg: '@fg',
navHoverFg: ':darken<17<@fg', navHoverFg: ':darken<17<@fg',
@ -33,8 +34,7 @@
divider: 'rgba(0, 0, 0, 0.1)', divider: 'rgba(0, 0, 0, 0.1)',
scrollbarHandle: 'rgba(0, 0, 0, 0.2)', scrollbarHandle: 'rgba(0, 0, 0, 0.2)',
scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)', scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)',
dateLabelBg: 'rgba(0, 0, 0, 0.5)', dateLabelFg: '@fg',
dateLabelFg: '#fff',
infoBg: '#e5f5ff', infoBg: '#e5f5ff',
infoFg: '#72818a', infoFg: '#72818a',
infoWarnBg: '#fff0db', infoWarnBg: '#fff0db',
@ -51,14 +51,13 @@
driveFolderBg: ':alpha<0.3<@accent', driveFolderBg: ':alpha<0.3<@accent',
wallpaperOverlay: 'rgba(255, 255, 255, 0.5)', wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
badge: '#31b1ce', badge: '#31b1ce',
messageBg: '@panel',
bonzsgfz: ':alpha<0<@bg', bonzsgfz: ':alpha<0<@bg',
pcncwizz: ':darken<2<@panel', pcncwizz: ':darken<2<@panel',
vocsgcxy: 'rgba(255, 255, 255, 0.5)',
yrnqrguo: 'rgba(0, 0, 0, 0.05)', yrnqrguo: 'rgba(0, 0, 0, 0.05)',
nwjktjjq: 'rgba(0, 0, 0, 0.1)', nwjktjjq: 'rgba(0, 0, 0, 0.1)',
geavgsxy: 'rgba(0, 0, 0, 0.05)', geavgsxy: 'rgba(0, 0, 0, 0.05)',
nhzhphzx: 'rgba(0, 0, 0, 0.25)', nhzhphzx: 'rgba(0, 0, 0, 0.25)',
tyvedwbe: 'rgba(0, 0, 0, 0.1)',
bwqtlupy: 'rgba(0, 0, 0, 0.05)', bwqtlupy: 'rgba(0, 0, 0, 0.05)',
jkhztclx: ':lighten<5<@accent', jkhztclx: ':lighten<5<@accent',
zbqjwygh: ':darken<5<@accent', zbqjwygh: ':darken<5<@accent',

View File

@ -0,0 +1,14 @@
{
id: '8c539dc1-0fab-4d47-9194-39c508e9bfe1',
name: 'Black',
author: 'syuilo',
desc: 'Basic dark theme',
base: 'dark',
props: {
divider: '#2d2d2d',
messageBg: '#1d1d1d',
},
}

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