Merge branch 'develop'

This commit is contained in:
syuilo 2019-04-14 20:38:55 +09:00
commit d66e4b7ff9
No known key found for this signature in database
GPG Key ID: BDC4C49D06AB9D69
661 changed files with 15350 additions and 15328 deletions

View File

@ -0,0 +1,5 @@
# db settings
POSTGRES_PASSWORD="example-misskey-pass"
POSTGRES_USER="example-misskey-user"
POSTGRES_DB="misskey"

View File

@ -1,8 +1,16 @@
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Misskey configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────
# Final accessible URL seen by a user. # Final accessible URL seen by a user.
url: https://example.tld/ url: https://example.tld/
# ┌───────────────────────┐
#───┘ Port and TLS settings └───────────────────────────────────
### Port and TLS settings ######################################
# #
# Misskey supports two deployment options for public. # Misskey supports two deployment options for public.
# #
@ -34,24 +42,47 @@ url: https://example.tld/
# To use option 2, uncomment below lines. # To use option 2, uncomment below lines.
#port: 443 #port: 443
#
#https: #https:
# # path for certification # # path for certification
# key: /etc/letsencrypt/live/example.tld/privkey.pem # key: /etc/letsencrypt/live/example.tld/privkey.pem
# cert: /etc/letsencrypt/live/example.tld/fullchain.pem # cert: /etc/letsencrypt/live/example.tld/fullchain.pem
################################################################ # ┌──────────────────────────┐
#───┘ PostgreSQL configuration └────────────────────────────────
db:
mongodb:
host: localhost host: localhost
port: 27017 port: 5432
# Database name
db: misskey db: misskey
# Auth
user: example-misskey-user user: example-misskey-user
pass: example-misskey-pass pass: example-misskey-pass
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
redis:
host: localhost
port: 6379
#pass: example-pass
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 9200
# pass: null
# ┌────────────────────────────────────┐
#───┘ File storage (Drive) configuration └──────────────────────
drive: drive:
storage: 'db' storage: 'fs'
# OR # OR
@ -88,26 +119,28 @@ drive:
# accessKey: XXX # accessKey: XXX
# secretKey: YYY # secretKey: YYY
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method.
# You don't usually need to change this setting, but you can
# change it according to your preferences.
# Available methods:
# aid ... Short, Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
# If enabled: # If enabled:
# The first account created is automatically marked as Admin. # The first account created is automatically marked as Admin.
autoAdmin: true autoAdmin: true
#
# Below settings are optional
#
# Redis
#redis:
# host: localhost
# port: 6379
# pass: example-pass
# Elasticsearch
#elasticsearch:
# host: localhost
# port: 9200
# pass: null
# Whether disable HSTS # Whether disable HSTS
#disableHsts: true #disableHsts: true

View File

@ -1,13 +0,0 @@
var user = {
user: 'example-misskey-user',
pwd: 'example-misskey-pass',
roles: [
{
role: 'readWrite',
db: 'misskey'
}
]
};
db.createUser(user);

6
.dockerignore Executable file → Normal file
View File

@ -5,8 +5,8 @@
.vscode .vscode
Dockerfile Dockerfile
build/ build/
db/
docker-compose.yml docker-compose.yml
node_modules/
mongo/
redis/
elasticsearch/ elasticsearch/
node_modules/
redis/

4
.gitignore vendored
View File

@ -8,14 +8,14 @@
built built
/data /data
/.cache-loader /.cache-loader
/db
/elasticsearch
npm-debug.log npm-debug.log
*.pem *.pem
run.bat run.bat
api-docs.json api-docs.json
*.log *.log
/redis /redis
/mongo
/elasticsearch
*.code-workspace *.code-workspace
yarn.lock yarn.lock
.DS_Store .DS_Store

View File

@ -5,6 +5,32 @@ If you encounter any problems with updating, please try the following:
1. `npm run clean` or `npm run cleanall` 1. `npm run clean` or `npm run cleanall`
2. Retry update (Don't forget `npm i`) 2. Retry update (Don't forget `npm i`)
11.0.0 (daybreak)
-----------------
* **データベースがMongoDBからPostgreSQLに変更されました**
* **Redisが必須に**
* アカウントを完全に削除できるように
* 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように
* ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように
* フォロー申請数が実際より1すくなくなる問題を修正
* リストからアカウント削除したユーザーを削除できない問題を修正
* リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正
* リストTLでダイレクト投稿が流れない問題を修正
* ミュートしているユーザーの投稿がタイムラインに流れてくることがある問題を修正
### APIの破壊的変更
* v10時点で deprecated だったパラメータなどを削除
* ユーザーリストの title が name に
* リバーシの対局の`settings`プロパティがなくなり、その中にあったプロパティがすべて上の階層に
* 例えば`game.settings.map`は`game.map`になる
### 既知の問題
* アプリが作成できない
* 依存ライブラリの問題と思わるため、対応が難しい
### Migration
coming soon...
10.100.0 10.100.0
---------- ----------
* ユーザーリストでフォローボタンを表示するように * ユーザーリストでフォローボタンを表示するように

View File

@ -75,3 +75,95 @@ src ... Source code
test ... Test code test ... Test code
``` ```
## Notes
### placeholder
SQLをクエリビルダで組み立てる際、使用するプレースホルダは重複してはならない
例えば
``` ts
query.andWhere(new Brackets(qb => {
for (const type of ps.fileType) {
qb.orWhere(`:type = ANY(note.attachedFileTypes)`, { type: type });
}
}));
```
と書くと、ループ中で`type`というプレースホルダが複数回使われてしまいおかしくなる
だから次のようにする必要がある
```ts
query.andWhere(new Brackets(qb => {
for (const type of ps.fileType) {
const i = ps.fileType.indexOf(type);
qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type });
}
}));
```
### `null` in SQL
SQLを発行する際、パラメータが`null`になる可能性のある場合はSQL文を出し分けなければならない
例えば
``` ts
query.where('file.folderId = :folderId', { folderId: ps.folderId });
```
という処理で、`ps.folderId`が`null`だと結果的に`file.folderId = null`のようなクエリが発行されてしまい、これは正しいSQLではないので期待した結果が得られない
だから次のようにする必要がある
``` ts
if (ps.folderId) {
query.where('file.folderId = :folderId', { folderId: ps.folderId });
} else {
query.where('file.folderId IS NULL');
}
```
### `[]` in SQL
SQLを発行する際、`IN`のパラメータが`[]`(空の配列)になる可能性のある場合はSQL文を出し分けなければならない
例えば
``` ts
const users = await Users.find({
id: In(userIds)
});
```
という処理で、`userIds`が`[]`だと結果的に`user.id IN ()`のようなクエリが発行されてしまい、これは正しいSQLではないので期待した結果が得られない
だから次のようにする必要がある
``` ts
const users = userIds.length > 0 ? await Users.find({
id: In(userIds)
}) : [];
```
### 配列のインデックス in SQL
SQLでは配列のインデックスは**1始まり**。
`[a, b, c]``a`にアクセスしたいなら`[0]`ではなく`[1]`と書く
### `undefined`にご用心
MongoDBの時とは違い、findOneでレコードを取得する時に対象レコードが存在しない場合 **`undefined`** が返ってくるので注意。
MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください
### 簡素な`undefined`チェック
データベースからレコードを取得するときに、プログラムの流れ的に(ほぼ)絶対`undefined`にはならない場合でも、`undefined`チェックしないとTypeScriptに怒られます。
でもいちいち複数行を費やして、発生するはずのない`undefined`をチェックするのも面倒なので、`ensure`というユーティリティ関数を用意しています。
例えば、
``` ts
const user = await Users.findOne(userId);
// この時点で user の型は User | undefined
if (user == null) {
throw 'missing user';
}
// この時点で user の型は User
```
という処理を`ensure`を使うと
``` ts
const user = await Users.findOne(userId).then(ensure);
// この時点で user の型は User
```
という風に書けます。
もちろん`ensure`内部でエラーを握りつぶすようなことはしておらず、万が一`undefined`だった場合はPromiseがRejectされ後続の処理は実行されません。
``` ts
const user = await Users.findOne(userId).then(ensure);
// 万が一 Users.findOne の結果が undefined だったら、ensure でエラーが発生するので
// この行に到達することは無い
// なので、.then(ensure) は
// if (user == null) {
// throw 'missing user';
// }
// の糖衣構文のような扱いです
```

View File

@ -23,8 +23,9 @@ RUN apk add --no-cache \
zlib-dev zlib-dev
RUN npm i -g yarn RUN npm i -g yarn
COPY . ./ COPY package.json ./
RUN yarn install RUN yarn install
COPY . ./
RUN yarn build RUN yarn build
FROM base AS runner FROM base AS runner

View File

@ -1,4 +1,4 @@
<a href="https://ai.misskey.xyz/"><img src="https://github.com/syuilo/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/></a> <a href="https://xn--931a.moe/"><img src="https://github.com/syuilo/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/></a>
[![Misskey](/assets/title.png)](https://misskey.xyz/) [![Misskey](/assets/title.png)](https://misskey.xyz/)
================================================================ ================================================================
@ -101,30 +101,33 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
---------------------------------------------------------------- ----------------------------------------------------------------
<!-- PATREON_START --> <!-- PATREON_START -->
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1?token-time=2145916800&token-hash=HGkZJ7s4bSaQVoOJ5q30mTWHTxDLiw1LuyaogKPLy24%3D" alt="Hiroshi Seki" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/1?token-time=2145916800&token-hash=WeuDzzz24cRXJogyIkU-mxARqkdyms-rcZKbO-GpGjw%3D" alt="weep" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weep" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td> <td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4?token-time=2145916800&token-hash=vZdDTTF-ahiKBjjgppS2ev4rkD8H7TTKkXXoxsucs6Y%3D" alt="Melilot" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1?token-time=2145916800&token-hash=ubqJzjhBQUo8Nw6h_8jMDlJ5ocIO46EflpiRkp2jIw4%3D" alt="osapon" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
<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>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td> <td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
<td><a href="https://www.patreon.com/weepjp">weep</a></td> <td><a href="https://www.patreon.com/weepjp">weep</a></td>
<td><a href="https://www.patreon.com/user?u=12059069">naga_rus</a></td> <td><a href="https://www.patreon.com/user?u=12059069">naga_rus</a></td>
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td> <td><a href="https://www.patreon.com/osapon">osapon</a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td> <td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
</tr></table> </tr></table>
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=1FlxS9MEgmNGH_RHUVHbO5hIXB5I1z0lvA33CTvYvjA%3D" alt="gutfuckllc" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1.png?token-time=2145916800&token-hash=FMV7cPKBD1TU2WTbl1jg6AcdKSvTb2BSFcDhgc-EO8w%3D" alt="gutfuckllc" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1?token-time=2145916800&token-hash=0xgcpqvFDqRcV_YIEhcPNVH7gs9sLg_BBnTJXCkN4ao%3D" alt="mydarkstar" 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://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td> <td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
<td><img src="https://c8.patreon.com/2/200/18833336" alt="itiradi" width="100"></td> <td><img src="https://c8.patreon.com/2/200/18833336" alt="itiradi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=2PsbFNw0tnubZzgSXD01R6hIgncfiElG7H7HX2Y3dyo%3D" alt="nemu" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td> <td><img src="https://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?token-time=2145916800&token-hash=9JtETp0X8gI280Ne1E8bxn6j4Lw5o2k4mJkICx97V_k%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://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td> <td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1?token-time=2145916800&token-hash=95p8VdGX45E8BitZR_eOcDlqCjumjzNLBPQJrJdeCpI%3D" alt="takimura" 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.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
</tr><tr> </tr><tr>
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td> <td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td> <td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
@ -134,18 +137,19 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<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=17463605">Sampot</a></td> <td><a href="https://www.patreon.com/user?u=17463605">Sampot</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>
</tr></table> </tr></table>
<table><tr> <table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4?token-time=2145916800&token-hash=SbdZeN5SmsuT9stD6v0jN1z0hftg0FmRiCTxysU0Ihw%3D" alt="Damillora" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/935a10339daa4ede8e555903a0707060/1?token-time=2145916800&token-hash=3CrpqH-XtKs_NoIlSsTyVs8wCzP1WFCsG2xwps1IJq0%3D" alt="Atsuko Tominaga" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/935a10339daa4ede8e555903a0707060/1.png?token-time=2145916800&token-hash=c1XAS1qGBPxVdCvnICxtAUmx41mVkMG87h7cIRF9YYE%3D" alt="Atsuko Tominaga" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3?token-time=2145916800&token-hash=-iJszBqgYBhsM5qMdA1knf9wvprhEfESzKfR2oh7mIA%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/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=5T8XcaAf9Zyzfg3QubR06s_kJZkArVEM2dwObrBVAU4%3D" alt="Hiratake" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1?token-time=2145916800&token-hash=D6QK3fPyqiYKJfOzc-QqaSSairUrWdjld-ewp2waj6s%3D" alt="Hekovic" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=Ksk_2l3gjPDbnzMUOCSW1E-hdPJsNs2tSR4_RAakRK8%3D" alt="dansup" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=CXe9AqlZy9AsYfiWd3OBYVOzvODoN47Litz0Tu4BFpU%3D" alt="Gargron" width="100"></td> <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1?token-time=2145916800&token-hash=xhR1n6NAAyEb-IUXLD6_dshkFa3mefU5ZZuk1L8qKTs%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://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=uR-48MQ0A4j0irQSrCAQZJ-sJUSs_Fkihlg3-l59b7c%3D" alt="Takashi Shibuya" 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/damillora">Damillora</a></td> <td><a href="https://www.patreon.com/damillora">Damillora</a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td> <td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
@ -157,8 +161,11 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<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=12531784">Takashi Shibuya</a></td> <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table> </tr></table>
<table><tr>
</tr><tr>
</tr></table>
**Last updated:** Sat, 06 Apr 2019 03:35:05 UTC **Last updated:** Sun, 14 Apr 2019 08:13:12 UTC
<!-- PATREON_END --> <!-- PATREON_END -->
:four_leaf_clover: Copyright :four_leaf_clover: Copyright

View File

@ -1,23 +0,0 @@
const mongo = require('mongodb');
const User = require('../built/models/user').default;
const args = process.argv.slice(2);
const user = args[0];
const q = user.startsWith('@') ? {
username: user.split('@')[1],
host: user.split('@')[2] || null
} : { _id: new mongo.ObjectID(user) };
console.log(`Mark as admin ${user}...`);
User.update(q, {
$set: {
isAdmin: true
}
}).then(() => {
console.log(`Done ${user}`);
}, e => {
console.error(e);
});

View File

@ -1,57 +0,0 @@
// for Node.js interpret
const chalk = require('chalk');
const sequential = require('promise-sequential');
const { default: User } = require('../../built/models/user');
const { default: DriveFile } = require('../../built/models/drive-file');
async function main() {
const promiseGens = [];
const count = await DriveFile.count({});
let prev;
for (let i = 0; i < count; i++) {
promiseGens.push(() => {
const promise = new Promise(async (res, rej) => {
const file = await DriveFile.findOne(prev ? {
_id: { $gt: prev._id }
} : {}, {
sort: {
_id: 1
}
});
prev = file;
const user = await User.findOne({ _id: file.metadata.userId });
DriveFile.update({
_id: file._id
}, {
$set: {
'metadata._user': {
host: user.host
}
}
}).then(() => {
res([i, file]);
}).catch(rej);
});
promise.then(([i, file]) => {
console.log(chalk`{gray ${i}} {green done: {bold ${file._id}} ${file.filename}}`);
});
return promise;
});
}
return await sequential(promiseGens);
}
main().then(() => {
console.log('ALL DONE');
}).catch(console.error);

View File

@ -1,71 +0,0 @@
// for Node.js interpret
const chalk = require('chalk');
const sequential = require('promise-sequential');
const { default: User } = require('../../built/models/user');
const { default: DriveFile } = require('../../built/models/drive-file');
async function main() {
const promiseGens = [];
const count = await User.count({});
let prev;
for (let i = 0; i < count; i++) {
promiseGens.push(() => {
const promise = new Promise(async (res, rej) => {
const user = await User.findOne(prev ? {
_id: { $gt: prev._id }
} : {}, {
sort: {
_id: 1
}
});
prev = user;
const set = {};
if (user.avatarId != null) {
const file = await DriveFile.findOne({ _id: user.avatarId });
if (file && file.metadata.properties.avgColor) {
set.avatarColor = file.metadata.properties.avgColor;
}
}
if (user.bannerId != null) {
const file = await DriveFile.findOne({ _id: user.bannerId });
if (file && file.metadata.properties.avgColor) {
set.bannerColor = file.metadata.properties.avgColor;
}
}
if (Object.keys(set).length === 0) return res([i, user]);
User.update({
_id: user._id
}, {
$set: set
}).then(() => {
res([i, user]);
}).catch(rej);
});
promise.then(([i, user]) => {
console.log(chalk`{gray ${i}} {green done: {bold ${user._id}} @${user.username}}`);
});
return promise;
});
}
return await sequential(promiseGens);
}
main().then(() => {
console.log('ALL DONE');
}).catch(console.error);

View File

@ -1,9 +0,0 @@
const { default: DriveFile } = require('../../built/models/drive-file');
DriveFile.update({}, {
$rename: {
'metadata.isMetaOnly': 'metadata.withoutChunks'
}
}, {
multi: true
});

View File

@ -1,134 +0,0 @@
const { default: Stats } = require('../../built/models/stats');
const { default: User } = require('../../built/models/user');
const { default: Note } = require('../../built/models/note');
const { default: DriveFile } = require('../../built/models/drive-file');
const now = new Date();
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
const today = new Date(y, m, d);
async function main() {
const localUsersCount = await User.count({
host: null
});
const remoteUsersCount = await User.count({
host: { $ne: null }
});
const localNotesCount = await Note.count({
'_user.host': null
});
const remoteNotesCount = await Note.count({
'_user.host': { $ne: null }
});
const localDriveFilesCount = await DriveFile.count({
'metadata._user.host': null
});
const remoteDriveFilesCount = await DriveFile.count({
'metadata._user.host': { $ne: null }
});
const localDriveFilesSize = await DriveFile
.aggregate([{
$match: {
'metadata._user.host': null,
'metadata.deletedAt': { $exists: false }
}
}, {
$project: {
length: true
}
}, {
$group: {
_id: null,
usage: { $sum: '$length' }
}
}])
.then(aggregates => {
if (aggregates.length > 0) {
return aggregates[0].usage;
}
return 0;
});
const remoteDriveFilesSize = await DriveFile
.aggregate([{
$match: {
'metadata._user.host': { $ne: null },
'metadata.deletedAt': { $exists: false }
}
}, {
$project: {
length: true
}
}, {
$group: {
_id: null,
usage: { $sum: '$length' }
}
}])
.then(aggregates => {
if (aggregates.length > 0) {
return aggregates[0].usage;
}
return 0;
});
await Stats.insert({
date: today,
users: {
local: {
total: localUsersCount,
diff: 0
},
remote: {
total: remoteUsersCount,
diff: 0
}
},
notes: {
local: {
total: localNotesCount,
diff: 0,
diffs: {
normal: 0,
reply: 0,
renote: 0
}
},
remote: {
total: remoteNotesCount,
diff: 0,
diffs: {
normal: 0,
reply: 0,
renote: 0
}
}
},
drive: {
local: {
totalCount: localDriveFilesCount,
totalSize: localDriveFilesSize,
diffCount: 0,
diffSize: 0
},
remote: {
totalCount: remoteDriveFilesCount,
totalSize: remoteDriveFilesSize,
diffCount: 0,
diffSize: 0
}
}
});
console.log('done');
}
main();

View File

@ -1,144 +0,0 @@
const { default: Stats } = require('../../built/models/stats');
const { default: User } = require('../../built/models/user');
const { default: Note } = require('../../built/models/note');
const { default: DriveFile } = require('../../built/models/drive-file');
const now = new Date();
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
const h = now.getHours();
const date = new Date(y, m, d, h);
async function main() {
await Stats.update({}, {
$set: {
span: 'day'
}
}, {
multi: true
});
const localUsersCount = await User.count({
host: null
});
const remoteUsersCount = await User.count({
host: { $ne: null }
});
const localNotesCount = await Note.count({
'_user.host': null
});
const remoteNotesCount = await Note.count({
'_user.host': { $ne: null }
});
const localDriveFilesCount = await DriveFile.count({
'metadata._user.host': null
});
const remoteDriveFilesCount = await DriveFile.count({
'metadata._user.host': { $ne: null }
});
const localDriveFilesSize = await DriveFile
.aggregate([{
$match: {
'metadata._user.host': null,
'metadata.deletedAt': { $exists: false }
}
}, {
$project: {
length: true
}
}, {
$group: {
_id: null,
usage: { $sum: '$length' }
}
}])
.then(aggregates => {
if (aggregates.length > 0) {
return aggregates[0].usage;
}
return 0;
});
const remoteDriveFilesSize = await DriveFile
.aggregate([{
$match: {
'metadata._user.host': { $ne: null },
'metadata.deletedAt': { $exists: false }
}
}, {
$project: {
length: true
}
}, {
$group: {
_id: null,
usage: { $sum: '$length' }
}
}])
.then(aggregates => {
if (aggregates.length > 0) {
return aggregates[0].usage;
}
return 0;
});
await Stats.insert({
date: date,
span: 'hour',
users: {
local: {
total: localUsersCount,
diff: 0
},
remote: {
total: remoteUsersCount,
diff: 0
}
},
notes: {
local: {
total: localNotesCount,
diff: 0,
diffs: {
normal: 0,
reply: 0,
renote: 0
}
},
remote: {
total: remoteNotesCount,
diff: 0,
diffs: {
normal: 0,
reply: 0,
renote: 0
}
}
},
drive: {
local: {
totalCount: localDriveFilesCount,
totalSize: localDriveFilesSize,
diffCount: 0,
diffSize: 0
},
remote: {
totalCount: remoteDriveFilesCount,
totalSize: remoteDriveFilesSize,
diffCount: 0,
diffSize: 0
}
}
});
console.log('done');
}
main();

View File

@ -5,8 +5,8 @@ services:
build: . build: .
restart: always restart: always
links: links:
- mongo - db
# - redis - redis
# - es # - es
ports: ports:
- "127.0.0.1:3000:3000" - "127.0.0.1:3000:3000"
@ -14,26 +14,23 @@ services:
- internal_network - internal_network
- external_network - external_network
# redis: redis:
# restart: always
# image: redis:4.0-alpine
# networks:
# - internal_network
### Uncomment to enable Redis persistance
## volumes:
## - ./redis:/data
mongo:
restart: always restart: always
image: mongo:4.1 image: redis:4.0-alpine
networks: networks:
- internal_network - internal_network
environment:
MONGO_INITDB_DATABASE: "misskey"
volumes: volumes:
- ./.config/mongo_initdb.js:/docker-entrypoint-initdb.d/mongo_initdb.js:ro - ./redis:/data
### Uncomment to enable MongoDB persistance
# - ./mongo:/data db:
restart: always
image: postgres:11.2-alpine
networks:
- internal_network
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
# es: # es:
# restart: always # restart: always
@ -42,9 +39,8 @@ services:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# networks: # networks:
# - internal_network # - internal_network
#### Uncomment to enable ES persistence # volumes:
## volumes: # - ./elasticsearch:/usr/share/elasticsearch/data
## - ./elasticsearch:/usr/share/elasticsearch/data
networks: networks:
internal_network: internal_network:

View File

@ -1,22 +0,0 @@
Comment faire une sauvegarde de votre Misskey ?
==========================
Assurez-vous d'avoir installé **mongodb-tools**.
---
Dans votre terminal :
``` shell
$ mongodump --archive=db-backup -u <VotreNomdUtilisateur> -p <VotreMotDePasse>
```
Pour plus de détails, merci de consulter [la documentation de mongodump](https://docs.mongodb.com/manual/reference/program/mongodump/).
Restauration
-------
``` shell
$ mongorestore --archive=db-backup
```
Pour plus de détails, merci de consulter [la documentation de mongorestore](https://docs.mongodb.com/manual/reference/program/mongorestore/).

View File

@ -1,22 +0,0 @@
How to backup your Misskey
==========================
Make sure **mongodb-tools** installed.
---
In your shell:
``` shell
$ mongodump --archive=db-backup -u <YourUserName> -p <YourPassword>
```
For details, please see [mongodump docs](https://docs.mongodb.com/manual/reference/program/mongodump/).
Restore
-------
``` shell
$ mongorestore --archive=db-backup
```
For details, please see [mongorestore docs](https://docs.mongodb.com/manual/reference/program/mongorestore/).

View File

@ -11,13 +11,41 @@ This guide describes how to install and setup Misskey with Docker.
---------------------------------------------------------------- ----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` Clone Misskey repository's master branch. 1. `git clone -b master git://github.com/syuilo/misskey.git` Clone Misskey repository's master branch.
2. `cd misskey` Move to misskey directory. 2. `cd misskey` Move to misskey directory.
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag. 3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag.
*2.* Configure Misskey *2.* Configure Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`. Create configuration files with following:
3. Edit `default.yml` and `mongo_initdb.js`.
```bash
cd .config
cp example.yml default.yml
cp docker_example.env docker.env
```
### `default.yml`
Edit this file the same as non-Docker environment.
However hostname of Postgresql, Redis and Elasticsearch are not `localhost`, they are set in `docker-compose.yml`.
The following is default hostname:
| Service | Hostname |
|---------------|----------|
| Postgresql | `db` |
| Redis | `redis` |
| Elasticsearch | `es` |
### `docker.env`
Configure Postgresql in this file.
The minimum required settings are:
| name | Description |
|---------------------|---------------|
| `POSTGRES_PASSWORD` | Password |
| `POSTGRES_USER` | Username |
| `POSTGRES_DB` | Database name |
*3.* Configure Docker *3.* Configure Docker
---------------------------------------------------------------- ----------------------------------------------------------------
@ -39,7 +67,7 @@ Just `docker-compose up -d`. GLHF!
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git fetch` 1. `git fetch`
2. `git stash` 2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` 3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)`
4. `git stash pop` 4. `git stash pop`
5. `docker-compose build` 5. `docker-compose build`
6. Check [ChangeLog](../CHANGELOG.md) for migration information 6. Check [ChangeLog](../CHANGELOG.md) for migration information

View File

@ -12,7 +12,7 @@ Ce guide explique comment installer et configurer Misskey avec Docker.
---------------------------------------------------------------- ----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` Clone le dépôt de Misskey sur la branche master. 1. `git clone -b master git://github.com/syuilo/misskey.git` Clone le dépôt de Misskey sur la branche master.
2. `cd misskey` Naviguez dans le dossier du dépôt. 2. `cd misskey` Naviguez dans le dossier du dépôt.
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout sur le tag de la [dernière version](https://github.com/syuilo/misskey/releases/latest). 3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout sur le tag de la [dernière version](https://github.com/syuilo/misskey/releases/latest).
*2.* Configuration de Misskey *2.* Configuration de Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
@ -40,7 +40,7 @@ Utilisez la commande `docker-compose up -d`. GLHF!
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git fetch` 1. `git fetch`
2. `git stash` 2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` 3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)`
4. `git stash pop` 4. `git stash pop`
5. `docker-compose build` 5. `docker-compose build`
6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration 6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration

View File

@ -11,13 +11,41 @@ Dockerを使ったMisskey構築方法
---------------------------------------------------------------- ----------------------------------------------------------------
1. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン 1. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン
2. `cd misskey` misskeyディレクトリに移動 2. `cd misskey` misskeyディレクトリに移動
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認 3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
*2.* 設定ファイルを作成する *2.* 設定ファイルの作成と編集
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` `.config/mongo_initdb_example.js`をコピーし名前を`mongo_initdb.js`にする 下記コマンドで設定ファイルを作成してください。
3. `default.yml`と`mongo_initdb.js`を編集する
```bash
cd .config
cp example.yml default.yml
cp docker_example.env docker.env
```
### `default.yml`の編集
非Docker環境と同じ様に編集してください。
ただし、Postgresql、RedisとElasticsearchのホストは`localhost`ではなく、`docker-compose.yml`で設定されたサービス名になっています。
標準設定では次の通りです。
| サービス | ホスト名 |
|---------------|---------|
| Postgresql |`db` |
| Redis |`redis` |
| Elasticsearch |`es` |
### `docker.env`の編集
このファイルはPostgresqlの設定を記述します。
最低限記述する必要がある設定は次の通りです。
| 設定 | 内容 |
|---------------------|--------------|
| `POSTGRES_PASSWORD` | パスワード |
| `POSTGRES_USER` | ユーザー名 |
| `POSTGRES_DB` | データベース名 |
*3.* Dockerの設定 *3.* Dockerの設定
---------------------------------------------------------------- ----------------------------------------------------------------
@ -39,7 +67,7 @@ Dockerを使ったMisskey構築方法
### Misskeyを最新バージョンにアップデートする方法: ### Misskeyを最新バージョンにアップデートする方法:
1. `git fetch` 1. `git fetch`
2. `git stash` 2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` 3. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)`
4. `git stash pop` 4. `git stash pop`
5. `docker-compose build` 5. `docker-compose build`
6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する 6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する

View File

@ -22,37 +22,28 @@ adduser --disabled-password --disabled-login misskey
Please install and setup these softwares: Please install and setup these softwares:
#### Dependencies :package: #### Dependencies :package:
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0 * **[Node.js](https://nodejs.org/en/)** >= 11.7.0
* **[MongoDB](https://www.mongodb.com/)** >= 3.6 * **[PostgreSQL](https://www.postgresql.org/)** >= 10
* **[Redis](https://redis.io/)**
##### Optional ##### Optional
* [Redis](https://redis.io/)
* Redis is optional, but we strongly recommended to install it
* [Elasticsearch](https://www.elastic.co/) - required to enable the search feature * [Elasticsearch](https://www.elastic.co/) - required to enable the search feature
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
*3.* Setup MongoDB *3.* Install Misskey
----------------------------------------------------------------
As root:
1. `mongo` Go to the mongo shell
2. `use misskey` Use the misskey database
3. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` Create the misskey user.
4. `exit` You're done!
*4.* Install Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `su - misskey` Connect to misskey user. 1. `su - misskey` Connect to misskey user.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clone the misskey repo from master branch. 2. `git clone -b master git://github.com/syuilo/misskey.git` Clone the misskey repo from master branch.
3. `cd misskey` Navigate to misskey directory 3. `cd misskey` Navigate to misskey directory
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) 4. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Install misskey dependencies. 5. `npm install` Install misskey dependencies.
*5.* Configure Misskey *4.* Configure Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`. 1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
2. Edit `default.yml` 2. Edit `default.yml`
*6.* Build Misskey *5.* Build Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
Build misskey with the following: Build misskey with the following:
@ -68,6 +59,12 @@ If you're still encountering errors about some modules, use node-gyp:
3. `node-gyp build` 3. `node-gyp build`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
*6.* Init DB
----------------------------------------------------------------
``` shell
npm run init
```
*7.* That is it. *7.* That is it.
---------------------------------------------------------------- ----------------------------------------------------------------
Well done! Now, you have an environment that run to Misskey. Well done! Now, you have an environment that run to Misskey.
@ -107,7 +104,7 @@ You can check if the service is running with `systemctl status misskey`.
### How to update your Misskey server to the latest version ### How to update your Misskey server to the latest version
1. `git fetch` 1. `git fetch`
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` 2. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)`
3. `npm install` 3. `npm install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
5. Check [ChangeLog](../CHANGELOG.md) for migration information 5. Check [ChangeLog](../CHANGELOG.md) for migration information

View File

@ -22,37 +22,28 @@ adduser --disabled-password --disabled-login misskey
Installez les paquets suivants : Installez les paquets suivants :
#### Dépendences :package: #### Dépendences :package:
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0 * **[Node.js](https://nodejs.org/en/)** >= 11.7.0
* **[MongoDB](https://www.mongodb.com/)** >= 3.6 * **[PostgreSQL](https://www.postgresql.org/)** >= 10
* **[Redis](https://redis.io/)**
##### Optionnels ##### Optionnels
* [Redis](https://redis.io/)
* Redis est optionnel mais nous vous recommandons vivement de l'installer
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche * [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
*3.* Paramètrage de MongoDB *3.* Installation de Misskey
----------------------------------------------------------------
En root :
1. `mongo` Ouvrez le shell mongo
2. `use misskey` Utilisez la base de données misskey
3. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` Créez l'utilisateur misskey.
4. `exit` Vous avez terminé !
*4.* Installation de Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
1. `su - misskey` Basculez vers l'utilisateur misskey. 1. `su - misskey` Basculez vers l'utilisateur misskey.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey. 2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey.
3. `cd misskey` Accédez au dossier misskey. 3. `cd misskey` Accédez au dossier misskey.
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout sur le tag de la [version la plus récente](https://github.com/syuilo/misskey/releases/latest) 4. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` Checkout sur le tag de la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Installez les dépendances de misskey. 5. `npm install` Installez les dépendances de misskey.
*5.* Création du fichier de configuration *4.* Création du fichier de configuration
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le`default.yml`. 1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le`default.yml`.
2. Editez le fichier `default.yml` 2. Editez le fichier `default.yml`
*6.* Construction de Misskey *5.* Construction de Misskey
---------------------------------------------------------------- ----------------------------------------------------------------
Construisez Misskey comme ceci : Construisez Misskey comme ceci :
@ -68,7 +59,7 @@ Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
3. `node-gyp build` 3. `node-gyp build`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
*7.* C'est tout. *6.* C'est tout.
---------------------------------------------------------------- ----------------------------------------------------------------
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
@ -107,7 +98,7 @@ Vous pouvez vérifier si le service a démarré en utilisant la commande `system
### Méthode de mise à jour vers la plus récente version de Misskey ### Méthode de mise à jour vers la plus récente version de Misskey
1. `git fetch` 1. `git fetch`
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` 2. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)`
3. `npm install` 3. `npm install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration. 5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.

View File

@ -22,44 +22,29 @@ adduser --disabled-password --disabled-login misskey
これらのソフトウェアをインストール・設定してください: これらのソフトウェアをインストール・設定してください:
#### 依存関係 :package: #### 依存関係 :package:
* **[Node.js](https://nodejs.org/en/)** (10.0.0以上) * **[Node.js](https://nodejs.org/en/)** (11.7.0以上)
* **[MongoDB](https://www.mongodb.com/)** (3.6以上) * **[PostgreSQL](https://www.postgresql.org/)** (10以上)
* **[Redis](https://redis.io/)**
##### オプション ##### オプション
* [Redis](https://redis.io/)
* Redisはオプションですが、インストールすることを強く推奨します。
* インストールしなくていいのは、あなたのインスタンスが自分専用のときだけとお考えください。
* 具体的には、Redisをインストールしないと、次の事が出来なくなります:
* Misskeyプロセスを複数起動しての負荷分散
* レートリミット
* ジョブキュー
* Twitter連携
* [Elasticsearch](https://www.elastic.co/) * [Elasticsearch](https://www.elastic.co/)
* 検索機能を有効にするためにはインストールが必要です。 * 検索機能を有効にするためにはインストールが必要です。
* [FFmpeg](https://www.ffmpeg.org/) * [FFmpeg](https://www.ffmpeg.org/)
*3.* MongoDBの設定 *3.* Misskeyのインストール
----------------------------------------------------------------
ルートで:
1. `mongo` mongoシェルを起動
2. `use misskey` misskeyデータベースを使用
3. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` misskeyユーザーを作成
4. `exit` mongoシェルを終了
*4.* Misskeyのインストール
---------------------------------------------------------------- ----------------------------------------------------------------
1. `su - misskey` misskeyユーザーを使用 1. `su - misskey` misskeyユーザーを使用
2. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン 2. `git clone -b master git://github.com/syuilo/misskey.git` masterブランチからMisskeyレポジトリをクローン
3. `cd misskey` misskeyディレクトリに移動 3. `cd misskey` misskeyディレクトリに移動
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認 4. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
5. `npm install` Misskeyの依存パッケージをインストール 5. `npm install` Misskeyの依存パッケージをインストール
*5.* 設定ファイルを作成する *4.* 設定ファイルを作成する
---------------------------------------------------------------- ----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする。 1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする。
2. `default.yml` を編集する。 2. `default.yml` を編集する。
*6.* Misskeyのビルド *5.* Misskeyのビルド
---------------------------------------------------------------- ----------------------------------------------------------------
次のコマンドでMisskeyをビルドしてください: 次のコマンドでMisskeyをビルドしてください:
@ -74,6 +59,12 @@ Debianをお使いであれば、`build-essential`パッケージをインスト
3. `node-gyp build` 3. `node-gyp build`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
*6.* データベースを初期化
----------------------------------------------------------------
``` shell
npm run init
```
*7.* 以上です! *7.* 以上です!
---------------------------------------------------------------- ----------------------------------------------------------------
お疲れ様でした。これでMisskeyを動かす準備は整いました。 お疲れ様でした。これでMisskeyを動かす準備は整いました。
@ -113,7 +104,7 @@ CentOSで1024以下のポートを使用してMisskeyを使用する場合は`Ex
### Misskeyを最新バージョンにアップデートする方法: ### Misskeyを最新バージョンにアップデートする方法:
1. `git fetch` 1. `git fetch`
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` 2. `git checkout $(git tag -l | grep -Ev -- '-(rc|alpha)\.[0-9]+$' | sort -V | tail -n 1)`
3. `npm install` 3. `npm install`
4. `NODE_ENV=production npm run build` 4. `NODE_ENV=production npm run build`
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する 5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する

View File

@ -120,7 +120,7 @@ gulp.task('copy:client', () =>
]) ])
.pipe(isProduction ? (imagemin as any)() : gutil.noop()) .pipe(isProduction ? (imagemin as any)() : gutil.noop())
.pipe(rename(path => { .pipe(rename(path => {
path.dirname = path.dirname.replace('assets', '.'); path.dirname = path.dirname!.replace('assets', '.');
})) }))
.pipe(gulp.dest('./built/client/assets/')) .pipe(gulp.dest('./built/client/assets/'))
); );

View File

@ -1 +1 @@
require('./built'); require('./built').default();

View File

@ -20,7 +20,7 @@ common:
outro: "Podívejte se na unikátní vlastnosti Misskey vlastníma očima! Pokud si myslíte, že tato instance není pro vás, zkuste jiné instance, neboť Misskey je decentralizovaná sociální síť, takže můžete snadno najít své přátele. Hodně štěstí a zábavy!" outro: "Podívejte se na unikátní vlastnosti Misskey vlastníma očima! Pokud si myslíte, že tato instance není pro vás, zkuste jiné instance, neboť Misskey je decentralizovaná sociální síť, takže můžete snadno najít své přátele. Hodně štěstí a zábavy!"
adblock: adblock:
detected: "Prosím vypněte svůj blokovač reklam" detected: "Prosím vypněte svůj blokovač reklam"
warning: "<strong>Misskey nepoužívá reklamy</strong>, některé vlastnosti však mohou být nedostupné nebo mohou způsobovat chyby, pokud máte povolený blokovač reklam." warning: "Některé vlastnosti mohou být nedostupné nebo mohou způsobovat chyby, pokud máte povolený blokovač reklam. <strong>Misskey nepoužívá reklamy</strong>."
application-authorization: "Autorizované aplikace" application-authorization: "Autorizované aplikace"
close: "Zavřít" close: "Zavřít"
do-not-copy-paste: "Prosím nezadávejte ani nevkládejte sem kód. Váš účet může být kompromitován." do-not-copy-paste: "Prosím nezadávejte ani nevkládejte sem kód. Váš účet může být kompromitován."
@ -227,6 +227,7 @@ common:
cancel: "Zrušit" cancel: "Zrušit"
update-available-title: "Aktualizace k dispozici" update-available-title: "Aktualizace k dispozici"
update-available: "Je k dispozici nová verze Misskey ({newer},vaše verze je {current}). Pro aplikování nové verze znovunačtěte stránku." update-available: "Je k dispozici nová verze Misskey ({newer},vaše verze je {current}). Pro aplikování nové verze znovunačtěte stránku."
my-token-regenerated: "Váš token byl regenerován, proto budete odhlášen/a."
verified-user: "Ověřené účty" verified-user: "Ověřené účty"
hide-password: "Skrýt heslo" hide-password: "Skrýt heslo"
show-password: "Zobrazit heslo" show-password: "Zobrazit heslo"
@ -281,12 +282,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "Chcete dovolit aplikaci <i>{name}</i> přístup k vašemu účtu?" share-access: "Chcete dovolit aplikaci <i>{name}</i> přístup k vašemu účtu?"
permission-ask: "Tato aplikace vyžaduje následující oprávnění:" permission-ask: "Tato aplikace vyžaduje následující oprávnění:"
account-read: "Zobrazit informace účtu"
note-write: "Odeslat."
following-write: "Sledovat a přestat sledovat"
drive-read: "Přečíst váš Disk"
notification-read: "Sledovat oznámení."
notification-write: "Zpravovat notifikace."
cancel: "Zrušit" cancel: "Zrušit"
accept: "Povolit přístup" accept: "Povolit přístup"
auth/views/index.vue: auth/views/index.vue:
@ -302,6 +297,7 @@ common/views/pages/explore.vue:
popular-tags: "Populární tagy" popular-tags: "Populární tagy"
federated: "Z fedivesmíru" federated: "Z fedivesmíru"
explore: "Prozkoumat {host}" explore: "Prozkoumat {host}"
users-info: "Aktuálně je zde registrováno {users} uživatelů"
common/views/components/url-preview.vue: common/views/components/url-preview.vue:
enable-player: "Otevřít v přehrávači" enable-player: "Otevřít v přehrávači"
disable-player: "Zavřít přehrávač" disable-player: "Zavřít přehrávač"
@ -354,19 +350,23 @@ common/views/components/connect-failed.troubleshooter.vue:
no-network-desc: "Ujistěte se že jste připojeni k Internetu." no-network-desc: "Ujistěte se že jste připojeni k Internetu."
no-internet: "Nejste připojeni k internetu" no-internet: "Nejste připojeni k internetu"
no-internet-desc: "Jste připojen k síti, ale zdá se že stále chybí připojení k Internetu. Prosím zkontrolujte Vaše připojení k Internetu." no-internet-desc: "Jste připojen k síti, ale zdá se že stále chybí připojení k Internetu. Prosím zkontrolujte Vaše připojení k Internetu."
no-server: "Nelze se připojit k serveru Misskey"
common/views/components/media-banner.vue: common/views/components/media-banner.vue:
click-to-show: "Klikněte pro zobrazení" click-to-show: "Klikněte pro zobrazení"
common/views/components/theme.vue: common/views/components/theme.vue:
light-theme: "Šablona pro použití ve světlém vzhledu" light-theme: "Motiv pro použití ve světlém vzhledu"
dark-theme: "Šablona pro použití v tmavém vzhledu" dark-theme: "Motiv pro použití v tmavém vzhledu"
light-themes: "Světlý vzhled" light-themes: "Světlý vzhled"
dark-themes: "Tmavý vzhled" dark-themes: "Tmavý vzhled"
install-a-theme: "Nainstalovat šablonu" install-a-theme: "Nainstalovat motiv"
theme-code: "Kód šablony" theme-code: "Kód motivu"
install: "Nainstalovat" install: "Nainstalovat"
installed: "\"{}\" byl nainstalován" installed: "\"{}\" byl nainstalován"
create-a-theme: "Vytvořit motiv" create-a-theme: "Vytvořit motiv"
save-created-theme: "Uložit motiv"
base-theme: "Základní vzhled" base-theme: "Základní vzhled"
base-theme-light: "Světlý"
base-theme-dark: "Tmavý"
find-more-theme: "Najít další vzhledy" find-more-theme: "Najít další vzhledy"
theme-name: "Jméno vzhledu" theme-name: "Jméno vzhledu"
preview-created-theme: "Náhled" preview-created-theme: "Náhled"
@ -435,6 +435,7 @@ common/views/components/user-menu.vue:
unblock: "Odblokovat" unblock: "Odblokovat"
push-to-list: "Přidat do seznamu" push-to-list: "Přidat do seznamu"
select-list: "Vyberte seznam" select-list: "Vyberte seznam"
report-abuse: "Nahlásit spam"
report-abuse-reported: "Problém byl nahlášen administrátorovi. Děkujeme za Vaší kooperaci." report-abuse-reported: "Problém byl nahlášen administrátorovi. Děkujeme za Vaší kooperaci."
silence: "Ztlumit" silence: "Ztlumit"
suspend: "Zmrazit" suspend: "Zmrazit"
@ -793,8 +794,6 @@ desktop/views/components/settings.tags.vue:
title: "Tagy" title: "Tagy"
add: "Přidat" add: "Přidat"
save: "Uložit" save: "Uložit"
desktop/views/components/taskmanager.vue:
title: "Správce úloh"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Domů" home: "Domů"
local: "Lokální" local: "Lokální"
@ -839,7 +838,7 @@ admin/views/index.vue:
emoji: "Emoji" emoji: "Emoji"
moderators: "Moderátoři" moderators: "Moderátoři"
users: "Uživatelé" users: "Uživatelé"
federation: "Federovaná" federation: "Z fedivesmíru"
announcements: "Oznámení" announcements: "Oznámení"
hashtags: "Hashtagy" hashtags: "Hashtagy"
queue: "Fronta úloh" queue: "Fronta úloh"
@ -851,7 +850,7 @@ admin/views/dashboard.vue:
drive: "Disk" drive: "Disk"
instances: "Instance" instances: "Instance"
this-instance: "Tato instance" this-instance: "Tato instance"
federated: "Federovaná" federated: "Z fedivesmíru"
admin/views/abuse.vue: admin/views/abuse.vue:
details: "Popis" details: "Popis"
remove-report: "Odstranit" remove-report: "Odstranit"
@ -1006,7 +1005,7 @@ admin/views/federation.vue:
status: "Status" status: "Status"
latest-request-received-at: "Poslední požadavek přijat" latest-request-received-at: "Poslední požadavek přijat"
block: "Blokován" block: "Blokován"
instances: "Federovaná" instances: "Z fedivesmíru"
states: states:
all: "Všechny" all: "Všechny"
blocked: "Blokován" blocked: "Blokován"
@ -1184,10 +1183,3 @@ deck/deck.user-column.vue:
dev/views/new-app.vue: dev/views/new-app.vue:
app-name-desc: "Jméno vaší aplikace" app-name-desc: "Jméno vaší aplikace"
app-desc: "Stručný popis nebo představení vaší aplikace." app-desc: "Stručný popis nebo představení vaší aplikace."
account-read: "Zobrazit informace účtu"
note-write: "Odeslat."
reaction-write: "Přidat nebo odebrat reakce."
following-write: "Sledovat a přestat sledovat"
drive-read: "Přečíst váš Disk"
notification-read: "Sledovat oznámení."
notification-write: "Zpravovat notifikace."

View File

@ -138,13 +138,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "Erlaubst Du <i>{name}</i> auf deinen Account zuzugreifen?" share-access: "Erlaubst Du <i>{name}</i> auf deinen Account zuzugreifen?"
permission-ask: "Diese Applikation benötigt folgende Berechtigungen:" permission-ask: "Diese Applikation benötigt folgende Berechtigungen:"
account-read: "Accountinformationen anzeigen."
account-write: "Accountinformationen bearbeiten."
note-write: "Senden."
like-write: "Auf Beiträge reagieren."
following-write: "Folgen oder entfolgen."
notification-read: "Siehe deine Benachrichtigungen."
notification-write: "Benachrichtigungen verwalten."
cancel: "Abbrechen" cancel: "Abbrechen"
accept: "Zugriff erlauben." accept: "Zugriff erlauben."
auth/views/index.vue: auth/views/index.vue:
@ -478,7 +471,6 @@ desktop/views/components/post-form.vue:
posting: "Posting" posting: "Posting"
attach-media-from-local: "Medien von deinem PC hinzufügen" attach-media-from-local: "Medien von deinem PC hinzufügen"
attach-media-from-drive: "Medien von deinem Speicher hinzufügen" attach-media-from-drive: "Medien von deinem Speicher hinzufügen"
attach-cancel: "Hinzufügen abbrechen"
create-poll: "Eine Abstimmung erstellen" create-poll: "Eine Abstimmung erstellen"
text-remain: "{} Zeichen verbleibend" text-remain: "{} Zeichen verbleibend"
visibility: "Sichtbarkeit" visibility: "Sichtbarkeit"
@ -524,8 +516,6 @@ desktop/views/components/sub-note-content.vue:
desktop/views/components/settings.tags.vue: desktop/views/components/settings.tags.vue:
add: "Hinzufügen" add: "Hinzufügen"
save: "Speichern" save: "Speichern"
desktop/views/components/taskmanager.vue:
title: "Taskmanager"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Home" home: "Home"
local: "Lokal" local: "Lokal"
@ -664,10 +654,3 @@ deck:
rename: "Umbenennen" rename: "Umbenennen"
deck/deck.user-column.vue: deck/deck.user-column.vue:
activity: "Aktivität" activity: "Aktivität"
dev/views/new-app.vue:
account-read: "Accountinformationen anzeigen."
account-write: "Accountinformationen bearbeiten."
note-write: "Senden."
following-write: "Folgen oder entfolgen."
notification-read: "Siehe deine Benachrichtigungen."
notification-write: "Benachrichtigungen verwalten."

View File

@ -69,6 +69,11 @@ common:
following: "Following" following: "Following"
followers: "Followers" followers: "Followers"
favorites: "Favorites" favorites: "Favorites"
permissions:
'read:account': "View account information"
'write:account': "Update your account information"
'read:drive': "Browse the Drive"
'write:drive': "Work with the Drive"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Following users will show their posts in your timeline." follow-users-to-make-your-timeline: "Following users will show their posts in your timeline."
explore: "Find users" explore: "Find users"
@ -282,15 +287,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "Would you allow <i>{name}</i> to access your account?" share-access: "Would you allow <i>{name}</i> to access your account?"
permission-ask: "This application requires the following permissions:" permission-ask: "This application requires the following permissions:"
account-read: "View account information."
account-write: "Modify account information."
note-write: "Post."
like-write: "Express yourself about this post."
following-write: "Follow and unfollow."
drive-read: "Read your drive."
drive-write: "Upload/delete files in your drive."
notification-read: "Read your notifications."
notification-write: "Manage your notifications."
cancel: "Cancel" cancel: "Cancel"
accept: "Allow access." accept: "Allow access."
auth/views/index.vue: auth/views/index.vue:
@ -877,7 +873,6 @@ desktop/views/components/post-form.vue:
posting: "Posting" posting: "Posting"
attach-media-from-local: "Attach media from your device" attach-media-from-local: "Attach media from your device"
attach-media-from-drive: "Attach media from your Drive" attach-media-from-drive: "Attach media from your Drive"
attach-cancel: "Cancel attachment"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "Create a poll" create-poll: "Create a poll"
text-remain: "{} characters remaining" text-remain: "{} characters remaining"
@ -970,6 +965,10 @@ common/views/components/password-settings.vue:
not-match: "The new passwords do not match" not-match: "The new passwords do not match"
changed: "Password changed" changed: "Password changed"
failed: "Failed to change password" failed: "Failed to change password"
common/views/components/post-form-attaches.vue:
attach-cancel: "Remove Attachment"
mark-as-sensitive: "Mark as 'sensitive'"
unmark-as-sensitive: "Unmark as 'sensitive'"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "This post is private" private: "This post is private"
deleted: "This post has been deleted" deleted: "This post has been deleted"
@ -980,8 +979,6 @@ desktop/views/components/settings.tags.vue:
query: "Query (optional)" query: "Query (optional)"
add: "Add" add: "Add"
save: "Save" save: "Save"
desktop/views/components/taskmanager.vue:
title: "Task Manager"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Home" home: "Home"
local: "Local" local: "Local"
@ -1115,11 +1112,6 @@ admin/views/instance.vue:
save: "Save" save: "Save"
saved: "Saved" saved: "Saved"
user-recommendation-config: "Recommended users" user-recommendation-config: "Recommended users"
enable-external-user-recommendation: "Enable external user recommendations"
external-user-recommendation-engine: "Engine"
external-user-recommendation-engine-desc: "Example: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
external-user-recommendation-timeout: "Timeout"
external-user-recommendation-timeout-desc: "Number of milliseconds (ex. 300,000)"
email-config: "Email server settings" email-config: "Email server settings"
email-config-info: "Used to confirm email and password reset etc." email-config-info: "Used to confirm email and password reset etc."
enable-email: "Enable email delivery" enable-email: "Enable email delivery"
@ -1632,12 +1624,3 @@ dev/views/new-app.vue:
authority: "Permissions" authority: "Permissions"
authority-desc: "Only the functions requested here can be accessed via the API." authority-desc: "Only the functions requested here can be accessed via the API."
authority-warning: "You can change it even after creating the application, but if you give different permissions, all user keys associated at that time will be invalidated." authority-warning: "You can change it even after creating the application, but if you give different permissions, all user keys associated at that time will be invalidated."
account-read: "View account information."
account-write: "Modify account information."
note-write: "Post."
reaction-write: "Add or remove reactions."
following-write: "Follow and unfollow."
drive-read: "Read the drive."
drive-write: "Upload/delete files in the drive."
notification-read: "Read your notifications."
notification-write: "Manage your notifications."

View File

@ -169,15 +169,6 @@ common:
you: "Tú" you: "Tú"
auth/views/form.vue: auth/views/form.vue:
permission-ask: "La aplicación requiere los siguientes permisos:" permission-ask: "La aplicación requiere los siguientes permisos:"
account-read: "Viendo información de la cuenta:"
account-write: "Modificar información de la cuenta:"
note-write: "Publicación."
like-write: "Para reaccionar a las publicaciones."
following-write: "Seguir o dejar de seguir"
drive-read: "Leer tu unidad."
drive-write: "Cargar o borrar archivos de tu unidad."
notification-read: "Leer tus notificaciones."
notification-write: "Administrar tus notificaciones."
cancel: "Cancelar" cancel: "Cancelar"
accept: "Garantizar acceso." accept: "Garantizar acceso."
auth/views/index.vue: auth/views/index.vue:
@ -631,7 +622,6 @@ desktop/views/components/post-form.vue:
posting: "Publicando" posting: "Publicando"
attach-media-from-local: "Agregar medios de tu dispositivo" attach-media-from-local: "Agregar medios de tu dispositivo"
attach-media-from-drive: "Adjunta multimedia desde tu Disco" attach-media-from-drive: "Adjunta multimedia desde tu Disco"
attach-cancel: "Quitar el archivo adjunto"
create-poll: "Crea una encuesta" create-poll: "Crea una encuesta"
text-remain: "quedan {} caracteres" text-remain: "quedan {} caracteres"
recent-tags: "Reciente" recent-tags: "Reciente"
@ -697,6 +687,9 @@ common/views/components/mute-and-block.vue:
mute: "Silenciar" mute: "Silenciar"
block: "Bloquear" block: "Bloquear"
save: "Guardar" save: "Guardar"
common/views/components/post-form-attaches.vue:
mark-as-sensitive: "Marcar como 'sensible'"
unmark-as-sensitive: "Desmarcar como 'sensible'"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "Esta publicación es privada" private: "Esta publicación es privada"
deleted: "Esta publicación ha sido removida" deleted: "Esta publicación ha sido removida"
@ -708,7 +701,6 @@ desktop/views/components/settings.tags.vue:
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Inicio" home: "Inicio"
local: "Local" local: "Local"
hybrid: "Social"
global: "Global" global: "Global"
list: "Listas" list: "Listas"
hashtag: "Hashtags" hashtag: "Hashtags"
@ -768,7 +760,6 @@ admin/views/instance.vue:
recaptcha-secret-key: "clave secreta reCAPTCHA" recaptcha-secret-key: "clave secreta reCAPTCHA"
invite: "Invitar" invite: "Invitar"
save: "Guardar" save: "Guardar"
external-user-recommendation-timeout: "Tiempo de espera"
email: "Correo electrónico" email: "Correo electrónico"
smtp-host: "Host SMTP" smtp-host: "Host SMTP"
smtp-port: "Puerto SMTP" smtp-port: "Puerto SMTP"
@ -898,7 +889,6 @@ mobile/views/pages/user-lists.vue:
mobile/views/pages/home.vue: mobile/views/pages/home.vue:
home: "Inicio" home: "Inicio"
local: "Local" local: "Local"
hybrid: "Social"
global: "Global" global: "Global"
mobile/views/pages/widgets.vue: mobile/views/pages/widgets.vue:
dashboard: "Panel de control" dashboard: "Panel de control"
@ -921,7 +911,6 @@ mobile/views/pages/user/home.photos.vue:
deck: deck:
home: "Inicio" home: "Inicio"
local: "Local" local: "Local"
hybrid: "Social"
hashtag: "Etiquetas" hashtag: "Etiquetas"
global: "Global" global: "Global"
notifications: "Notificaciones" notifications: "Notificaciones"
@ -929,12 +918,3 @@ deck:
rename: "Renombrar" rename: "Renombrar"
deck/deck.user-column.vue: deck/deck.user-column.vue:
activity: "Actividad" activity: "Actividad"
dev/views/new-app.vue:
account-read: "Viendo información de la cuenta:"
account-write: "Modificar información de la cuenta:"
note-write: "Publicación."
following-write: "Seguir o dejar de seguir"
drive-read: "Leer tu unidad."
drive-write: "Cargar o borrar archivos de tu unidad."
notification-read: "Leer tus notificaciones."
notification-write: "Administrar tus notificaciones."

View File

@ -67,6 +67,11 @@ common:
following: "Suit" following: "Suit"
followers: "Abonné·e·s" followers: "Abonné·e·s"
favorites: "Mettre cette note en favoris" favorites: "Mettre cette note en favoris"
permissions:
'read:account': "Afficher les informations du compte"
'write:account': "Mettre à jour les informations de votre compte"
'read:drive': "Parcourir le Drive"
'write:drive': "Écrire sur le Drive"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil." follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil."
explore: "Trouver des utilisateurs" explore: "Trouver des utilisateurs"
@ -121,6 +126,7 @@ common:
notification: "Notifications" notification: "Notifications"
apps: "Applications" apps: "Applications"
tags: "Hashtags" tags: "Hashtags"
mute-and-block: "Silencer / Bloquer"
blocking: "En cours blocage" blocking: "En cours blocage"
security: "Sécurité" security: "Sécurité"
signin: "Historique des connexions" signin: "Historique des connexions"
@ -134,6 +140,16 @@ common:
remember-note-visibility: "Se souvenir du mode de visibilité de la publication" remember-note-visibility: "Se souvenir du mode de visibilité de la publication"
web-search-engine: "Moteur de recherche Web" web-search-engine: "Moteur de recherche Web"
web-search-engine-desc: "Exemple: https://www.google.com/?#q={{query}}" web-search-engine-desc: "Exemple: https://www.google.com/?#q={{query}}"
keep-cw: "Maintenir l'avertissement de contenu"
i-like-sushi: "Je préfère les sushis plutôt que le pudding"
show-reversi-board-labels: "Afficher les étiquettes des lignes et colonnes dans Reversi"
use-avatar-reversi-stones: "Utiliser lavatar comme pion dans Reversi"
disable-animated-mfm: "Désactiver les textes animés dans les publications"
disable-showing-animated-images: "Désactiver l'animation des images"
suggest-recent-hashtags: "Afficher les hashtags populaires dans le champs de saisie"
always-show-nsfw: "Toujours afficher les contenus sensibles"
always-mark-nsfw: "Toujours marquer les notes ayant des médias comme sensibles"
show-full-acct: "Afficher ladresse complète de lutilisateur"
show-via: "Afficher via" show-via: "Afficher via"
reduce-motion: "Réduire les animations dans linterface utilisateur" reduce-motion: "Réduire les animations dans linterface utilisateur"
this-setting-is-this-device-only: "Uniquement sur cet appareil" this-setting-is-this-device-only: "Uniquement sur cet appareil"
@ -143,25 +159,69 @@ common:
line-width-normal: "Normale" line-width-normal: "Normale"
line-width-thick: "Épaisse" line-width-thick: "Épaisse"
font-size: "Taille du texte" font-size: "Taille du texte"
font-size-x-small: "Très petit"
font-size-small: "Petite"
font-size-medium: "Normale" font-size-medium: "Normale"
font-size-large: "Grande"
font-size-x-large: "Large" font-size-x-large: "Large"
deck-column-align: "Alignement des colonnes du Deck"
deck-column-align-center: "Centrer" deck-column-align-center: "Centrer"
deck-column-align-left: "À gauche" deck-column-align-left: "À gauche"
deck-column-align-flexible: "Flexible" deck-column-align-flexible: "Flexible"
deck-column-width: "Largeur des colonnes du Deck" deck-column-width: "Largeur des colonnes du Deck"
deck-column-width-narrow: "Étroite"
deck-column-width-narrower: "Légèrement étroite"
deck-column-width-normal: "Normale" deck-column-width-normal: "Normale"
deck-column-width-wider: "Légèrement large"
deck-column-width-wide: "Large"
use-shadow: "Utiliser les ombres dans l'interface utilisateur"
rounded-corners: "Coins arrondis de l'interface utilisateur"
circle-icons: "Utiliser des icônes circulaires"
contrasted-acct: "Ajouter du contraste au nom de lutilisateur"
wallpaper: "Image du fond d'écran"
choose-wallpaper: "Sélectionner un fond d'écran"
delete-wallpaper: "Supprimer le fond d'écran"
post-form-on-timeline: "Afficher le champs de saisie en haut du fil"
show-clock-on-header: "Afficher l'horloge sur le coté supérieur droit"
timeline: "Fil dactualité" timeline: "Fil dactualité"
show-my-renotes: "Afficher mes republications dans le fil"
remain-deleted-note: "Continuer à afficher les notes supprimées"
sound: "Son"
enable-sounds: "Activer les sons"
enable-sounds-desc: "Jouer un son lorsque vous recevez un message/publication. Ce paramètre est sauvegardé dans le navigateur."
volume: "Volume"
test: "Test"
update: "Mise à jour de Misskey"
version: "Version actuelle :"
latest-version: "Dernière version :"
update-checking: "Recherche de mises à jour"
do-update: "Rechercher des mises à jour"
update-settings: "Paramètres avancés"
no-updates: "Aucune mise à jour disponible"
no-updates-desc: "Votre Misskey est à jour."
update-available: "Nouvelle version disponible !"
update-available-desc: "Les mises à jour seront appliquées une fois la page est rechargée."
advanced-settings: "Paramètres avancés"
debug-mode: "Activer le mode débogage"
debug-mode-desc: "Ce paramètre est stocké dans le navigateur."
navbar-position: "Position de la barre de navigation"
navbar-position-top: "en haut" navbar-position-top: "en haut"
navbar-position-left: "À gauche" navbar-position-left: "À gauche"
navbar-position-right: "à droite" navbar-position-right: "à droite"
i-am-under-limited-internet: "J'ai un accès Internet limité"
post-style: "Style d'affichage des notes"
post-style-standard: "Standard" post-style-standard: "Standard"
post-style-smart: "Intelligent" post-style-smart: "Intelligent"
notification-position: "Afficher les notifications" notification-position: "Afficher les notifications"
notification-position-bottom: "en bas" notification-position-bottom: "en bas"
notification-position-top: "en haut" notification-position-top: "en haut"
disable-via-mobile: "Enlever la mention publié via 'mobile'"
load-raw-images: "Afficher les photos jointes dans leur qualité originale"
load-remote-media: "Afficher les médias depuis le serveur distant"
search: "Recherche" search: "Recherche"
delete: "Supprimer" delete: "Supprimer"
loading: "Chargement en cours…" loading: "Chargement en cours…"
ok: "Confirmer"
cancel: "Quitter" cancel: "Quitter"
update-available-title: "Mise à jour disponible" update-available-title: "Mise à jour disponible"
update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour." update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour."
@ -176,7 +236,7 @@ common:
view-on-remote: " Consulter le profil complet" view-on-remote: " Consulter le profil complet"
renoted-by: "Renoté par {user}" renoted-by: "Renoté par {user}"
no-notes: "Sans aucune note" no-notes: "Sans aucune note"
turn-on-darkmode: "Basculer vers le mode nuit" turn-on-darkmode: "Mode nuit"
turn-off-darkmode: "Mode jour" turn-off-darkmode: "Mode jour"
error: error:
title: "Une erreur est survenue" title: "Une erreur est survenue"
@ -220,15 +280,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "Désirez-vous autoriser <i>{name}</i> à avoir accès à votre compte?" share-access: "Désirez-vous autoriser <i>{name}</i> à avoir accès à votre compte?"
permission-ask: "Cette application nécessite les autorisations suivantes :" permission-ask: "Cette application nécessite les autorisations suivantes :"
account-read: "Afficher les informations du compte."
account-write: "Modifications des informations du compte."
note-write: "Publier."
like-write: "Réagir aux publications."
following-write: "Suivre des comptes et se désabonner."
drive-read: "Lire votre Drive"
drive-write: "Téléverser/supprimer des fichiers dans votre Drive."
notification-read: "Lire vos notifications."
notification-write: "Gérer vos notifications."
cancel: "Annuler" cancel: "Annuler"
accept: "Autoriser laccès" accept: "Autoriser laccès"
auth/views/index.vue: auth/views/index.vue:
@ -250,6 +301,8 @@ common/views/pages/explore.vue:
federated: "Du Fédiverse" federated: "Du Fédiverse"
explore: "Explorer {host}" explore: "Explorer {host}"
users-info: "Actuellement, {users} utilisateurs se sont inscrit ici" users-info: "Actuellement, {users} utilisateurs se sont inscrit ici"
common/views/components/url-preview.vue:
disable-player: "Fermer le lecteur"
common/views/components/user-list.vue: common/views/components/user-list.vue:
no-users: "Il n'y a aucun utilisateur" no-users: "Il n'y a aucun utilisateur"
common/views/components/games/reversi/reversi.vue: common/views/components/games/reversi/reversi.vue:
@ -424,19 +477,32 @@ common/views/components/user-menu.vue:
common/views/components/poll.vue: common/views/components/poll.vue:
vote-to: "Voter pour '{}'" vote-to: "Voter pour '{}'"
vote-count: "{} votes" vote-count: "{} votes"
total-votes: "{} Total des votes"
vote: "Vote" vote: "Vote"
show-result: "Montrer les résultats" show-result: "Montrer les résultats"
voted: "Voté" voted: "Voté"
closed: "Terminé"
remaining-days: "{d} jours, {h} heures restantes"
remaining-hours: "{h} heures et {m} minutes restantes"
remaining-minutes: "{m} minutes et {s} secondes restantes"
remaining-seconds: "{s} secondes restantes"
common/views/components/poll-editor.vue: common/views/components/poll-editor.vue:
no-only-one-choice: "Vous devez saisir au moins deux choix." no-only-one-choice: "Vous devez saisir au moins deux choix."
choice-n: "Choix {}" choice-n: "Choix {}"
remove: "Supprimer ce choix" remove: "Supprimer ce choix"
add: "+ Ajouter un choix" add: "+ Ajouter un choix"
destroy: "Annuler ce sondage" destroy: "Annuler ce sondage"
expiration: "Valide jusqu'à"
infinite: "Illimité"
at: "Choisir une date et une durée"
no-more: "Vous ne pouvez pas en ajouter davantage"
deadline-date: "Date déchéance"
deadline-time: "Durée"
interval: "Durée" interval: "Durée"
unit: "Unité" unit: "Unité"
second: "secondes" second: "secondes"
minute: "Minutes" minute: "Minutes"
hour: "Heures"
day: "D" day: "D"
common/views/components/reaction-picker.vue: common/views/components/reaction-picker.vue:
choose-reaction: "Choisissez votre réaction" choose-reaction: "Choisissez votre réaction"
@ -568,6 +634,7 @@ common/views/components/profile-editor.vue:
email-not-verified: "Adresse de courriel nest pas confirmée. Veuillez vérifier votre boite de réception." email-not-verified: "Adresse de courriel nest pas confirmée. Veuillez vérifier votre boite de réception."
export: "Exporter" export: "Exporter"
import: "Importer" import: "Importer"
export-and-import: "Exportation et importation"
export-targets: export-targets:
all-notes: "Toutes les notes publiées" all-notes: "Toutes les notes publiées"
following-list: "Liste des abonnements" following-list: "Liste des abonnements"
@ -793,7 +860,6 @@ desktop/views/components/post-form.vue:
posting: "Publication …" posting: "Publication …"
attach-media-from-local: "Joindre un média depuis votre appareil" attach-media-from-local: "Joindre un média depuis votre appareil"
attach-media-from-drive: "Joindre un média depuis votre Drive" attach-media-from-drive: "Joindre un média depuis votre Drive"
attach-cancel: "Annuler le fichier attaché"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "Créer un sondage" create-poll: "Créer un sondage"
text-remain: "{} caractères restants" text-remain: "{} caractères restants"
@ -886,6 +952,9 @@ common/views/components/password-settings.vue:
not-match: "Les nouveaux mots de passe ne sont pas identiques" not-match: "Les nouveaux mots de passe ne sont pas identiques"
changed: "Mot de passe modifié avec succès" changed: "Mot de passe modifié avec succès"
failed: "Échec lors de la modification du mot de passe" failed: "Échec lors de la modification du mot de passe"
common/views/components/post-form-attaches.vue:
mark-as-sensitive: "Marquer comme sensible"
unmark-as-sensitive: "Ne pas marquer comme sensible"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "cette publication est privée" private: "cette publication est privée"
deleted: "cette publication a été supprimée" deleted: "cette publication a été supprimée"
@ -896,8 +965,6 @@ desktop/views/components/settings.tags.vue:
query: "Requête (optionnelle)" query: "Requête (optionnelle)"
add: "Ajouter" add: "Ajouter"
save: "Enregistrer" save: "Enregistrer"
desktop/views/components/taskmanager.vue:
title: "Gestionnaire de tâches"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Accueil" home: "Accueil"
local: "Local" local: "Local"
@ -958,6 +1025,7 @@ admin/views/index.vue:
hashtags: "Hashtags" hashtags: "Hashtags"
abuse: "Abus" abuse: "Abus"
queue: "File dattente" queue: "File dattente"
logs: "Journaux"
back-to-misskey: "Retour vers Misskey" back-to-misskey: "Retour vers Misskey"
admin/views/dashboard.vue: admin/views/dashboard.vue:
dashboard: "Tableau de bord" dashboard: "Tableau de bord"
@ -1026,11 +1094,6 @@ admin/views/instance.vue:
save: "Sauvegarder" save: "Sauvegarder"
saved: "Enregistré" saved: "Enregistré"
user-recommendation-config: "Utilisateurs" user-recommendation-config: "Utilisateurs"
enable-external-user-recommendation: "Activer la recommandation des utilisateurs distants"
external-user-recommendation-engine: "Moteur"
external-user-recommendation-engine-desc: "Exemple: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
external-user-recommendation-timeout: "Délai dexpiration"
external-user-recommendation-timeout-desc: "En millisecondes (par exemple : 300000)"
email-config: "Paramètres du serveur de messagerie" email-config: "Paramètres du serveur de messagerie"
email-config-info: "Utilisé pour confirmer votre adresse de courrier électronique et la réinitialisation de votre mot de passe." email-config-info: "Utilisé pour confirmer votre adresse de courrier électronique et la réinitialisation de votre mot de passe."
enable-email: "Activation de la distribution du courrier" enable-email: "Activation de la distribution du courrier"
@ -1199,6 +1262,7 @@ admin/views/federation.vue:
marked-as-closed: "Marquées comme fermées" marked-as-closed: "Marquées comme fermées"
lookup: "Recherche" lookup: "Recherche"
instances: "Fédérées" instances: "Fédérées"
instance-not-registered: "Linstance na pas encore été découverte"
sort: "Trier par" sort: "Trier par"
sorts: sorts:
caughtAtAsc: "Date dinscription (Ascendant)" caughtAtAsc: "Date dinscription (Ascendant)"
@ -1206,6 +1270,7 @@ admin/views/federation.vue:
lastCommunicatedAtAsc: "La date et l'heure des interactions plus anciennes" lastCommunicatedAtAsc: "La date et l'heure des interactions plus anciennes"
lastCommunicatedAtDesc: "La date et l'heure des nouvelles interactions" lastCommunicatedAtDesc: "La date et l'heure des nouvelles interactions"
notesDesc: "Description des notes" notesDesc: "Description des notes"
usersAsc: "Peu d'abonnés"
followingAsc: "Les moins suivies" followingAsc: "Les moins suivies"
followingDesc: "Ayant le plus d'abonné·e·s" followingDesc: "Ayant le plus d'abonné·e·s"
followersAsc: "Ayant le moins d'abonné·e·s" followersAsc: "Ayant le moins d'abonné·e·s"
@ -1530,12 +1595,3 @@ dev/views/new-app.vue:
authority: "Autorisations " authority: "Autorisations "
authority-desc: "Sont accessibles via lAPI, uniquement les fonctionnalités demandées ici." authority-desc: "Sont accessibles via lAPI, uniquement les fonctionnalités demandées ici."
authority-warning: "Vous pouvez le changer même après avoir créé l'application, mais si vous attribuez une nouvelle permission, toutes les clés utilisateur associées seront dès lors invalides." authority-warning: "Vous pouvez le changer même après avoir créé l'application, mais si vous attribuez une nouvelle permission, toutes les clés utilisateur associées seront dès lors invalides."
account-read: "Afficher les informations du compte"
account-write: "Modifications des informations du compte"
note-write: "Publications."
reaction-write: "Ajout et suppression de réactions."
following-write: "Sabonner et se désabonner."
drive-read: "Lecture du Drive."
drive-write: "Téléversement/suppression des fichiers de votre Lecteur."
notification-read: "Lire vos notifications."
notification-write: "Gestion de vos notifications."

View File

@ -73,6 +73,12 @@ common:
followers: "フォロワー" followers: "フォロワー"
favorites: "お気に入り" favorites: "お気に入り"
permissions:
'read:account': "アカウントの情報を見る"
'write:account': "アカウントの情報を変更する"
'read:drive': "ドライブを見る"
'write:drive': "ドライブを操作する"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。" follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
explore: "ユーザーを探索する" explore: "ユーザーを探索する"
@ -299,15 +305,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "<i>{name}</i>があなたのアカウントにアクセスすることを許可しますか?" share-access: "<i>{name}</i>があなたのアカウントにアクセスすることを許可しますか?"
permission-ask: "このアプリは次の権限を要求しています:" permission-ask: "このアプリは次の権限を要求しています:"
account-read: "アカウントの情報を見る。"
account-write: "アカウントの情報を操作する。"
note-write: "投稿する。"
like-write: "いいねしたりいいね解除する。"
following-write: "フォローしたりフォロー解除する。"
drive-read: "ドライブを見る。"
drive-write: "ドライブを操作する。"
notification-read: "通知を見る。"
notification-write: "通知を操作する。"
cancel: "キャンセル" cancel: "キャンセル"
accept: "アクセスを許可" accept: "アクセスを許可"
@ -967,7 +964,6 @@ desktop/views/components/post-form.vue:
posting: "投稿中" posting: "投稿中"
attach-media-from-local: "PCからメディアを添付" attach-media-from-local: "PCからメディアを添付"
attach-media-from-drive: "ドライブからメディアを添付" attach-media-from-drive: "ドライブからメディアを添付"
attach-cancel: "添付取り消し"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "アンケートを作成" create-poll: "アンケートを作成"
text-remain: "残り{}文字" text-remain: "残り{}文字"
@ -1073,6 +1069,11 @@ common/views/components/password-settings.vue:
changed: "パスワードを変更しました" changed: "パスワードを変更しました"
failed: "パスワード変更に失敗しました" failed: "パスワード変更に失敗しました"
common/views/components/post-form-attaches.vue:
attach-cancel: "添付取り消し"
mark-as-sensitive: "閲覧注意に設定"
unmark-as-sensitive: "閲覧注意を解除"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "この投稿は非公開です" private: "この投稿は非公開です"
deleted: "この投稿は削除されました" deleted: "この投稿は削除されました"
@ -1085,9 +1086,6 @@ desktop/views/components/settings.tags.vue:
add: "追加" add: "追加"
save: "保存" save: "保存"
desktop/views/components/taskmanager.vue:
title: "タスクマネージャ"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "ホーム" home: "ホーム"
local: "ローカル" local: "ローカル"
@ -1238,11 +1236,6 @@ admin/views/instance.vue:
save: "保存" save: "保存"
saved: "保存しました" saved: "保存しました"
user-recommendation-config: "おすすめユーザー" user-recommendation-config: "おすすめユーザー"
enable-external-user-recommendation: "外部ユーザーレコメンデーションを有効にする"
external-user-recommendation-engine: "エンジン"
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
external-user-recommendation-timeout: "タイムアウト"
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
email-config: "メールサーバーの設定" email-config: "メールサーバーの設定"
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。" email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
enable-email: "メール配信を有効にする" enable-email: "メール配信を有効にする"
@ -1823,12 +1816,3 @@ dev/views/new-app.vue:
authority: "権限" authority: "権限"
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。" authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。" authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
account-read: "アカウントの情報を見る。"
account-write: "アカウントの情報を操作する。"
note-write: "投稿する。"
reaction-write: "リアクションしたりリアクションをキャンセルする。"
following-write: "フォローしたりフォロー解除する。"
drive-read: "ドライブを見る。"
drive-write: "ドライブを操作する。"
notification-read: "通知を見る。"
notification-write: "通知を操作する。"

View File

@ -166,15 +166,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "あんたのアカウントに<i>{name}</i>がアクセスしようとしてるで?ええか?" share-access: "あんたのアカウントに<i>{name}</i>がアクセスしようとしてるで?ええか?"
permission-ask: "このアプリは次の権限を要求してんで:" permission-ask: "このアプリは次の権限を要求してんで:"
account-read: "アカウントの情報を見させてもらうで。"
account-write: "アカウントの情報を操作させてもらうで。"
note-write: "投稿させてもらうで。"
like-write: "いいねしたりいいね解除させてもらうで。"
following-write: "フォローしたりフォロー解除させてもらうで。"
drive-read: "ドライブを見させてもらうで。"
drive-write: "ドライブを操作させてもらうで。"
notification-read: "通知を見させてもらうで。"
notification-write: "通知を操作させてもらうで。"
cancel: "やめとくわ" cancel: "やめとくわ"
accept: "アクセスを許可や!" accept: "アクセスを許可や!"
auth/views/index.vue: auth/views/index.vue:
@ -688,7 +679,6 @@ desktop/views/components/post-form.vue:
posting: "投稿中" posting: "投稿中"
attach-media-from-local: "PCからメディア持ってくる" attach-media-from-local: "PCからメディア持ってくる"
attach-media-from-drive: "ドライブからメディア持ってくる" attach-media-from-drive: "ドライブからメディア持ってくる"
attach-cancel: "くっつけるのやめよか"
create-poll: "アンケートを作成" create-poll: "アンケートを作成"
text-remain: "残り{}文字" text-remain: "残り{}文字"
recent-tags: "最近のタグ" recent-tags: "最近のタグ"
@ -779,6 +769,9 @@ common/views/components/password-settings.vue:
enter-new-password-again: "もっぺん入れてや" enter-new-password-again: "もっぺん入れてや"
not-match: "パスワードがおうとらん" not-match: "パスワードがおうとらん"
changed: "パスワード変えたわ" changed: "パスワード変えたわ"
common/views/components/post-form-attaches.vue:
mark-as-sensitive: "見たらあかん感じにしとく"
unmark-as-sensitive: "やっぱ見せたるわ"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "この投稿は見せられへんわ" private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ" deleted: "この投稿なんか無くなってもうたわ"
@ -787,12 +780,9 @@ desktop/views/components/sub-note-content.vue:
desktop/views/components/settings.tags.vue: desktop/views/components/settings.tags.vue:
add: "増やす" add: "増やす"
save: "保存" save: "保存"
desktop/views/components/taskmanager.vue:
title: "タスクマネージャ"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "ホーム" home: "ホーム"
local: "ローカル" local: "ローカル"
hybrid: "ソーシャル"
global: "グローバル" global: "グローバル"
mentions: "あんた宛て" mentions: "あんた宛て"
messages: "ダイレクト投稿" messages: "ダイレクト投稿"
@ -908,11 +898,6 @@ admin/views/instance.vue:
save: "保存" save: "保存"
saved: "保存したで!" saved: "保存したで!"
user-recommendation-config: "このユーザーええで" user-recommendation-config: "このユーザーええで"
enable-external-user-recommendation: "外部ユーザーレコメンデーションを使えるようにする"
external-user-recommendation-engine: "エンジン"
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
external-user-recommendation-timeout: "タイムアウト"
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
email-config: "メールサーバーの設定" email-config: "メールサーバーの設定"
email-config-info: "メールアドレス確認やパスワードリセットの際に使うで。" email-config-info: "メールアドレス確認やパスワードリセットの際に使うで。"
enable-email: "メール配信を有効にする" enable-email: "メール配信を有効にする"
@ -1174,7 +1159,6 @@ mobile/views/pages/following.vue:
mobile/views/pages/home.vue: mobile/views/pages/home.vue:
home: "ホーム" home: "ホーム"
local: "ローカル" local: "ローカル"
hybrid: "ソーシャル"
global: "グローバル" global: "グローバル"
mentions: "あんた宛て" mentions: "あんた宛て"
messages: "ダイレクト投稿" messages: "ダイレクト投稿"
@ -1229,7 +1213,6 @@ deck:
widgets: "ウィジェット" widgets: "ウィジェット"
home: "ホーム" home: "ホーム"
local: "ローカル" local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ" hashtag: "ハッシュタグ"
global: "グローバル" global: "グローバル"
mentions: "あんた宛て" mentions: "あんた宛て"
@ -1298,12 +1281,3 @@ dev/views/new-app.vue:
authority: "権限" authority: "権限"
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな" authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。" authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
account-read: "アカウントの情報見せて"
account-write: "アカウントの情報いじらせて"
note-write: "投稿させて"
reaction-write: "リアクションしたりそれをキャンセルさせて"
following-write: "フォローとかフォロー解除させて"
drive-read: "ドライブ見せて"
drive-write: "ドライブいじらせて"
notification-read: "通知見せて"
notification-write: "通知いじらせて"

View File

@ -69,6 +69,11 @@ common:
following: "팔로우 중" following: "팔로우 중"
followers: "팔로워" followers: "팔로워"
favorites: "즐겨찾기" favorites: "즐겨찾기"
permissions:
'read:account': "계정 정보 보기"
'write:account': "계정 정보 변경"
'read:drive': "드라이브 보기"
'write:drive': "드라이브 수정"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "사용자를 팔로우하면 글이 타임라인에 표시됩니다." follow-users-to-make-your-timeline: "사용자를 팔로우하면 글이 타임라인에 표시됩니다."
explore: "사용자 탐색" explore: "사용자 탐색"
@ -282,15 +287,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "<i>{name}</i>가 당신의 계정에 엑세스하도록 허용하시겠습니까?" share-access: "<i>{name}</i>가 당신의 계정에 엑세스하도록 허용하시겠습니까?"
permission-ask: "이 앱은 다음의 권한을 요청합니다:" permission-ask: "이 앱은 다음의 권한을 요청합니다:"
account-read: "계정 정보 보기."
account-write: "계정 정보의 수정."
note-write: "게시하기."
like-write: "좋아요 하거나 좋아요 해제하기."
following-write: "팔로우하거나 팔로우를 취소하기."
drive-read: "드라이브 보기."
drive-write: "드라이브의 수정."
notification-read: "알림 읽기."
notification-write: "알림 수정하기."
cancel: "취소" cancel: "취소"
accept: "접근 권한 허용" accept: "접근 권한 허용"
auth/views/index.vue: auth/views/index.vue:
@ -877,7 +873,6 @@ desktop/views/components/post-form.vue:
posting: "게시중" posting: "게시중"
attach-media-from-local: "PC에서 미디어 첨부" attach-media-from-local: "PC에서 미디어 첨부"
attach-media-from-drive: "드라이브에서 미디어 첨부" attach-media-from-drive: "드라이브에서 미디어 첨부"
attach-cancel: "첨부 취소"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "투표 만들기" create-poll: "투표 만들기"
text-remain: "{}문자 남음" text-remain: "{}문자 남음"
@ -970,6 +965,9 @@ common/views/components/password-settings.vue:
not-match: "새 비밀번호가 일치하지 않습니다" not-match: "새 비밀번호가 일치하지 않습니다"
changed: "비밀번호를 변경하였습니다" changed: "비밀번호를 변경하였습니다"
failed: "비밀번호 변경을 실패하였습니다." failed: "비밀번호 변경을 실패하였습니다."
common/views/components/post-form-attaches.vue:
mark-as-sensitive: "열람주의로 설정"
unmark-as-sensitive: "열람주의 해제"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "이 글은 비공개입니다" private: "이 글은 비공개입니다"
deleted: "이 글은 삭제되었습니다" deleted: "이 글은 삭제되었습니다"
@ -980,8 +978,6 @@ desktop/views/components/settings.tags.vue:
query: "쿼리 (생략 가능)" query: "쿼리 (생략 가능)"
add: "추가" add: "추가"
save: "저장" save: "저장"
desktop/views/components/taskmanager.vue:
title: "작업 관리자"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "홈" home: "홈"
local: "로컬" local: "로컬"
@ -1115,11 +1111,6 @@ admin/views/instance.vue:
save: "저장" save: "저장"
saved: "저장하였습니다" saved: "저장하였습니다"
user-recommendation-config: "추천 사용자" user-recommendation-config: "추천 사용자"
enable-external-user-recommendation: "외부 사용자 추천 활성화"
external-user-recommendation-engine: "엔진"
external-user-recommendation-engine-desc: "예: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
external-user-recommendation-timeout: "타임 아웃"
external-user-recommendation-timeout-desc: "밀리초 (예: 300000)"
email-config: "메일 서버 설정" email-config: "메일 서버 설정"
email-config-info: "메일 주소 확인 혹은 비밀번호 재설정에 사용 됩니다." email-config-info: "메일 주소 확인 혹은 비밀번호 재설정에 사용 됩니다."
enable-email: "메일 발신 활성화" enable-email: "메일 발신 활성화"
@ -1632,12 +1623,3 @@ dev/views/new-app.vue:
authority: "권한" authority: "권한"
authority-desc: "이곳에서 요청한 권한에 한정하여 API로 액세스할 수 있습니다." authority-desc: "이곳에서 요청한 권한에 한정하여 API로 액세스할 수 있습니다."
authority-warning: "앱을 생성한 뒤에도 변경할 수 있지만, 새로운 권한을 설정하는 경우 그 시점부터 예전에 발급받았던 유저 키는 모두 무효화됩니다." authority-warning: "앱을 생성한 뒤에도 변경할 수 있지만, 새로운 권한을 설정하는 경우 그 시점부터 예전에 발급받았던 유저 키는 모두 무효화됩니다."
account-read: "계정 정보 보기."
account-write: "계정 정보 편집."
note-write: "글 쓰기."
reaction-write: "리액션을 하거나 리액션을 취소할 수 있음."
following-write: "팔로우하거나 팔로우 해제하기."
drive-read: "드라이브 보기."
drive-write: "드라이브를 조작."
notification-read: "알림 보기."
notification-write: "알림 조작."

View File

@ -334,7 +334,6 @@ desktop/views/components/post-form.vue:
posting: "Bezig met plaatsen" posting: "Bezig met plaatsen"
attach-media-from-local: "Media bijvoegen van je computer" attach-media-from-local: "Media bijvoegen van je computer"
attach-media-from-drive: "Media bijvoegen uit je Drive" attach-media-from-drive: "Media bijvoegen uit je Drive"
attach-cancel: "Bijlage annuleren"
create-poll: "Peiling creëren" create-poll: "Peiling creëren"
text-remain: "{} resterende tekens" text-remain: "{} resterende tekens"
desktop/views/components/post-form-window.vue: desktop/views/components/post-form-window.vue:
@ -384,8 +383,6 @@ desktop/views/components/sub-note-content.vue:
poll: "Peilingen" poll: "Peilingen"
desktop/views/components/settings.tags.vue: desktop/views/components/settings.tags.vue:
add: "Toevoegen" add: "Toevoegen"
desktop/views/components/taskmanager.vue:
title: "Taakbeheer"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Startpagina" home: "Startpagina"
local: "Lokaal" local: "Lokaal"

View File

@ -23,6 +23,7 @@ common:
enter-password: "Wprowadź Hasło" enter-password: "Wprowadź Hasło"
2fa: "Uwierzytelnienie dwuetapowe" 2fa: "Uwierzytelnienie dwuetapowe"
customize-home: "Dostosuj stronę główną" customize-home: "Dostosuj stronę główną"
featured-notes: "Wyróżnienia"
dark-mode: "Tryb ciemny" dark-mode: "Tryb ciemny"
signin: "Zaloguj się" signin: "Zaloguj się"
signup: "Rejestracja" signup: "Rejestracja"
@ -61,6 +62,8 @@ common:
following: "Śledzisz" following: "Śledzisz"
followers: "Śledzący" followers: "Śledzący"
favorites: "Moje ulubione" favorites: "Moje ulubione"
permissions:
'read:drive': "Wyświetl dysk"
empty-timeline-info: empty-timeline-info:
explore: "Poznaj" explore: "Poznaj"
weekday-short: weekday-short:
@ -201,15 +204,6 @@ common:
you: "Ty" you: "Ty"
auth/views/form.vue: auth/views/form.vue:
permission-ask: "Ta aplikacja wymaga następujących uprawnień:" permission-ask: "Ta aplikacja wymaga następujących uprawnień:"
account-read: "Wyświetlanie informacji o koncie:"
account-write: "Modyfikowanie informacji o koncie:"
note-write: "Publikacja."
like-write: "Reagowanie na wpisy."
following-write: "Śledzenie i cofanie śledzenia."
drive-read: "Odczytywanie Twojego dysku."
drive-write: "Wysyłanie i usuwanie plików na Twoim dysku."
notification-read: "Odczytywanie Twoich powiadomień."
notification-write: "Zarządzanie Twoimi powiadomieniami."
cancel: "Anuluj" cancel: "Anuluj"
accept: "Przyznaj dostęp." accept: "Przyznaj dostęp."
auth/views/index.vue: auth/views/index.vue:
@ -715,7 +709,6 @@ desktop/views/components/post-form.vue:
posting: "Wysyłanie" posting: "Wysyłanie"
attach-media-from-local: "Załącz zawartość multimedialną z komputera" attach-media-from-local: "Załącz zawartość multimedialną z komputera"
attach-media-from-drive: "Załącz zawartość multimedialną z dysku" attach-media-from-drive: "Załącz zawartość multimedialną z dysku"
attach-cancel: "Usuń załącznik"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "Utwórz ankietę" create-poll: "Utwórz ankietę"
text-remain: "pozostałe znaki: {}" text-remain: "pozostałe znaki: {}"
@ -795,6 +788,9 @@ common/views/components/password-settings.vue:
enter-current-password: "Wprowadź obecne hasło" enter-current-password: "Wprowadź obecne hasło"
enter-new-password: "Wprowadź nowe hasło" enter-new-password: "Wprowadź nowe hasło"
enter-new-password-again: "Wprowadź ponownie nowe hasło" enter-new-password-again: "Wprowadź ponownie nowe hasło"
common/views/components/post-form-attaches.vue:
mark-as-sensitive: "Oznacz jako zawartość wrażliwą"
unmark-as-sensitive: "Cofnij oznaczenie jako zawartość wrażliwą"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "ten wpis jest prywatny" private: "ten wpis jest prywatny"
deleted: "ten wpis został usunięty" deleted: "ten wpis został usunięty"
@ -805,12 +801,9 @@ desktop/views/components/settings.tags.vue:
query: "Zapytanie (opcjonalne)" query: "Zapytanie (opcjonalne)"
add: "Dodaj" add: "Dodaj"
save: "Zapisz" save: "Zapisz"
desktop/views/components/taskmanager.vue:
title: "Menedżer zadań"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "Strona główna" home: "Strona główna"
local: "Lokalne" local: "Lokalne"
hybrid: "Społeczność"
global: "Globalne" global: "Globalne"
mentions: "Wspomnienia" mentions: "Wspomnienia"
messages: "Bezpośrednie wpisy" messages: "Bezpośrednie wpisy"
@ -882,8 +875,6 @@ admin/views/instance.vue:
save: "Zapisz" save: "Zapisz"
saved: "Zapisano" saved: "Zapisano"
user-recommendation-config: "Polecani użytkownicy" user-recommendation-config: "Polecani użytkownicy"
external-user-recommendation-engine: "Silnik"
external-user-recommendation-engine-desc: "Np: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
email: "Adres e-mail" email: "Adres e-mail"
admin/views/charts.vue: admin/views/charts.vue:
notes: "Wpisy" notes: "Wpisy"
@ -1119,7 +1110,6 @@ mobile/views/pages/signup.vue:
mobile/views/pages/home.vue: mobile/views/pages/home.vue:
home: "Strona główna" home: "Strona główna"
local: "Lokalne" local: "Lokalne"
hybrid: "Społeczność"
global: "Globalne" global: "Globalne"
mentions: "Wspomnienia" mentions: "Wspomnienia"
messages: "Bezpośrednie wpisy" messages: "Bezpośrednie wpisy"
@ -1169,7 +1159,6 @@ deck:
widgets: "Widżety" widgets: "Widżety"
home: "Strona główna" home: "Strona główna"
local: "Lokalne" local: "Lokalne"
hybrid: "Społeczność"
hashtag: "Hashtag" hashtag: "Hashtag"
global: "Globalne" global: "Globalne"
mentions: "Wspomnienia" mentions: "Wspomnienia"
@ -1224,11 +1213,3 @@ dev/views/apps.vue:
dev/views/new-app.vue: dev/views/new-app.vue:
app-name: "Nazwa Aplikacji" app-name: "Nazwa Aplikacji"
authority: "Uprawnienia" authority: "Uprawnienia"
account-read: "Wyświetlanie informacji o koncie:"
account-write: "Modyfikowanie informacji o koncie:"
note-write: "Publikacja."
following-write: "Śledzenie i cofanie śledzenia."
drive-read: "Odczytywanie Twojego dysku."
drive-write: "Wysyłanie i usuwanie plików na Twoim dysku."
notification-read: "Odczytywanie Twoich powiadomień."
notification-write: "Zarządzanie Twoimi powiadomieniami."

View File

@ -119,9 +119,6 @@ common:
you: "Você" you: "Você"
auth/views/form.vue: auth/views/form.vue:
permission-ask: "Este aplicativo precisa das seguintes permissões:" permission-ask: "Este aplicativo precisa das seguintes permissões:"
account-read: "Ver informações da conta."
account-write: "Modificar informações da conta."
note-write: "Publicar"
cancel: "Cancelar" cancel: "Cancelar"
accept: "Permitir acesso" accept: "Permitir acesso"
auth/views/index.vue: auth/views/index.vue:
@ -292,7 +289,3 @@ docs:
description: "Descrição" description: "Descrição"
dev/views/index.vue: dev/views/index.vue:
manage-apps: "Gerenciar aplicativos" manage-apps: "Gerenciar aplicativos"
dev/views/new-app.vue:
account-read: "Ver informações da conta."
account-write: "Modificar informações da conta."
note-write: "Publicar"

View File

@ -69,6 +69,11 @@ common:
following: "正在关注" following: "正在关注"
followers: "关注者" followers: "关注者"
favorites: "最爱" favorites: "最爱"
permissions:
'read:account': "查看账户信息"
'write:account': "更改我的帐户信息"
'read:drive': "查看网盘"
'write:drive': "管理网盘文件"
empty-timeline-info: empty-timeline-info:
follow-users-to-make-your-timeline: "关注其他用户时,帖子将显示在时间线中。" follow-users-to-make-your-timeline: "关注其他用户时,帖子将显示在时间线中。"
explore: "查找用户" explore: "查找用户"
@ -282,15 +287,6 @@ common:
auth/views/form.vue: auth/views/form.vue:
share-access: "您要允许<i>{name}</i>来访问您的账户吗?" share-access: "您要允许<i>{name}</i>来访问您的账户吗?"
permission-ask: "这个应用程序需要以下权限:" permission-ask: "这个应用程序需要以下权限:"
account-read: "查看账户信息"
account-write: "修改账户信息"
note-write: "投稿。"
like-write: "点赞或取消赞。"
following-write: "关注或取消关注。"
drive-read: "查看您的网盘"
drive-write: "管理网盘文件。"
notification-read: "查看通知。"
notification-write: "管理通知。"
cancel: "取消" cancel: "取消"
accept: "允许访问。" accept: "允许访问。"
auth/views/index.vue: auth/views/index.vue:
@ -877,7 +873,6 @@ desktop/views/components/post-form.vue:
posting: "发送中" posting: "发送中"
attach-media-from-local: "从设备中添加媒体文件" attach-media-from-local: "从设备中添加媒体文件"
attach-media-from-drive: "从网盘中添加媒体文件" attach-media-from-drive: "从网盘中添加媒体文件"
attach-cancel: "删除附件"
insert-a-kao: "v('ω')v" insert-a-kao: "v('ω')v"
create-poll: "创建一个投票" create-poll: "创建一个投票"
text-remain: "还剩{}字" text-remain: "还剩{}字"
@ -970,6 +965,9 @@ common/views/components/password-settings.vue:
not-match: "新密码不匹配" not-match: "新密码不匹配"
changed: "密码已更改" changed: "密码已更改"
failed: "更改密码失败" failed: "更改密码失败"
common/views/components/post-form-attaches.vue:
mark-as-sensitive: "标记为“敏感”"
unmark-as-sensitive: "取消标记为“敏感”"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "这个帖子是私密的" private: "这个帖子是私密的"
deleted: "帖子已删除" deleted: "帖子已删除"
@ -980,8 +978,6 @@ desktop/views/components/settings.tags.vue:
query: "查询 (可选)" query: "查询 (可选)"
add: "添加" add: "添加"
save: "保存" save: "保存"
desktop/views/components/taskmanager.vue:
title: "任务管理器"
desktop/views/components/timeline.vue: desktop/views/components/timeline.vue:
home: "首页" home: "首页"
local: "本地" local: "本地"
@ -1115,11 +1111,6 @@ admin/views/instance.vue:
save: "保存" save: "保存"
saved: "保存完毕" saved: "保存完毕"
user-recommendation-config: "推荐用户" user-recommendation-config: "推荐用户"
enable-external-user-recommendation: "启用外部用户推荐"
external-user-recommendation-engine: "引擎"
external-user-recommendation-engine-desc: "例如: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
external-user-recommendation-timeout: "超时"
external-user-recommendation-timeout-desc: "单位为毫秒 (例如300000)"
email-config: "电子邮件服务器设置" email-config: "电子邮件服务器设置"
email-config-info: "用于确认电子邮件和密码重置等。" email-config-info: "用于确认电子邮件和密码重置等。"
enable-email: "启用电子邮件送递" enable-email: "启用电子邮件送递"
@ -1503,7 +1494,7 @@ mobile/views/pages/following.vue:
mobile/views/pages/home.vue: mobile/views/pages/home.vue:
home: "首页" home: "首页"
local: "Local" local: "Local"
hybrid: "Social" hybrid: "社交"
global: "Global" global: "Global"
mentions: "Mentions" mentions: "Mentions"
messages: "直接发布" messages: "直接发布"
@ -1559,7 +1550,7 @@ deck:
widgets: "小部件" widgets: "小部件"
home: "首页" home: "首页"
local: "Local" local: "Local"
hybrid: "Social" hybrid: "社交"
hashtag: "标签" hashtag: "标签"
global: "Global" global: "Global"
mentions: "Mentions" mentions: "Mentions"
@ -1632,12 +1623,3 @@ dev/views/new-app.vue:
authority: "权限" authority: "权限"
authority-desc: "只能通过API访问此处请求的功能。" authority-desc: "只能通过API访问此处请求的功能。"
authority-warning: "您可以在创建应用程序后对其进行更改,但如果您授予不同的权限,则当时关联的所有用户密钥都将失效。" authority-warning: "您可以在创建应用程序后对其进行更改,但如果您授予不同的权限,则当时关联的所有用户密钥都将失效。"
account-read: "查看账户信息"
account-write: "修改账户信息"
note-write: "投稿。"
reaction-write: "添加或删除回应。"
following-write: "关注和不关注"
drive-read: "查看网盘"
drive-write: "管理网盘文件。"
notification-read: "阅读您的通知"
notification-write: "管理通知"

View File

@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "10.100.0", "version": "11.0.0",
"codename": "nighthike", "codename": "daybreak",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/syuilo/misskey.git" "url": "https://github.com/syuilo/misskey.git"
@ -11,7 +11,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node ./index.js", "start": "node ./index.js",
"debug": "DEBUG=misskey:* node ./index.js", "init": "node ./built/init.js",
"migrate": "node ./built/migrate.js",
"build": "webpack && gulp build", "build": "webpack && gulp build",
"webpack": "webpack", "webpack": "webpack",
"watch": "webpack --watch", "watch": "webpack --watch",
@ -32,14 +33,13 @@
"@prezzemolo/rap": "0.1.2", "@prezzemolo/rap": "0.1.2",
"@prezzemolo/zip": "0.0.3", "@prezzemolo/zip": "0.0.3",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.5.8", "@types/bull": "3.5.11",
"@types/chai-http": "3.0.5",
"@types/dateformat": "3.0.0", "@types/dateformat": "3.0.0",
"@types/deep-equal": "1.0.1", "@types/deep-equal": "1.0.1",
"@types/double-ended-queue": "2.1.0", "@types/double-ended-queue": "2.1.0",
"@types/elasticsearch": "5.0.30", "@types/elasticsearch": "5.0.32",
"@types/file-type": "10.6.0", "@types/file-type": "10.9.1",
"@types/gulp": "4.0.5", "@types/gulp": "4.0.6",
"@types/gulp-mocha": "0.0.32", "@types/gulp-mocha": "0.0.32",
"@types/gulp-rename": "0.0.33", "@types/gulp-rename": "0.0.33",
"@types/gulp-replace": "0.0.31", "@types/gulp-replace": "0.0.31",
@ -47,66 +47,66 @@
"@types/gulp-util": "3.0.34", "@types/gulp-util": "3.0.34",
"@types/is-root": "1.0.0", "@types/is-root": "1.0.0",
"@types/is-url": "1.2.28", "@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.0", "@types/js-yaml": "3.12.1",
"@types/jsdom": "12.2.3", "@types/jsdom": "12.2.3",
"@types/katex": "0.10.1", "@types/katex": "0.10.1",
"@types/koa": "2.0.48", "@types/koa": "2.0.48",
"@types/koa-bodyparser": "5.0.2", "@types/koa-bodyparser": "5.0.2",
"@types/koa-compress": "2.0.8", "@types/koa-compress": "2.0.9",
"@types/koa-cors": "0.0.0", "@types/koa-cors": "0.0.0",
"@types/koa-favicon": "2.0.19", "@types/koa-favicon": "2.0.19",
"@types/koa-logger": "3.1.1", "@types/koa-logger": "3.1.1",
"@types/koa-mount": "3.0.1", "@types/koa-mount": "3.0.1",
"@types/koa-multer": "1.0.0", "@types/koa-multer": "1.0.0",
"@types/koa-router": "7.0.40", "@types/koa-router": "7.0.40",
"@types/koa-send": "4.1.1", "@types/koa-send": "4.1.2",
"@types/koa-views": "2.0.3", "@types/koa-views": "2.0.3",
"@types/koa__cors": "2.2.3", "@types/koa__cors": "2.2.3",
"@types/lolex": "3.1.1",
"@types/minio": "7.0.1", "@types/minio": "7.0.1",
"@types/mkdirp": "0.5.2", "@types/mocha": "5.2.6",
"@types/mocha": "5.2.5", "@types/mongodb": "3.1.22",
"@types/mongodb": "3.1.20", "@types/monk": "6.0.0",
"@types/node": "11.10.4", "@types/node": "11.13.4",
"@types/nodemailer": "4.6.6", "@types/nodemailer": "4.6.7",
"@types/nprogress": "0.0.29", "@types/nprogress": "0.0.29",
"@types/oauth": "0.9.1", "@types/oauth": "0.9.1",
"@types/parse5": "5.0.0", "@types/parse5": "5.0.0",
"@types/parsimmon": "1.10.0", "@types/parsimmon": "1.10.0",
"@types/portscanner": "2.1.0", "@types/portscanner": "2.1.0",
"@types/pug": "2.0.4", "@types/pug": "2.0.4",
"@types/qrcode": "1.3.0", "@types/qrcode": "1.3.2",
"@types/ratelimiter": "2.1.28", "@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.10", "@types/redis": "2.8.12",
"@types/rename": "1.0.1", "@types/rename": "1.0.1",
"@types/request": "2.48.1", "@types/request": "2.48.1",
"@types/request-promise-native": "1.0.15", "@types/request-promise-native": "1.0.15",
"@types/request-stats": "3.0.0", "@types/request-stats": "3.0.0",
"@types/rimraf": "2.0.2", "@types/rimraf": "2.0.2",
"@types/seedrandom": "2.4.27", "@types/sharp": "0.22.1",
"@types/sharp": "0.21.2",
"@types/showdown": "1.9.2", "@types/showdown": "1.9.2",
"@types/speakeasy": "2.0.4", "@types/speakeasy": "2.0.4",
"@types/systeminformation": "3.23.1", "@types/systeminformation": "3.23.1",
"@types/tinycolor2": "1.4.1", "@types/tinycolor2": "1.4.1",
"@types/tmp": "0.0.33", "@types/tmp": "0.1.0",
"@types/uuid": "3.4.4", "@types/uuid": "3.4.4",
"@types/web-push": "3.3.0", "@types/web-push": "3.3.0",
"@types/webpack": "4.4.24", "@types/webpack": "4.4.27",
"@types/webpack-stream": "3.2.10", "@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
"animejs": "3.0.1", "animejs": "3.0.1",
"apexcharts": "3.6.2", "apexcharts": "3.6.7",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "4.0.2", "autosize": "4.0.2",
"autwh": "0.1.0", "autwh": "0.1.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"bootstrap-vue": "2.0.0-rc.13", "bootstrap-vue": "2.0.0-rc.13",
"bull": "3.7.0", "bull": "3.7.0",
"cafy": "15.1.0", "cafy": "15.1.1",
"chai": "4.2.0", "chai": "4.2.0",
"chai-http": "4.2.1",
"chalk": "2.4.2", "chalk": "2.4.2",
"cli-highlight": "2.1.0",
"commander": "2.20.0", "commander": "2.20.0",
"content-disposition": "0.5.3", "content-disposition": "0.5.3",
"crc-32": "1.2.0", "crc-32": "1.2.0",
@ -114,17 +114,15 @@
"cssnano": "4.1.10", "cssnano": "4.1.10",
"dateformat": "3.0.3", "dateformat": "3.0.3",
"deep-equal": "1.0.1", "deep-equal": "1.0.1",
"deepcopy": "0.6.3",
"diskusage": "1.0.0", "diskusage": "1.0.0",
"double-ended-queue": "2.1.0-0", "double-ended-queue": "2.1.0-0",
"elasticsearch": "15.4.1", "elasticsearch": "15.4.1",
"emojilib": "2.4.0", "emojilib": "2.4.0",
"escape-regexp": "0.0.1", "eslint": "5.16.0",
"eslint": "5.15.1",
"eslint-plugin-vue": "5.2.2", "eslint-plugin-vue": "5.2.2",
"eventemitter3": "3.1.0", "eventemitter3": "3.1.0",
"feed": "2.0.4", "feed": "2.0.4",
"file-type": "10.10.0", "file-type": "10.11.0",
"fuckadblock": "3.2.1", "fuckadblock": "3.2.1",
"gulp": "4.0.0", "gulp": "4.0.0",
"gulp-cssnano": "2.1.3", "gulp-cssnano": "2.1.3",
@ -135,19 +133,19 @@
"gulp-sourcemaps": "2.6.5", "gulp-sourcemaps": "2.6.5",
"gulp-stylus": "2.7.0", "gulp-stylus": "2.7.0",
"gulp-tslint": "8.1.4", "gulp-tslint": "8.1.4",
"gulp-typescript": "5.0.0", "gulp-typescript": "5.0.1",
"gulp-uglify": "3.0.2", "gulp-uglify": "3.0.2",
"gulp-util": "3.0.8", "gulp-util": "3.0.8",
"hard-source-webpack-plugin": "0.13.1", "hard-source-webpack-plugin": "0.13.1",
"html-minifier": "3.5.21", "html-minifier": "4.0.0",
"http-signature": "1.2.0", "http-signature": "1.2.0",
"insert-text-at-cursor": "0.1.2", "insert-text-at-cursor": "0.2.0",
"is-root": "2.0.0", "is-root": "2.0.0",
"is-svg": "4.0.0", "is-svg": "4.1.0",
"js-yaml": "3.13.0", "js-yaml": "3.13.1",
"jsdom": "14.0.0", "jsdom": "14.0.0",
"json5": "2.1.0", "json5": "2.1.0",
"json5-loader": "1.0.1", "json5-loader": "2.0.0",
"katex": "0.10.1", "katex": "0.10.1",
"koa": "2.7.0", "koa": "2.7.0",
"koa-bodyparser": "4.2.1", "koa-bodyparser": "4.2.1",
@ -163,23 +161,25 @@
"koa-views": "6.2.0", "koa-views": "6.2.0",
"langmap": "0.0.16", "langmap": "0.0.16",
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
"lolex": "3.1.0",
"lookup-dns-cache": "2.1.0", "lookup-dns-cache": "2.1.0",
"minio": "7.0.5", "minio": "7.0.5",
"mkdirp": "0.5.1", "mocha": "6.1.3",
"mocha": "5.2.0",
"moji": "0.5.1", "moji": "0.5.1",
"moment": "2.24.0", "moment": "2.24.0",
"mongodb": "3.2.2", "mongodb": "3.2.3",
"monk": "6.0.6", "monk": "6.0.6",
"ms": "2.1.1", "ms": "2.1.1",
"nan": "2.12.1", "nan": "2.12.1",
"nested-property": "0.0.7", "nested-property": "0.0.7",
"nodemailer": "5.1.1", "node-fetch": "2.3.0",
"nodemailer": "6.1.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"object-assign-deep": "0.4.0", "object-assign-deep": "0.4.0",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"parse5": "5.1.0", "parse5": "5.1.0",
"parsimmon": "1.12.0", "parsimmon": "1.12.0",
"pg": "7.9.0",
"portscanner": "2.2.0", "portscanner": "2.2.0",
"postcss-loader": "3.0.0", "postcss-loader": "3.0.0",
"prismjs": "1.16.0", "prismjs": "1.16.0",
@ -195,14 +195,15 @@
"recaptcha-promise": "0.1.3", "recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.10", "reconnecting-websocket": "4.1.10",
"redis": "2.8.0", "redis": "2.8.0",
"reflect-metadata": "0.1.13",
"rename": "1.0.4", "rename": "1.0.4",
"request": "2.88.0", "request": "2.88.0",
"request-promise-native": "1.0.7", "request-promise-native": "1.0.7",
"request-stats": "3.0.0", "request-stats": "3.0.0",
"require-all": "3.0.0",
"rimraf": "2.6.3", "rimraf": "2.6.3",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"s-age": "1.1.2", "s-age": "1.1.2",
"seedrandom": "2.4.4",
"sharp": "0.22.0", "sharp": "0.22.0",
"showdown": "1.9.0", "showdown": "1.9.0",
"showdown-highlightjs-extension": "0.1.2", "showdown-highlightjs-extension": "0.1.2",
@ -212,19 +213,21 @@
"stylus": "0.54.5", "stylus": "0.54.5",
"stylus-loader": "3.0.2", "stylus-loader": "3.0.2",
"summaly": "2.2.0", "summaly": "2.2.0",
"systeminformation": "4.0.16", "systeminformation": "4.1.4",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.2.3", "terser-webpack-plugin": "1.2.3",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"tinycolor2": "1.4.1", "tinycolor2": "1.4.1",
"tmp": "0.0.33", "tmp": "0.1.0",
"ts-loader": "5.3.3", "ts-loader": "5.3.3",
"ts-node": "8.0.3", "ts-node": "7.0.1",
"tslint": "5.13.1", "tslint": "5.15.0",
"tslint-sonarts": "1.9.0", "tslint-sonarts": "1.9.0",
"typeorm": "0.2.16-rc.1",
"typescript": "3.3.3333", "typescript": "3.3.3333",
"typescript-eslint-parser": "22.0.0", "typescript-eslint-parser": "22.0.0",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
"ulid": "2.3.0",
"url-loader": "1.1.2", "url-loader": "1.1.2",
"uuid": "3.3.2", "uuid": "3.3.2",
"v-animate-css": "0.0.3", "v-animate-css": "0.0.3",
@ -232,7 +235,7 @@
"video-thumbnail-generator": "1.1.3", "video-thumbnail-generator": "1.1.3",
"vue": "2.6.10", "vue": "2.6.10",
"vue-color": "2.7.0", "vue-color": "2.7.0",
"vue-content-loading": "1.5.3", "vue-content-loading": "1.6.0",
"vue-cropperjs": "3.0.0", "vue-cropperjs": "3.0.0",
"vue-i18n": "8.10.0", "vue-i18n": "8.10.0",
"vue-js-modal": "1.3.28", "vue-js-modal": "1.3.28",
@ -240,7 +243,7 @@
"vue-loader": "15.7.0", "vue-loader": "15.7.0",
"vue-marquee-text-component": "1.1.1", "vue-marquee-text-component": "1.1.1",
"vue-prism-component": "1.1.1", "vue-prism-component": "1.1.1",
"vue-router": "3.0.2", "vue-router": "3.0.4",
"vue-sequential-entrance": "1.1.3", "vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.15", "vue-svg-inline-loader": "1.2.15",
@ -250,9 +253,8 @@
"vuex": "3.1.0", "vuex": "3.1.0",
"vuex-persistedstate": "2.5.4", "vuex-persistedstate": "2.5.4",
"web-push": "3.3.3", "web-push": "3.3.3",
"webfinger.js": "2.7.0", "webpack": "4.30.0",
"webpack": "4.28.4", "webpack-cli": "3.3.0",
"webpack-cli": "3.2.3",
"websocket": "1.0.28", "websocket": "1.0.28",
"ws": "6.2.1", "ws": "6.2.1",
"xev": "2.0.1" "xev": "2.0.1"

View File

@ -1,19 +0,0 @@
declare module 'deepcopy' {
type DeepcopyCustomizerValueType = 'Object';
type DeepcopyCustomizer<T> = (
value: T,
valueType: DeepcopyCustomizerValueType) => T;
interface IDeepcopyOptions<T> {
customizer: DeepcopyCustomizer<T>;
}
function deepcopy<T>(
value: T,
options?: IDeepcopyOptions<T> | DeepcopyCustomizer<T>): T;
namespace deepcopy {} // Hack
export = deepcopy;
}

View File

@ -1,7 +0,0 @@
declare module 'escape-regexp' {
function escapeRegExp(str: string): string;
namespace escapeRegExp {} // Hack
export = escapeRegExp;
}

View File

@ -1,65 +0,0 @@
declare module 'webfinger.js' {
interface IWebFingerConstructorConfig {
tls_only?: boolean;
webfist_fallback?: boolean;
uri_fallback?: boolean;
request_timeout?: number;
}
type JRDProperties = { [type: string]: string };
interface IJRDLink {
rel: string;
type?: string;
href?: string;
template?: string;
titles?: { [lang: string]: string };
properties?: JRDProperties;
}
interface IJRD {
subject?: string;
expires?: Date;
aliases?: string[];
properties?: JRDProperties;
links?: IJRDLink[];
}
interface IIDXLinks {
'avatar': IJRDLink[];
'remotestorage': IJRDLink[];
'blog': IJRDLink[];
'vcard': IJRDLink[];
'updates': IJRDLink[];
'share': IJRDLink[];
'profile': IJRDLink[];
'webfist': IJRDLink[];
'camlistore': IJRDLink[];
[type: string]: IJRDLink[];
}
interface IIDXProperties {
'name': string;
[type: string]: string;
}
interface IIDX {
links: IIDXLinks;
properties: IIDXProperties;
}
interface ILookupCallbackResult {
object: IJRD;
json: string;
idx: IIDX;
}
type LookupCallback = (err: Error | string, result?: ILookupCallbackResult) => void;
export class WebFinger {
constructor(config?: IWebFingerConstructorConfig);
public lookup(address: string, cb: LookupCallback): NodeJS.Timeout;
public lookupLink(address: string, rel: string, cb: IJRDLink): void;
}
}

View File

@ -15,5 +15,8 @@ program
.parse(process.argv); .parse(process.argv);
if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true; if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true;
if (process.env.NODE_ENV === 'test') program.disableClustering = true;
if (process.env.NODE_ENV === 'test') program.quiet = true;
if (process.env.NODE_ENV === 'test') program.noDaemons = true;
export { program }; export { program };

77
src/boot/index.ts Normal file
View File

@ -0,0 +1,77 @@
import * as cluster from 'cluster';
import chalk from 'chalk';
import Xev from 'xev';
import Logger from '../services/logger';
import { program } from '../argv';
// for typeorm
import 'reflect-metadata';
import { masterMain } from './master';
import { workerMain } from './worker';
const logger = new Logger('core', 'cyan');
const clusterLogger = logger.createSubLogger('cluster', 'orange', false);
const ev = new Xev();
/**
* Init process
*/
export default async function() {
process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`;
if (cluster.isMaster || program.disableClustering) {
await masterMain();
if (cluster.isMaster) {
ev.mount();
}
}
if (cluster.isWorker || program.disableClustering) {
await workerMain();
}
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
// それ以外のときは process.send は使えないので弾く
if (process.send) {
process.send('ok');
}
}
//#region Events
// Listen new workers
cluster.on('fork', worker => {
clusterLogger.debug(`Process forked: [${worker.id}]`);
});
// Listen online workers
cluster.on('online', worker => {
clusterLogger.debug(`Process is now online: [${worker.id}]`);
});
// Listen for dying workers
cluster.on('exit', worker => {
// Replace the dead worker,
// we're not sentimental
clusterLogger.error(chalk.red(`[${worker.id}] died :(`));
cluster.fork();
});
// Display detail of unhandled promise rejection
if (!program.quiet) {
process.on('unhandledRejection', console.dir);
}
// Display detail of uncaught exception
process.on('uncaughtException', err => {
logger.error(err);
});
// Dying away...
process.on('exit', code => {
logger.info(`The process is going to exit with code ${code}`);
});
//#endregion

176
src/boot/master.ts Normal file
View File

@ -0,0 +1,176 @@
import * as os from 'os';
import * as cluster from 'cluster';
import chalk from 'chalk';
import * as portscanner from 'portscanner';
import * as isRoot from 'is-root';
import Logger from '../services/logger';
import loadConfig from '../config/load';
import { Config } from '../config/types';
import { lessThan } from '../prelude/array';
import * as pkg from '../../package.json';
import { program } from '../argv';
import { showMachineInfo } from '../misc/show-machine-info';
import { initDb } from '../db/postgre';
const logger = new Logger('core', 'cyan');
const bootLogger = logger.createSubLogger('boot', 'magenta', false);
function greet() {
if (!program.quiet) {
//#region Misskey logo
const v = `v${pkg.version}`;
console.log(' _____ _ _ ');
console.log(' | |_|___ ___| |_ ___ _ _ ');
console.log(' | | | | |_ -|_ -| \'_| -_| | |');
console.log(' |_|_|_|_|___|___|_,_|___|_ |');
console.log(' ' + chalk.gray(v) + (' |___|\n'.substr(v.length)));
//#endregion
console.log(' Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
console.log(chalk.keyword('orange')(' If you like Misskey, please donate to support development. https://www.patreon.com/syuilo'));
console.log('');
console.log(chalk`< ${os.hostname()} {gray (PID: ${process.pid.toString()})} >`);
}
bootLogger.info('Welcome to Misskey!');
bootLogger.info(`Misskey v${pkg.version}`, null, true);
}
/**
* Init master process
*/
export async function masterMain() {
greet();
let config!: Config;
try {
// initialize app
config = await init();
if (config.port == null) {
bootLogger.error('The port is not configured. Please configure port.', null, true);
process.exit(1);
}
if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
bootLogger.error('You need root privileges to listen on well-known port on Linux', null, true);
process.exit(1);
}
if (!await isPortAvailable(config.port)) {
bootLogger.error(`Port ${config.port} is already in use`, null, true);
process.exit(1);
}
} catch (e) {
bootLogger.error('Fatal error occurred during initialization', null, true);
process.exit(1);
}
bootLogger.succ('Misskey initialized');
if (!program.disableClustering) {
await spawnWorkers(config.clusterLimit);
}
if (!program.noDaemons) {
require('../daemons/server-stats').default();
require('../daemons/notes-stats').default();
require('../daemons/queue-stats').default();
}
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);
}
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
const requiredNodejsVersion = [11, 7, 0];
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
function isWellKnownPort(port: number): boolean {
return port < 1024;
}
async function isPortAvailable(port: number): Promise<boolean> {
return await portscanner.checkPortStatus(port, '127.0.0.1') === 'closed';
}
function showEnvironment(): void {
const env = process.env.NODE_ENV;
const logger = bootLogger.createSubLogger('env');
logger.info(typeof env == 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`);
if (env !== 'production') {
logger.warn('The environment is not in production mode.');
logger.warn('DO NOT USE FOR PRODUCTION PURPOSE!', null, true);
}
logger.info(`You ${isRoot() ? '' : 'do not '}have root privileges`);
}
/**
* Init app
*/
async function init(): Promise<Config> {
showEnvironment();
const nodejsLogger = bootLogger.createSubLogger('nodejs');
nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`);
if (!satisfyNodejsVersion) {
nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, null, true);
process.exit(1);
}
await showMachineInfo(bootLogger);
const configLogger = bootLogger.createSubLogger('config');
let config;
try {
config = loadConfig();
} catch (exception) {
if (typeof exception === 'string') {
configLogger.error(exception);
process.exit(1);
}
if (exception.code === 'ENOENT') {
configLogger.error('Configuration file not found', null, true);
process.exit(1);
}
throw exception;
}
configLogger.succ('Loaded');
// Try to connect to DB
try {
bootLogger.info('Connecting database...');
await initDb();
} catch (e) {
bootLogger.error('Cannot connect to database', null, true);
bootLogger.error(e);
process.exit(1);
}
return config;
}
async function spawnWorkers(limit: number = Infinity) {
const workers = Math.min(limit, os.cpus().length);
bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
await Promise.all([...Array(workers)].map(spawnWorker));
bootLogger.succ('All workers started');
}
function spawnWorker(): Promise<void> {
return new Promise(res => {
const worker = cluster.fork();
worker.on('message', message => {
if (message !== 'ready') return;
res();
});
});
}

20
src/boot/worker.ts Normal file
View File

@ -0,0 +1,20 @@
import * as cluster from 'cluster';
import { initDb } from '../db/postgre';
/**
* Init worker process
*/
export async function workerMain() {
await initDb();
// start server
await require('../server').default();
// start job queue
require('../queue').default();
if (cluster.isWorker) {
// Send a 'ready' message to parent process
process.send!('ready');
}
}

View File

@ -38,7 +38,7 @@
<div class="kidvdlkg" v-for="file in files"> <div class="kidvdlkg" v-for="file in files">
<div @click="file._open = !file._open"> <div @click="file._open = !file._open">
<div> <div>
<div class="thumbnail" :style="thumbnail(file)"></div> <x-file-thumbnail class="thumbnail" :file="file" fit="contain" @click="showFileMenu(file)"/>
</div> </div>
<div> <div>
<header> <header>
@ -48,7 +48,7 @@
<div> <div>
<div> <div>
<span style="margin-right:16px;">{{ file.type }}</span> <span style="margin-right:16px;">{{ file.type }}</span>
<span>{{ file.datasize | bytes }}</span> <span>{{ file.size | bytes }}</span>
</div> </div>
<div><mk-time :time="file.createdAt" mode="detail"/></div> <div><mk-time :time="file.createdAt" mode="detail"/></div>
</div> </div>
@ -75,10 +75,15 @@ import Vue from 'vue';
import i18n from '../../i18n'; import i18n from '../../i18n';
import { faCloud, faTerminal, faSearch } from '@fortawesome/free-solid-svg-icons'; import { faCloud, faTerminal, faSearch } from '@fortawesome/free-solid-svg-icons';
import { faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons'; import { faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import XFileThumbnail from '../../common/views/components/drive-file-thumbnail.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/drive.vue'), i18n: i18n('admin/views/drive.vue'),
components: {
XFileThumbnail
},
data() { data() {
return { return {
file: null, file: null,
@ -151,13 +156,6 @@ export default Vue.extend({
}); });
}, },
thumbnail(file: any): any {
return {
'background-color': file.properties.avgColor && file.properties.avgColor.length == 3 ? `rgb(${file.properties.avgColor.join(',')})` : 'transparent',
'background-image': `url(${file.thumbnailUrl})`
};
},
async del(file: any) { async del(file: any) {
const process = async () => { const process = async () => {
await this.$root.api('drive/files/delete', { fileId: file.id }); await this.$root.api('drive/files/delete', { fileId: file.id });
@ -179,9 +177,9 @@ export default Vue.extend({
this.$root.api('drive/files/update', { this.$root.api('drive/files/update', {
fileId: file.id, fileId: file.id,
isSensitive: !file.isSensitive isSensitive: !file.isSensitive
}); }).then(() => {
file.isSensitive = !file.isSensitive; file.isSensitive = !file.isSensitive;
});
}, },
async show() { async show() {

View File

@ -3,7 +3,7 @@
<ui-card> <ui-card>
<template #title>{{ $t('hided-tags') }}</template> <template #title>{{ $t('hided-tags') }}</template>
<section> <section>
<textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hidedTags"></textarea> <textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hiddenTags"></textarea>
<ui-button @click="save">{{ $t('save') }}</ui-button> <ui-button @click="save">{{ $t('save') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
@ -18,18 +18,18 @@ export default Vue.extend({
i18n: i18n('admin/views/hashtags.vue'), i18n: i18n('admin/views/hashtags.vue'),
data() { data() {
return { return {
hidedTags: '', hiddenTags: '',
}; };
}, },
created() { created() {
this.$root.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.hidedTags = meta.hidedTags.join('\n'); this.hiddenTags = meta.hiddenTags.join('\n');
}); });
}, },
methods: { methods: {
save() { save() {
this.$root.api('admin/update-meta', { this.$root.api('admin/update-meta', {
hidedTags: this.hidedTags.split('\n') hiddenTags: this.hiddenTags.split('\n')
}).then(() => { }).then(() => {
//this.$root.os.apis.dialog({ text: `Saved` }); //this.$root.os.apis.dialog({ text: `Saved` });
}).catch(e => { }).catch(e => {

View File

@ -77,12 +77,6 @@
<header>summaly Proxy</header> <header>summaly Proxy</header>
<ui-input v-model="summalyProxy">URL</ui-input> <ui-input v-model="summalyProxy">URL</ui-input>
</section> </section>
<section>
<header><fa :icon="faUserPlus"/> {{ $t('user-recommendation-config') }}</header>
<ui-switch v-model="enableExternalUserRecommendation">{{ $t('enable-external-user-recommendation') }}</ui-switch>
<ui-input v-model="externalUserRecommendationEngine" :disabled="!enableExternalUserRecommendation">{{ $t('external-user-recommendation-engine') }}<template #desc>{{ $t('external-user-recommendation-engine-desc') }}</template></ui-input>
<ui-input v-model="externalUserRecommendationTimeout" type="number" :disabled="!enableExternalUserRecommendation">{{ $t('external-user-recommendation-timeout') }}<template #suffix>ms</template><template #desc>{{ $t('external-user-recommendation-timeout-desc') }}</template></ui-input>
</section>
<section> <section>
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button> <ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
</section> </section>
@ -184,9 +178,6 @@ export default Vue.extend({
discordClientSecret: null, discordClientSecret: null,
proxyAccount: null, proxyAccount: null,
inviteCode: null, inviteCode: null,
enableExternalUserRecommendation: false,
externalUserRecommendationEngine: null,
externalUserRecommendationTimeout: null,
summalyProxy: null, summalyProxy: null,
enableEmail: false, enableEmail: false,
email: null, email: null,
@ -205,8 +196,8 @@ export default Vue.extend({
created() { created() {
this.$root.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.maintainerName = meta.maintainer.name; this.maintainerName = meta.maintainerName;
this.maintainerEmail = meta.maintainer.email; this.maintainerEmail = meta.maintainerEmail;
this.disableRegistration = meta.disableRegistration; this.disableRegistration = meta.disableRegistration;
this.disableLocalTimeline = meta.disableLocalTimeline; this.disableLocalTimeline = meta.disableLocalTimeline;
this.disableGlobalTimeline = meta.disableGlobalTimeline; this.disableGlobalTimeline = meta.disableGlobalTimeline;
@ -236,9 +227,6 @@ export default Vue.extend({
this.enableDiscordIntegration = meta.enableDiscordIntegration; this.enableDiscordIntegration = meta.enableDiscordIntegration;
this.discordClientId = meta.discordClientId; this.discordClientId = meta.discordClientId;
this.discordClientSecret = meta.discordClientSecret; this.discordClientSecret = meta.discordClientSecret;
this.enableExternalUserRecommendation = meta.enableExternalUserRecommendation;
this.externalUserRecommendationEngine = meta.externalUserRecommendationEngine;
this.externalUserRecommendationTimeout = meta.externalUserRecommendationTimeout;
this.summalyProxy = meta.summalyProxy; this.summalyProxy = meta.summalyProxy;
this.enableEmail = meta.enableEmail; this.enableEmail = meta.enableEmail;
this.email = meta.email; this.email = meta.email;
@ -299,9 +287,6 @@ export default Vue.extend({
enableDiscordIntegration: this.enableDiscordIntegration, enableDiscordIntegration: this.enableDiscordIntegration,
discordClientId: this.discordClientId, discordClientId: this.discordClientId,
discordClientSecret: this.discordClientSecret, discordClientSecret: this.discordClientSecret,
enableExternalUserRecommendation: this.enableExternalUserRecommendation,
externalUserRecommendationEngine: this.externalUserRecommendationEngine,
externalUserRecommendationTimeout: parseInt(this.externalUserRecommendationTimeout, 10),
summalyProxy: this.summalyProxy, summalyProxy: this.summalyProxy,
enableEmail: this.enableEmail, enableEmail: this.enableEmail,
email: this.email, email: this.email,

View File

@ -19,7 +19,7 @@
</ui-horizon-group> </ui-horizon-group>
<div class="nqjzuvev"> <div class="nqjzuvev">
<code v-for="log in logs" :key="log._id" :class="log.level"> <code v-for="log in logs" :key="log.id" :class="log.level">
<details> <details>
<summary><mk-time :time="log.createdAt"/> [{{ log.domain.join('.') }}] {{ log.message }}</summary> <summary><mk-time :time="log.createdAt"/> [{{ log.domain.join('.') }}] {{ log.message }}</summary>
<vue-json-pretty v-if="log.data" :data="log.data"></vue-json-pretty> <vue-json-pretty v-if="log.data" :data="log.data"></vue-json-pretty>

View File

@ -165,7 +165,7 @@ export default Vue.extend({
/** 処理対象ユーザーの情報を更新する */ /** 処理対象ユーザーの情報を更新する */
async refreshUser() { async refreshUser() {
this.$root.api('admin/show-user', { userId: this.user._id }).then(info => { this.$root.api('admin/show-user', { userId: this.user.id }).then(info => {
this.user = info; this.user = info;
}); });
}, },
@ -173,7 +173,7 @@ export default Vue.extend({
async resetPassword() { async resetPassword() {
if (!await this.getConfirmed(this.$t('reset-password-confirm'))) return; if (!await this.getConfirmed(this.$t('reset-password-confirm'))) return;
this.$root.api('admin/reset-password', { userId: this.user._id }).then(res => { this.$root.api('admin/reset-password', { userId: this.user.id }).then(res => {
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('password-updated', { password: res.password }) text: this.$t('password-updated', { password: res.password })
@ -187,7 +187,7 @@ export default Vue.extend({
this.verifying = true; this.verifying = true;
const process = async () => { const process = async () => {
await this.$root.api('admin/verify-user', { userId: this.user._id }); await this.$root.api('admin/verify-user', { userId: this.user.id });
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('verified') text: this.$t('verified')
@ -212,7 +212,7 @@ export default Vue.extend({
this.unverifying = true; this.unverifying = true;
const process = async () => { const process = async () => {
await this.$root.api('admin/unverify-user', { userId: this.user._id }); await this.$root.api('admin/unverify-user', { userId: this.user.id });
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('unverified') text: this.$t('unverified')
@ -233,7 +233,7 @@ export default Vue.extend({
async silenceUser() { async silenceUser() {
const process = async () => { const process = async () => {
await this.$root.api('admin/silence-user', { userId: this.user._id }); await this.$root.api('admin/silence-user', { userId: this.user.id });
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
splash: true splash: true
@ -252,7 +252,7 @@ export default Vue.extend({
async unsilenceUser() { async unsilenceUser() {
const process = async () => { const process = async () => {
await this.$root.api('admin/unsilence-user', { userId: this.user._id }); await this.$root.api('admin/unsilence-user', { userId: this.user.id });
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
splash: true splash: true
@ -275,7 +275,7 @@ export default Vue.extend({
this.suspending = true; this.suspending = true;
const process = async () => { const process = async () => {
await this.$root.api('admin/suspend-user', { userId: this.user._id }); await this.$root.api('admin/suspend-user', { userId: this.user.id });
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('suspended') text: this.$t('suspended')
@ -300,7 +300,7 @@ export default Vue.extend({
this.unsuspending = true; this.unsuspending = true;
const process = async () => { const process = async () => {
await this.$root.api('admin/unsuspend-user', { userId: this.user._id }); await this.$root.api('admin/unsuspend-user', { userId: this.user.id });
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('unsuspended') text: this.$t('unsuspended')
@ -320,7 +320,7 @@ export default Vue.extend({
}, },
async updateRemoteUser() { async updateRemoteUser() {
this.$root.api('admin/update-remote-user', { userId: this.user._id }).then(res => { this.$root.api('admin/update-remote-user', { userId: this.user.id }).then(res => {
this.$root.dialog({ this.$root.dialog({
type: 'success', type: 'success',
text: this.$t('remote-user-updated') text: this.$t('remote-user-updated')

View File

@ -14,15 +14,7 @@
<h2>{{ $t('permission-ask') }}</h2> <h2>{{ $t('permission-ask') }}</h2>
<ul> <ul>
<template v-for="p in app.permission"> <template v-for="p in app.permission">
<li v-if="p == 'account-read'">{{ $t('account-read') }}</li> <li :key="p">{{ $t(`@.permissions.${p}`) }}</li>
<li v-if="p == 'account-write'">{{ $t('account-write') }}</li>
<li v-if="p == 'note-write'">{{ $t('note-write') }}</li>
<li v-if="p == 'like-write'">{{ $t('like-write') }}</li>
<li v-if="p == 'following-write'">{{ $t('following-write') }}</li>
<li v-if="p == 'drive-read'">{{ $t('drive-read') }}</li>
<li v-if="p == 'drive-write'">{{ $t('drive-write') }}</li>
<li v-if="p == 'notification-read'">{{ $t('notification-read') }}</li>
<li v-if="p == 'notification-write'">{{ $t('notification-write') }}</li>
</template> </template>
</ul> </ul>
</section> </section>

View File

@ -45,15 +45,9 @@ export default function <T extends object>(data: {
this.$watch('props', () => { this.$watch('props', () => {
this.mergeProps(); this.mergeProps();
}); });
this.bakeProps();
}, },
methods: { methods: {
bakeProps() {
this.bakedOldProps = JSON.stringify(this.props);
},
mergeProps() { mergeProps() {
if (data.props) { if (data.props) {
const defaultProps = data.props(); const defaultProps = data.props();
@ -65,17 +59,10 @@ export default function <T extends object>(data: {
}, },
save() { save() {
if (this.bakedOldProps == JSON.stringify(this.props)) return;
this.bakeProps();
if (this.platform == 'deck') { if (this.platform == 'deck') {
this.$store.commit('device/updateDeckColumn', this.column); this.$store.commit('device/updateDeckColumn', this.column);
} else { } else {
this.$root.api('i/update_widget', { this.$store.commit('device/updateWidget', this.widget);
id: this.id,
data: this.props
});
} }
} }
} }

View File

@ -70,8 +70,8 @@ export default (opts: Opts = {}) => ({
}, },
reactionsCount(): number { reactionsCount(): number {
return this.appearNote.reactionCounts return this.appearNote.reactions
? sum(Object.values(this.appearNote.reactionCounts)) ? sum(Object.values(this.appearNote.reactions))
: 0; : 0;
}, },

View File

@ -87,16 +87,16 @@ export default prop => ({
case 'reacted': { case 'reacted': {
const reaction = body.reaction; const reaction = body.reaction;
if (this.$_ns_target.reactionCounts == null) { if (this.$_ns_target.reactions == null) {
Vue.set(this.$_ns_target, 'reactionCounts', {}); Vue.set(this.$_ns_target, 'reactions', {});
} }
if (this.$_ns_target.reactionCounts[reaction] == null) { if (this.$_ns_target.reactions[reaction] == null) {
Vue.set(this.$_ns_target.reactionCounts, reaction, 0); Vue.set(this.$_ns_target.reactions, reaction, 0);
} }
// Increment the count // Increment the count
this.$_ns_target.reactionCounts[reaction]++; this.$_ns_target.reactions[reaction]++;
if (body.userId == this.$store.state.i.id) { if (body.userId == this.$store.state.i.id) {
Vue.set(this.$_ns_target, 'myReaction', reaction); Vue.set(this.$_ns_target, 'myReaction', reaction);
@ -107,16 +107,16 @@ export default prop => ({
case 'unreacted': { case 'unreacted': {
const reaction = body.reaction; const reaction = body.reaction;
if (this.$_ns_target.reactionCounts == null) { if (this.$_ns_target.reactions == null) {
return; return;
} }
if (this.$_ns_target.reactionCounts[reaction] == null) { if (this.$_ns_target.reactions[reaction] == null) {
return; return;
} }
// Decrement the count // Decrement the count
if (this.$_ns_target.reactionCounts[reaction] > 0) this.$_ns_target.reactionCounts[reaction]--; if (this.$_ns_target.reactions[reaction] > 0) this.$_ns_target.reactions[reaction]--;
if (body.userId == this.$store.state.i.id) { if (body.userId == this.$store.state.i.id) {
Vue.set(this.$_ns_target, 'myReaction', null); Vue.set(this.$_ns_target, 'myReaction', null);
@ -125,9 +125,11 @@ export default prop => ({
} }
case 'pollVoted': { case 'pollVoted': {
if (body.userId == this.$store.state.i.id) return;
const choice = body.choice; const choice = body.choice;
this.$_ns_target.poll.choices.find(c => c.id === choice).votes++; this.$_ns_target.poll.choices[choice].votes++;
if (body.userId == this.$store.state.i.id) {
Vue.set(this.$_ns_target.poll.choices[choice], 'isVoted', true);
}
break; break;
} }

View File

@ -55,11 +55,7 @@ export default Vue.extend({
}, },
icon(): any { icon(): any {
return { return {
backgroundColor: this.lightmode backgroundColor: this.user.avatarColor,
? `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`
: this.user.avatarColor && this.user.avatarColor.length == 3
? `rgb(${this.user.avatarColor.join(',')})`
: null,
backgroundImage: this.lightmode ? null : `url(${this.url})`, backgroundImage: this.lightmode ? null : `url(${this.url})`,
borderRadius: this.$store.state.settings.circleIcons ? '100%' : null borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
}; };
@ -67,7 +63,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
if (this.user.avatarColor) { if (this.user.avatarColor) {
this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`; this.$el.style.color = this.user.avatarColor;
} }
}, },
methods: { methods: {

View File

@ -105,15 +105,11 @@ export default Vue.extend({
}, },
isThumbnailAvailable(): boolean { isThumbnailAvailable(): boolean {
return this.file.thumbnailUrl return this.file.thumbnailUrl
? this.file.thumbnailUrl.endsWith('?thumbnail')
? (this.is === 'image' || this.is === 'video') ? (this.is === 'image' || this.is === 'video')
: true
: false; : false;
}, },
background(): string { background(): string {
return this.file.properties.avgColor && this.file.properties.avgColor.length == 3 return this.file.properties.avgColor || 'transparent';
? `rgb(${this.file.properties.avgColor.join(',')})`
: 'transparent';
} }
}, },
mounted() { mounted() {
@ -122,10 +118,10 @@ export default Vue.extend({
}, },
methods: { methods: {
onThumbnailLoaded() { onThumbnailLoaded() {
if (this.file.properties.avgColor && this.file.properties.avgColor.length == 3) { if (this.file.properties.avgColor) {
anime({ anime({
targets: this.$refs.thumbnail, targets: this.$refs.thumbnail,
backgroundColor: `rgba(${this.file.properties.avgColor.join(',')}, 0)`, backgroundColor: this.file.properties.avgColor.replace('255)', '0)'),
duration: 100, duration: 100,
easing: 'linear' easing: 'linear'
}); });

View File

@ -24,11 +24,11 @@
<div class="board"> <div class="board">
<div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels"> <div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span> <span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div> </div>
<div class="flex"> <div class="flex">
<div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels"> <div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<div v-for="i in game.settings.map.length">{{ i }}</div> <div v-for="i in game.map.length">{{ i }}</div>
</div> </div>
<div class="cells" :style="cellsStyle"> <div class="cells" :style="cellsStyle">
<div v-for="(stone, i) in o.board" <div v-for="(stone, i) in o.board"
@ -46,11 +46,11 @@
</div> </div>
</div> </div>
<div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels"> <div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<div v-for="i in game.settings.map.length">{{ i }}</div> <div v-for="i in game.map.length">{{ i }}</div>
</div> </div>
</div> </div>
<div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels"> <div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span> <span v-for="i in game.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div> </div>
</div> </div>
@ -71,9 +71,9 @@
</div> </div>
<div class="info"> <div class="info">
<p v-if="game.settings.isLlotheo">{{ $t('is-llotheo') }}</p> <p v-if="game.isLlotheo">{{ $t('is-llotheo') }}</p>
<p v-if="game.settings.loopedBoard">{{ $t('looped-map') }}</p> <p v-if="game.loopedBoard">{{ $t('looped-map') }}</p>
<p v-if="game.settings.canPutEverywhere">{{ $t('can-put-everywhere') }}</p> <p v-if="game.canPutEverywhere">{{ $t('can-put-everywhere') }}</p>
</div> </div>
</div> </div>
</template> </template>
@ -160,8 +160,8 @@ export default Vue.extend({
cellsStyle(): any { cellsStyle(): any {
return { return {
'grid-template-rows': `repeat(${this.game.settings.map.length}, 1fr)`, 'grid-template-rows': `repeat(${this.game.map.length}, 1fr)`,
'grid-template-columns': `repeat(${this.game.settings.map[0].length}, 1fr)` 'grid-template-columns': `repeat(${this.game.map[0].length}, 1fr)`
}; };
} }
}, },
@ -169,10 +169,10 @@ export default Vue.extend({
watch: { watch: {
logPos(v) { logPos(v) {
if (!this.game.isEnded) return; if (!this.game.isEnded) return;
this.o = new Reversi(this.game.settings.map, { this.o = new Reversi(this.game.map, {
isLlotheo: this.game.settings.isLlotheo, isLlotheo: this.game.isLlotheo,
canPutEverywhere: this.game.settings.canPutEverywhere, canPutEverywhere: this.game.canPutEverywhere,
loopedBoard: this.game.settings.loopedBoard loopedBoard: this.game.loopedBoard
}); });
for (const log of this.logs.slice(0, v)) { for (const log of this.logs.slice(0, v)) {
this.o.put(log.color, log.pos); this.o.put(log.color, log.pos);
@ -184,10 +184,10 @@ export default Vue.extend({
created() { created() {
this.game = this.initGame; this.game = this.initGame;
this.o = new Reversi(this.game.settings.map, { this.o = new Reversi(this.game.map, {
isLlotheo: this.game.settings.isLlotheo, isLlotheo: this.game.isLlotheo,
canPutEverywhere: this.game.settings.canPutEverywhere, canPutEverywhere: this.game.canPutEverywhere,
loopedBoard: this.game.settings.loopedBoard loopedBoard: this.game.loopedBoard
}); });
for (const log of this.game.logs) { for (const log of this.game.logs) {
@ -286,10 +286,10 @@ export default Vue.extend({
onRescue(game) { onRescue(game) {
this.game = game; this.game = game;
this.o = new Reversi(this.game.settings.map, { this.o = new Reversi(this.game.map, {
isLlotheo: this.game.settings.isLlotheo, isLlotheo: this.game.isLlotheo,
canPutEverywhere: this.game.settings.canPutEverywhere, canPutEverywhere: this.game.canPutEverywhere,
loopedBoard: this.game.settings.loopedBoard loopedBoard: this.game.loopedBoard
}); });
for (const log of this.game.logs) { for (const log of this.game.logs) {

View File

@ -17,9 +17,9 @@
</header> </header>
<div> <div>
<div class="random" v-if="game.settings.map == null"><fa icon="dice"/></div> <div class="random" v-if="game.map == null"><fa icon="dice"/></div>
<div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }"> <div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.map[0].length }, 1fr)` }">
<div v-for="(x, i) in game.settings.map.join('')" <div v-for="(x, i) in game.map.join('')"
:data-none="x == ' '" :data-none="x == ' '"
@click="onPixelClick(i, x)"> @click="onPixelClick(i, x)">
<fa v-if="x == 'b'" :icon="fasCircle"/> <fa v-if="x == 'b'" :icon="fasCircle"/>
@ -35,9 +35,9 @@
</header> </header>
<div> <div>
<form-radio v-model="game.settings.bw" value="random" @change="updateSettings">{{ $t('random') }}</form-radio> <form-radio v-model="game.bw" value="random" @change="updateSettings('bw')">{{ $t('random') }}</form-radio>
<form-radio v-model="game.settings.bw" :value="1" @change="updateSettings">{{ this.$t('black-is').split('{}')[0] }}<b><mk-user-name :user="game.user1"/></b>{{ this.$t('black-is').split('{}')[1] }}</form-radio> <form-radio v-model="game.bw" :value="1" @change="updateSettings('bw')">{{ this.$t('black-is').split('{}')[0] }}<b><mk-user-name :user="game.user1"/></b>{{ this.$t('black-is').split('{}')[1] }}</form-radio>
<form-radio v-model="game.settings.bw" :value="2" @change="updateSettings">{{ this.$t('black-is').split('{}')[0] }}<b><mk-user-name :user="game.user2"/></b>{{ this.$t('black-is').split('{}')[1] }}</form-radio> <form-radio v-model="game.bw" :value="2" @change="updateSettings('bw')">{{ this.$t('black-is').split('{}')[0] }}<b><mk-user-name :user="game.user2"/></b>{{ this.$t('black-is').split('{}')[1] }}</form-radio>
</div> </div>
</div> </div>
@ -47,9 +47,9 @@
</header> </header>
<div> <div>
<ui-switch v-model="game.settings.isLlotheo" @change="updateSettings">{{ $t('is-llotheo') }}</ui-switch> <ui-switch v-model="game.isLlotheo" @change="updateSettings('isLlotheo')">{{ $t('is-llotheo') }}</ui-switch>
<ui-switch v-model="game.settings.loopedBoard" @change="updateSettings">{{ $t('looped-map') }}</ui-switch> <ui-switch v-model="game.loopedBoard" @change="updateSettings('loopedBoard')">{{ $t('looped-map') }}</ui-switch>
<ui-switch v-model="game.settings.canPutEverywhere" @change="updateSettings">{{ $t('can-put-everywhere') }}</ui-switch> <ui-switch v-model="game.canPutEverywhere" @change="updateSettings('canPutEverywhere')">{{ $t('can-put-everywhere') }}</ui-switch>
</div> </div>
</div> </div>
@ -159,8 +159,8 @@ export default Vue.extend({
this.connection.on('initForm', this.onInitForm); this.connection.on('initForm', this.onInitForm);
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
if (this.game.user1Id != this.$store.state.i.id && this.game.settings.form1) this.form = this.game.settings.form1; if (this.game.user1Id != this.$store.state.i.id && this.game.form1) this.form = this.game.form1;
if (this.game.user2Id != this.$store.state.i.id && this.game.settings.form2) this.form = this.game.settings.form2; if (this.game.user2Id != this.$store.state.i.id && this.game.form2) this.form = this.game.form2;
}, },
beforeDestroy() { beforeDestroy() {
@ -189,18 +189,19 @@ export default Vue.extend({
this.$forceUpdate(); this.$forceUpdate();
}, },
updateSettings() { updateSettings(key: string) {
this.connection.send('updateSettings', { this.connection.send('updateSettings', {
settings: this.game.settings key: key,
value: this.game[key]
}); });
}, },
onUpdateSettings(settings) { onUpdateSettings({ key, value }) {
this.game.settings = settings; this.game[key] = value;
if (this.game.settings.map == null) { if (this.game.map == null) {
this.mapName = null; this.mapName = null;
} else { } else {
const found = Object.values(maps).find(x => x.data.join('') == this.game.settings.map.join('')); const found = Object.values(maps).find(x => x.data.join('') == this.game.map.join(''));
this.mapName = found ? found.name : '-Custom-'; this.mapName = found ? found.name : '-Custom-';
} }
}, },
@ -224,27 +225,27 @@ export default Vue.extend({
onMapChange() { onMapChange() {
if (this.mapName == null) { if (this.mapName == null) {
this.game.settings.map = null; this.game.map = null;
} else { } else {
this.game.settings.map = Object.values(maps).find(x => x.name == this.mapName).data; this.game.map = Object.values(maps).find(x => x.name == this.mapName).data;
} }
this.$forceUpdate(); this.$forceUpdate();
this.updateSettings(); this.updateSettings();
}, },
onPixelClick(pos, pixel) { onPixelClick(pos, pixel) {
const x = pos % this.game.settings.map[0].length; const x = pos % this.game.map[0].length;
const y = Math.floor(pos / this.game.settings.map[0].length); const y = Math.floor(pos / this.game.map[0].length);
const newPixel = const newPixel =
pixel == ' ' ? '-' : pixel == ' ' ? '-' :
pixel == '-' ? 'b' : pixel == '-' ? 'b' :
pixel == 'b' ? 'w' : pixel == 'b' ? 'w' :
' '; ' ';
const line = this.game.settings.map[y].split(''); const line = this.game.map[y].split('');
line[x] = newPixel; line[x] = newPixel;
this.$set(this.game.settings.map, y, line.join('')); this.$set(this.game.map, y, line.join(''));
this.$forceUpdate(); this.$forceUpdate();
this.updateSettings(); this.updateSettings('map');
} }
} }
}); });

View File

@ -106,7 +106,7 @@ export default Vue.extend({
async nav(game, actualNav = true) { async nav(game, actualNav = true) {
if (this.selfNav) { if (this.selfNav) {
// //
if (game != null && (game.settings == null || game.settings.map == null)) { if (game != null && game.map == null) {
game = await this.$root.api('games/reversi/games/show', { game = await this.$root.api('games/reversi/games/show', {
gameId: game.id gameId: game.id
}); });

View File

@ -2,7 +2,7 @@
<div class="nhasjydimbopojusarffqjyktglcuxjy" v-if="meta"> <div class="nhasjydimbopojusarffqjyktglcuxjy" v-if="meta">
<div class="banner" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"></div> <div class="banner" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"></div>
<h1>{{ meta.name }}</h1> <h1>{{ meta.name || 'Misskey' }}</h1>
<p v-html="meta.description || this.$t('@.about')"></p> <p v-html="meta.description || this.$t('@.about')"></p>
<router-link to="/">{{ $t('start') }}</router-link> <router-link to="/">{{ $t('start') }}</router-link>
</div> </div>

View File

@ -52,7 +52,7 @@ export default Vue.extend({
} }
return { return {
'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent', 'background-color': this.image.properties.avgColor || 'transparent',
'background-image': url 'background-image': url
}; };
} }

View File

@ -33,7 +33,7 @@ export default Vue.extend({
}, },
computed: { computed: {
canonical(): string { canonical(): string {
return `@${this.username}@${toUnicode(this.host)}`; return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
}, },
isMe(): boolean { isMe(): boolean {
return this.$store.getters.isSignedIn && this.canonical.toLowerCase() === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase(); return this.$store.getters.isSignedIn && this.canonical.toLowerCase() === `@${this.$store.state.i.username}@${toUnicode(localHost)}`.toLowerCase();

View File

@ -11,7 +11,7 @@
<div class="file" v-if="message.file"> <div class="file" v-if="message.file">
<a :href="message.file.url" target="_blank" :title="message.file.name"> <a :href="message.file.url" target="_blank" :title="message.file.name">
<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name" <img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"
:style="{ backgroundColor: message.file.properties.avgColor && message.file.properties.avgColor.length == 3 ? `rgb(${message.file.properties.avgColor.join(',')})` : 'transparent' }"/> :style="{ backgroundColor: message.file.properties.avgColor || 'transparent' }"/>
<p v-else>{{ message.file.name }}</p> <p v-else>{{ message.file.name }}</p>
</a> </a>
</div> </div>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="mk-poll" :data-done="closed || isVoted"> <div class="mk-poll" :data-done="closed || isVoted">
<ul> <ul>
<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!closed && !isVoted ? $t('vote-to').replace('{}', choice.text) : ''"> <li v-for="(choice, i) in poll.choices" :key="i" @click="vote(i)" :class="{ voted: choice.voted }" :title="!closed && !isVoted ? $t('vote-to').replace('{}', choice.text) : ''">
<div class="backdrop" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div> <div class="backdrop" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div>
<span> <span>
<template v-if="choice.isVoted"><fa icon="check"/></template> <template v-if="choice.isVoted"><fa icon="check"/></template>
@ -82,12 +82,6 @@ export default Vue.extend({
noteId: this.note.id, noteId: this.note.id,
choice: id choice: id
}).then(() => { }).then(() => {
for (const c of this.poll.choices) {
if (c.id == id) {
c.votes++;
Vue.set(c, 'isVoted', true);
}
}
if (!this.showResult) this.showResult = !this.poll.multiple; if (!this.showResult) this.showResult = !this.poll.multiple;
}); });
} }

View File

@ -0,0 +1,139 @@
<template>
<div class="skeikyzd" v-show="files.length != 0">
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id" @click="showFileMenu(file, $event)" @contextmenu.prevent="showFileMenu(file, $event)">
<x-file-thumbnail :data-id="file.id" class="thumbnail" :file="file" fit="cover"/>
<img class="remove" @click.stop="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
<div class="sensitive" v-if="file.isSensitive">
<fa class="icon" :icon="faExclamationTriangle"/>
</div>
</div>
</x-draggable>
<p class="remain">{{ 4 - files.length }}/4</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import * as XDraggable from 'vuedraggable';
import XMenu from '../../../common/views/components/menu.vue';
import { faTimesCircle, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import XFileThumbnail from './drive-file-thumbnail.vue'
export default Vue.extend({
i18n: i18n('common/views/components/post-form-attaches.vue'),
components: {
XDraggable,
XFileThumbnail
},
props: {
files: {
type: Object,
required: true
},
detachMediaFn: {
type: Object,
required: false
}
},
data() {
return {
faExclamationTriangle
};
},
methods: {
detachMedia(id) {
if (this.detachMediaFn) this.detachMediaFn(id)
else if (this.$parent.detachMedia) this.$parent.detachMedia(id)
},
toggleSensitive(file) {
this.$root.api('drive/files/update', {
fileId: file.id,
isSensitive: !file.isSensitive
}).then(() => {
file.isSensitive = !file.isSensitive;
});
},
showFileMenu(file, ev: MouseEvent) {
this.$root.new(XMenu, {
items: [{
type: 'item',
text: file.isSensitive ? this.$t('unmark-as-sensitive') : this.$t('mark-as-sensitive'),
icon: file.isSensitive ? faEyeSlash : faEye,
action: () => { this.toggleSensitive(file) }
}, {
type: 'item',
text: this.$t('attach-cancel'),
icon: faTimesCircle,
action: () => { this.detachMedia(file.id) }
}],
source: ev.currentTarget || ev.target
});
}
}
});
</script>
<style lang="stylus" scoped>
.skeikyzd
padding 4px
> .files
display flex
flex-wrap wrap
> div
width 64px
height 64px
margin 4px
cursor move
&:hover > .remove
display block
> .thumbnail
width 100%
height 100%
z-index 1
color var(--text)
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
z-index 1000
> .sensitive
display flex
position absolute
width 64px
height 64px
top 0
left 0
z-index 2
background rgba(17, 17, 17, .7)
color #fff
> .icon
margin auto
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color var(--primaryAlpha04)
</style>

View File

@ -20,7 +20,7 @@ export default Vue.extend({
}, },
computed: { computed: {
reactions(): any { reactions(): any {
return this.note.reactionCounts; return this.note.reactions;
}, },
isMe(): boolean { isMe(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId; return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;

View File

@ -2,7 +2,7 @@
<ui-card> <ui-card>
<template #title><fa :icon="['far', 'bell']"/> {{ $t('title') }}</template> <template #title><fa :icon="['far', 'bell']"/> {{ $t('title') }}</template>
<section> <section>
<ui-switch v-model="$store.state.i.settings.autoWatch" @change="onChangeAutoWatch"> <ui-switch v-model="$store.state.i.autoWatch" @change="onChangeAutoWatch">
{{ $t('auto-watch') }}<template #desc>{{ $t('auto-watch-desc') }}</template> {{ $t('auto-watch') }}<template #desc>{{ $t('auto-watch-desc') }}</template>
</ui-switch> </ui-switch>
<section> <section>

View File

@ -158,14 +158,14 @@ export default Vue.extend({
computed: { computed: {
alwaysMarkNsfw: { alwaysMarkNsfw: {
get() { return this.$store.state.i.settings.alwaysMarkNsfw; }, get() { return this.$store.state.i.alwaysMarkNsfw; },
set(value) { this.$root.api('i/update', { alwaysMarkNsfw: value }); } set(value) { this.$root.api('i/update', { alwaysMarkNsfw: value }); }
}, },
bannerStyle(): any { bannerStyle(): any {
if (this.$store.state.i.bannerUrl == null) return {}; if (this.$store.state.i.bannerUrl == null) return {};
return { return {
backgroundColor: this.$store.state.i.bannerColor && this.$store.state.i.bannerColor.length == 3 ? `rgb(${ this.$store.state.i.bannerColor.join(',') })` : null, backgroundColor: this.$store.state.i.bannerColor,
backgroundImage: `url(${ this.$store.state.i.bannerUrl })` backgroundImage: `url(${ this.$store.state.i.bannerUrl })`
}; };
}, },
@ -178,10 +178,10 @@ export default Vue.extend({
this.email = this.$store.state.i.email; this.email = this.$store.state.i.email;
this.name = this.$store.state.i.name; this.name = this.$store.state.i.name;
this.username = this.$store.state.i.username; this.username = this.$store.state.i.username;
this.location = this.$store.state.i.profile.location; this.location = this.$store.state.i.location;
this.description = this.$store.state.i.description; this.description = this.$store.state.i.description;
this.lang = this.$store.state.i.lang; this.lang = this.$store.state.i.lang;
this.birthday = this.$store.state.i.profile.birthday; this.birthday = this.$store.state.i.birthday;
this.avatarId = this.$store.state.i.avatarId; this.avatarId = this.$store.state.i.avatarId;
this.bannerId = this.$store.state.i.bannerId; this.bannerId = this.$store.state.i.bannerId;
this.isCat = this.$store.state.i.isCat; this.isCat = this.$store.state.i.isCat;

View File

@ -130,20 +130,6 @@ import * as tinycolor from 'tinycolor2';
import * as JSON5 from 'json5'; import * as JSON5 from 'json5';
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons'; import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
//
function convertOldThemedefinition(t) {
const t2 = {
id: t.meta.id,
name: t.meta.name,
author: t.meta.author,
base: t.meta.base,
vars: t.meta.vars,
props: t
};
delete t2.props.meta;
return t2;
}
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/theme.vue'), i18n: i18n('common/views/components/theme.vue'),
components: { components: {
@ -231,20 +217,6 @@ export default Vue.extend({
} }
}, },
beforeCreate() {
// migrate old theme definitions
//
this.$store.commit('device/set', {
key: 'themes', value: this.$store.state.device.themes.map(t => {
if (t.id == null) {
return convertOldThemedefinition(t);
} else {
return t;
}
})
});
},
methods: { methods: {
install(code) { install(code) {
let theme; let theme;
@ -259,11 +231,6 @@ export default Vue.extend({
return; return;
} }
//
if (theme.id == null && theme.meta != null) {
theme = convertOldThemedefinition(theme);
}
if (theme.id == null) { if (theme.id == null) {
this.$root.dialog({ this.$root.dialog({
type: 'error', type: 'error',

View File

@ -4,7 +4,7 @@
<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill"> <ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill">
<span>{{ $t('invitation-code') }}</span> <span>{{ $t('invitation-code') }}</span>
<template #prefix><fa icon="id-card-alt"/></template> <template #prefix><fa icon="id-card-alt"/></template>
<template #desc v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainer.email)"></template> <template #desc v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainerEmail)"></template>
</ui-input> </ui-input>
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill"> <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill">
<span>{{ $t('username') }}</span> <span>{{ $t('username') }}</span>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="cudqjmnl"> <div class="cudqjmnl">
<ui-card> <ui-card>
<template #title><fa :icon="faList"/> {{ list.title }}</template> <template #title><fa :icon="faList"/> {{ list.name }}</template>
<section> <section>
<ui-button @click="rename"><fa :icon="faICursor"/> {{ $t('rename') }}</ui-button> <ui-button @click="rename"><fa :icon="faICursor"/> {{ $t('rename') }}</ui-button>
@ -75,7 +75,7 @@ export default Vue.extend({
this.$root.dialog({ this.$root.dialog({
title: this.$t('rename'), title: this.$t('rename'),
input: { input: {
default: this.list.title default: this.list.name
} }
}).then(({ canceled, result: title }) => { }).then(({ canceled, result: title }) => {
if (canceled) return; if (canceled) return;
@ -89,7 +89,7 @@ export default Vue.extend({
del() { del() {
this.$root.dialog({ this.$root.dialog({
type: 'warning', type: 'warning',
text: this.$t('delete-are-you-sure').replace('$1', this.list.title), text: this.$t('delete-are-you-sure').replace('$1', this.list.name),
showCancelButton: true showCancelButton: true
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; if (canceled) return;

View File

@ -51,7 +51,7 @@ export default Vue.extend({
fetchingMoreUsers: false, fetchingMoreUsers: false,
us: [], us: [],
inited: false, inited: false,
cursor: null more: false
}; };
}, },

View File

@ -73,7 +73,7 @@ export default Vue.extend({
title: t, title: t,
select: { select: {
items: lists.map(list => ({ items: lists.map(list => ({
value: list.id, text: list.title value: list.id, text: list.name
})) }))
}, },
showCancelButton: true showCancelButton: true

View File

@ -28,12 +28,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: notes[notes.length - 1].id more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}) })

View File

@ -37,12 +37,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: notes[notes.length - 1].id more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}) })

View File

@ -28,7 +28,7 @@ export default Vue.extend({
data() { data() {
return { return {
connection: null, connection: null,
makePromise: cursor => this.$root.api('notes/search_by_tag', { makePromise: cursor => this.$root.api('notes/search-by-tag', {
limit: fetchLimit + 1, limit: fetchLimit + 1,
untilId: cursor ? cursor : undefined, untilId: cursor ? cursor : undefined,
withFiles: this.mediaOnly, withFiles: this.mediaOnly,
@ -41,12 +41,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: notes[notes.length - 1].id more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}) })

View File

@ -41,12 +41,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: notes[notes.length - 1].id more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}) })

View File

@ -27,12 +27,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: notes[notes.length - 1].id more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}) })

View File

@ -26,8 +26,8 @@
</template> </template>
</component> </component>
<footer v-if="cursor != null"> <footer v-if="more">
<button @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> <button @click="fetchMore()" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template> <template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template> <template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button> </button>
@ -61,7 +61,7 @@ export default Vue.extend({
fetching: true, fetching: true,
moreFetching: false, moreFetching: false,
inited: false, inited: false,
cursor: null more: false
}; };
}, },
@ -119,7 +119,7 @@ export default Vue.extend({
this.notes = x; this.notes = x;
} else { } else {
this.notes = x.notes; this.notes = x.notes;
this.cursor = x.cursor; this.more = x.more;
} }
this.inited = true; this.inited = true;
this.fetching = false; this.fetching = false;
@ -129,12 +129,12 @@ export default Vue.extend({
}); });
}, },
more() { fetchMore() {
if (this.cursor == null || this.moreFetching) return; if (!this.more || this.moreFetching) return;
this.moreFetching = true; this.moreFetching = true;
this.makePromise(this.cursor).then(x => { this.makePromise(this.notes[this.notes.length - 1].id).then(x => {
this.notes = this.notes.concat(x.notes); this.notes = this.notes.concat(x.notes);
this.cursor = x.cursor; this.more = x.more;
this.moreFetching = false; this.moreFetching = false;
}, e => { }, e => {
this.moreFetching = false; this.moreFetching = false;
@ -157,7 +157,7 @@ export default Vue.extend({
// 稿 // 稿
if (this.notes.length >= displayLimit) { if (this.notes.length >= displayLimit) {
this.notes = this.notes.slice(0, displayLimit); this.notes = this.notes.slice(0, displayLimit);
this.cursor = this.notes[this.notes.length - 1].id this.more = true;
} }
} else { } else {
this.queue.push(note); this.queue.push(note);
@ -181,7 +181,7 @@ export default Vue.extend({
}, },
onBottom() { onBottom() {
this.more(); this.fetchMore();
} }
} }
}); });

View File

@ -62,7 +62,7 @@
</div> </div>
</div> </div>
<div class="notification poll_vote" v-if="notification.type == 'poll_vote'"> <div class="notification pollVote" v-if="notification.type == 'pollVote'">
<mk-avatar class="avatar" :user="notification.user"/> <mk-avatar class="avatar" :user="notification.user"/>
<div> <div>
<header> <header>

View File

@ -39,7 +39,7 @@ export default Vue.extend({
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}) })

View File

@ -82,7 +82,7 @@ export default Vue.extend({
case 'local': return this.$t('@deck.local'); case 'local': return this.$t('@deck.local');
case 'hybrid': return this.$t('@deck.hybrid'); case 'hybrid': return this.$t('@deck.hybrid');
case 'global': return this.$t('@deck.global'); case 'global': return this.$t('@deck.global');
case 'list': return this.column.list.title; case 'list': return this.column.list.name;
case 'hashtag': return this.$store.state.settings.tagTimelines.find(x => x.id == this.column.tagTlId).title; case 'hashtag': return this.$store.state.settings.tagTimelines.find(x => x.id == this.column.tagTlId).title;
} }
} }

View File

@ -85,12 +85,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: notes[notes.length - 1].id more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}); });

View File

@ -95,12 +95,12 @@ export default Vue.extend({
notes.pop(); notes.pop();
return { return {
notes: notes, notes: notes,
cursor: new Date(notes[notes.length - 1].createdAt).getTime() more: true
}; };
} else { } else {
return { return {
notes: notes, notes: notes,
cursor: null more: false
}; };
} }
}); });

View File

@ -8,7 +8,7 @@
<div class="is-remote" v-if="user.host != null"> <div class="is-remote" v-if="user.host != null">
<details> <details>
<summary><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}</summary> <summary><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}</summary>
<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a> <a :href="user.url" target="_blank">{{ $t('@.view-on-remote') }}</a>
</details> </details>
</div> </div>
<header :style="bannerStyle"> <header :style="bannerStyle">
@ -88,7 +88,7 @@ export default Vue.extend({
if (this.user == null) return {}; if (this.user == null) return {};
if (this.user.bannerUrl == null) return {}; if (this.user.bannerUrl == null) return {};
return { return {
backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null, backgroundColor: this.user.bannerColor,
backgroundImage: `url(${ this.user.bannerUrl })` backgroundImage: `url(${ this.user.bannerUrl })`
}; };
}, },

View File

@ -106,16 +106,6 @@ export default Vue.extend({
value: deck value: deck
}); });
} }
//
if (this.$store.state.device.deck != null && this.$store.state.device.deck.layout == null) {
this.$store.commit('device/set', {
key: 'deck',
value: Object.assign({}, this.$store.state.device.deck, {
layout: this.$store.state.device.deck.columns.map(c => [c.id])
})
});
}
}, },
mounted() { mounted() {
@ -199,7 +189,7 @@ export default Vue.extend({
title: this.$t('@deck.select-list'), title: this.$t('@deck.select-list'),
select: { select: {
items: lists.map(list => ({ items: lists.map(list => ({
value: list.id, text: list.title value: list.id, text: list.name
})) }))
}, },
showCancelButton: true showCancelButton: true

View File

@ -3,7 +3,7 @@
<ui-container :show-header="false" v-if="meta && stats"> <ui-container :show-header="false" v-if="meta && stats">
<div class="kpdsmpnk" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> <div class="kpdsmpnk" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
<div> <div>
<router-link to="/explore" class="title">{{ $t('explore', { host: meta.name }) }}</router-link> <router-link to="/explore" class="title">{{ $t('explore', { host: meta.name || 'Misskey' }) }}</router-link>
<span>{{ $t('users-info', { users: num(stats.originalUsersCount) }) }}</span> <span>{{ $t('users-info', { users: num(stats.originalUsersCount) }) }}</span>
</div> </div>
</div> </div>
@ -13,8 +13,8 @@
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template> <template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
<div class="vxjfqztj"> <div class="vxjfqztj">
<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</router-link> <router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.name}`" :key="'local:' + tag.name" class="local">{{ tag.name }}</router-link>
<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</router-link> <router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.name}`" :key="'remote:' + tag.name">{{ tag.name }}</router-link>
</div> </div>
</ui-container> </ui-container>

View File

@ -57,7 +57,7 @@ export default Vue.extend({
bannerStyle(): any { bannerStyle(): any {
if (this.user.bannerUrl == null) return {}; if (this.user.bannerUrl == null) return {};
return { return {
backgroundColor: this.user.bannerColor && this.user.bannerColor.length == 3 ? `rgb(${ this.user.bannerColor.join(',') })` : null, backgroundColor: this.user.bannerColor,
backgroundImage: `url(${ this.user.bannerUrl })` backgroundImage: `url(${ this.user.bannerUrl })`
}; };
} }

View File

@ -9,20 +9,30 @@ import Vue from 'vue';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
const fetchLimit = 30;
export default Vue.extend({ export default Vue.extend({
i18n: i18n(''), i18n: i18n(),
data() { data() {
return { return {
makePromise: cursor => this.$root.api('users/followers', { makePromise: cursor => this.$root.api('users/followers', {
...parseAcct(this.$route.params.user), ...parseAcct(this.$route.params.user),
limit: 30, limit: fetchLimit + 1,
cursor: cursor ? cursor : undefined untilId: cursor ? cursor : undefined,
}).then(x => { }).then(followings => {
if (followings.length == fetchLimit + 1) {
followings.pop();
return { return {
users: x.users, users: followings.map(following => following.follower),
cursor: x.next cursor: followings[followings.length - 1].id
}; };
} else {
return {
users: followings.map(following => following.follower),
more: false
};
}
}), }),
}; };
}, },

View File

@ -7,19 +7,32 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import i18n from '../../../i18n';
const fetchLimit = 30;
export default Vue.extend({ export default Vue.extend({
i18n: i18n(),
data() { data() {
return { return {
makePromise: cursor => this.$root.api('users/following', { makePromise: cursor => this.$root.api('users/following', {
...parseAcct(this.$route.params.user), ...parseAcct(this.$route.params.user),
limit: 30, limit: fetchLimit + 1,
cursor: cursor ? cursor : undefined untilId: cursor ? cursor : undefined,
}).then(x => { }).then(followings => {
if (followings.length == fetchLimit + 1) {
followings.pop();
return { return {
users: x.users, users: followings.map(following => following.followee),
cursor: x.next cursor: followings[followings.length - 1].id
}; };
} else {
return {
users: followings.map(following => following.followee),
more: false
};
}
}), }),
}; };
}, },

View File

@ -42,7 +42,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
this.$root.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.name = meta.name; this.name = meta.name || 'Misskey';
}); });
} }
}); });

View File

@ -21,14 +21,7 @@
<fa :icon="['far', 'laugh']"/> <fa :icon="['far', 'laugh']"/>
</button> </button>
</div> </div>
<div class="files" v-show="files.length != 0"> <x-post-form-attaches class="files" :files="files" :detachMediaFn="detachMedia"/>
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
</div>
<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/> <input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
<mk-uploader ref="uploader" @uploaded="attachMedia"/> <mk-uploader ref="uploader" @uploaded="attachMedia"/>
<footer> <footer>
@ -45,7 +38,7 @@
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
import * as XDraggable from 'vuedraggable'; import XPostFormAttaches from '../components/post-form-attaches.vue';
export default define({ export default define({
name: 'post-form', name: 'post-form',
@ -56,7 +49,7 @@ export default define({
i18n: i18n('desktop/views/widgets/post-form.vue'), i18n: i18n('desktop/views/widgets/post-form.vue'),
components: { components: {
XDraggable XPostFormAttaches
}, },
data() { data() {
@ -249,38 +242,6 @@ export default define({
& + .emoji & + .emoji
opacity 0.7 opacity 0.7
> .files
> div
padding 4px
&:after
content ""
display block
clear both
> div
float left
border solid 4px transparent
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> input[type=file] > input[type=file]
display none display none

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="info"> <div class="info">
<p>Maintainer: <b><a :href="'mailto:' + meta.maintainer.email" target="_blank">{{ meta.maintainer.name }}</a></b></p> <p>Maintainer: <b><a :href="'mailto:' + meta.maintainerEmail" target="_blank">{{ meta.maintainerName }}</a></b></p>
<p>Machine: {{ meta.machine }}</p> <p>Machine: {{ meta.machine }}</p>
<p>Node: {{ meta.node }}</p> <p>Node: {{ meta.node }}</p>
<p>Version: {{ meta.version }} </p> <p>Version: {{ meta.version }} </p>

View File

@ -60,7 +60,7 @@ export default Vue.extend({
return this.browser.selectedFiles.some(f => f.id == this.file.id); return this.browser.selectedFiles.some(f => f.id == this.file.id);
}, },
title(): string { title(): string {
return `${this.file.name}\n${this.file.type} ${Vue.filter('bytes')(this.file.datasize)}`; return `${this.file.name}\n${this.file.type} ${Vue.filter('bytes')(this.file.size)}`;
} }
}, },
methods: { methods: {
@ -139,10 +139,10 @@ export default Vue.extend({
}, },
onThumbnailLoaded() { onThumbnailLoaded() {
if (this.file.properties.avgColor && this.file.properties.avgColor.length == 3) { if (this.file.properties.avgColor) {
anime({ anime({
targets: this.$refs.thumbnail, targets: this.$refs.thumbnail,
backgroundColor: `rgba(${this.file.properties.avgColor.join(',')}, 0)`, backgroundColor: this.file.properties.avgColor.replace('255)', '0)'),
duration: 100, duration: 100,
easing: 'linear' easing: 'linear'
}); });

View File

@ -769,7 +769,6 @@ export default Vue.extend({
> .mk-uploader > .mk-uploader
height 100px height 100px
padding 16px padding 16px
background #fff
> input > input
display none display none

View File

@ -54,11 +54,11 @@
</button> </button>
<button v-if="!isMyNote && appearNote.myReaction == null" class="reactionButton button" @click="react()" ref="reactButton" :title="$t('add-reaction')"> <button v-if="!isMyNote && appearNote.myReaction == null" class="reactionButton button" @click="react()" ref="reactButton" :title="$t('add-reaction')">
<fa icon="plus"/> <fa icon="plus"/>
<p class="count" v-if="Object.values(appearNote.reactionCounts).some(x => x)">{{ Object.values(appearNote.reactionCounts).reduce((a, c) => a + c, 0) }}</p> <p class="count" v-if="Object.values(appearNote.reactions).some(x => x)">{{ Object.values(appearNote.reactions).reduce((a, c) => a + c, 0) }}</p>
</button> </button>
<button v-if="!isMyNote && appearNote.myReaction != null" class="reactionButton reacted button" @click="undoReact(appearNote)" ref="reactButton" :title="$t('undo-reaction')"> <button v-if="!isMyNote && appearNote.myReaction != null" class="reactionButton reacted button" @click="undoReact(appearNote)" ref="reactButton" :title="$t('undo-reaction')">
<fa icon="minus"/> <fa icon="minus"/>
<p class="count" v-if="Object.values(appearNote.reactionCounts).some(x => x)">{{ Object.values(appearNote.reactionCounts).reduce((a, c) => a + c, 0) }}</p> <p class="count" v-if="Object.values(appearNote.reactions).some(x => x)">{{ Object.values(appearNote.reactions).reduce((a, c) => a + c, 0) }}</p>
</button> </button>
<button @click="menu()" ref="menuButton" class="button"> <button @click="menu()" ref="menuButton" class="button">
<fa icon="ellipsis-h"/> <fa icon="ellipsis-h"/>

View File

@ -25,8 +25,8 @@
</template> </template>
</component> </component>
<footer v-if="cursor != null"> <footer v-if="more">
<button @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }"> <button @click="fetchMore()" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template> <template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template> <template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button> </button>
@ -58,7 +58,7 @@ export default Vue.extend({
fetching: true, fetching: true,
moreFetching: false, moreFetching: false,
inited: false, inited: false,
cursor: null more: false
}; };
}, },
@ -112,7 +112,7 @@ export default Vue.extend({
this.notes = x; this.notes = x;
} else { } else {
this.notes = x.notes; this.notes = x.notes;
this.cursor = x.cursor; this.more = x.more;
} }
this.inited = true; this.inited = true;
this.fetching = false; this.fetching = false;
@ -122,12 +122,12 @@ export default Vue.extend({
}); });
}, },
more() { fetchMore() {
if (this.cursor == null || this.moreFetching) return; if (!this.more || this.moreFetching) return;
this.moreFetching = true; this.moreFetching = true;
this.makePromise(this.cursor).then(x => { this.makePromise(this.notes[this.notes.length - 1].id).then(x => {
this.notes = this.notes.concat(x.notes); this.notes = this.notes.concat(x.notes);
this.cursor = x.cursor; this.more = x.more;
this.moreFetching = false; this.moreFetching = false;
}, e => { }, e => {
this.moreFetching = false; this.moreFetching = false;
@ -157,7 +157,7 @@ export default Vue.extend({
// 稿 // 稿
if (this.notes.length >= displayLimit) { if (this.notes.length >= displayLimit) {
this.notes = this.notes.slice(0, displayLimit); this.notes = this.notes.slice(0, displayLimit);
this.cursor = this.notes[this.notes.length - 1].id this.more = true;
} }
} else { } else {
this.queue.push(note); this.queue.push(note);
@ -183,7 +183,7 @@ export default Vue.extend({
if (this.$store.state.settings.fetchOnScroll !== false) { if (this.$store.state.settings.fetchOnScroll !== false) {
const current = window.scrollY + window.innerHeight; const current = window.scrollY + window.innerHeight;
if (current > document.body.offsetHeight - 8) this.more(); if (current > document.body.offsetHeight - 8) this.fetchMore();
} }
} }
} }

View File

@ -110,7 +110,7 @@
</div> </div>
</template> </template>
<template v-if="notification.type == 'poll_vote'"> <template v-if="notification.type == 'pollVote'">
<mk-avatar class="avatar" :user="notification.user"/> <mk-avatar class="avatar" :user="notification.user"/>
<div class="text"> <div class="text">
<p><fa icon="chart-pie"/><a :href="notification.user | userPage" v-user-preview="notification.user.id"> <p><fa icon="chart-pie"/><a :href="notification.user | userPage" v-user-preview="notification.user.id">

View File

@ -27,15 +27,7 @@
<button class="emoji" @click="emoji" ref="emoji"> <button class="emoji" @click="emoji" ref="emoji">
<fa :icon="['far', 'laugh']"/> <fa :icon="['far', 'laugh']"/>
</button> </button>
<div class="files" :class="{ with: poll }" v-show="files.length != 0"> <x-post-form-attaches class="files" :class="{ with: poll }" :files="files"/>
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
<p class="remain">{{ 4 - files.length }}/4</p>
</div>
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="onPollUpdate()"/> <mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="onPollUpdate()"/>
</div> </div>
</div> </div>
@ -65,7 +57,6 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
import * as XDraggable from 'vuedraggable';
import getFace from '../../../common/scripts/get-face'; import getFace from '../../../common/scripts/get-face';
import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue'; import MkVisibilityChooser from '../../../common/views/components/visibility-chooser.vue';
import { parse } from '../../../../../mfm/parse'; import { parse } from '../../../../../mfm/parse';
@ -74,13 +65,14 @@ import { erase, unique } from '../../../../../prelude/array';
import { length } from 'stringz'; import { length } from 'stringz';
import { toASCII } from 'punycode'; import { toASCII } from 'punycode';
import extractMentions from '../../../../../misc/extract-mentions'; import extractMentions from '../../../../../misc/extract-mentions';
import XPostFormAttaches from '../../../common/views/components/post-form-attaches.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('desktop/views/components/post-form.vue'), i18n: i18n('desktop/views/components/post-form.vue'),
components: { components: {
XDraggable, MkVisibilityChooser,
MkVisibilityChooser XPostFormAttaches
}, },
props: { props: {
@ -513,7 +505,7 @@ export default Vue.extend({
kao() { kao() {
this.text += getFace(); this.text += getFace();
} },
} }
}); });
</script> </script>
@ -618,46 +610,6 @@ export default Vue.extend({
border-bottom solid 1px var(--primaryAlpha01) !important border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 0 border-radius 0
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color var(--primaryAlpha04)
> div
padding 4px
&:after
content ""
display block
clear both
> div
float left
border solid 4px transparent
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .mk-poll-editor > .mk-poll-editor
background var(--desktopPostFormTextareaBg) background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01) border solid 1px var(--primaryAlpha01)

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