Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
d4c7ca76ac | |||
1a6aae944f | |||
71e0241c94 | |||
d838ef5b76 | |||
d90a5c9154 | |||
9b79a411e0 | |||
b8e0ec9edc | |||
57009057ae | |||
5db7b2e193 | |||
d994c84901 | |||
febfb97bb4 | |||
a6c5e62923 | |||
ac0390fec3 | |||
97b99867f2 | |||
a55d5516a6 | |||
b679163d01 | |||
76edcdbe45 | |||
d8d51519c4 | |||
3446969121 | |||
0e0c35a701 | |||
c9a6c9e20a | |||
3d2b982a94 | |||
6157d8331b | |||
3d0fc09fae | |||
b975751710 | |||
4530d40537 | |||
94e3ac9b72 | |||
e13fe97ebb | |||
4ad7632113 | |||
0709cac97f | |||
7dd4180fba | |||
558213490a | |||
78a69e3da8 | |||
140c78e5a7 | |||
a8e18e0e22 | |||
2a8bb23625 | |||
936a4d1bc4 | |||
69c34e8d00 | |||
1a2a190828 | |||
251cf1d76f | |||
52774bbe64 | |||
68a6758302 | |||
13e43a4f74 | |||
b7d62d09ec | |||
321ec18173 | |||
a44ac3306e | |||
951288ecf0 | |||
cc8a7dd588 | |||
813c52f51e | |||
be3298639d | |||
e8d2959717 | |||
e7680e08eb | |||
bd792d7661 | |||
4920983f23 | |||
2756f553c6 | |||
fc52e95ad0 | |||
5d1d6bc028 | |||
b106acac91 | |||
a5071db864 | |||
bae874eb45 | |||
60da17940d | |||
385eeed732 | |||
2e908758d8 | |||
a164f8ad95 | |||
372138dfea | |||
922a657c7e | |||
3409a51cca | |||
7174a55846 | |||
6656a59402 | |||
7612ead551 | |||
fa78fe665d | |||
c89aa7eb95 | |||
43f4c5b7cd | |||
2b6c566386 | |||
91ef328b6b | |||
5a9d9dc41d | |||
a48e503caa | |||
fe00cb9ad5 | |||
ed0fdaddbd | |||
893795a31d | |||
f1047f1ce5 | |||
9beddc941a | |||
3a6a01d2d6 | |||
e0e4bdbdbc | |||
b3daf79b6a | |||
81aa21915b | |||
5aadd80243 | |||
23350b1cbc | |||
daff0977cb | |||
c1e7d56ff8 | |||
61aef56f83 |
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,6 +1,39 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
10.89.1
|
||||
----------
|
||||
* リアクション数を表示するように
|
||||
* モバイル版でドライブのフォルダを削除できるように
|
||||
* ドキュメントの強化
|
||||
* プロフィールが更新できない場合がある問題を修正
|
||||
* UIの修正
|
||||
|
||||
10.89.0
|
||||
----------
|
||||
* APIのエラーの形式を統一
|
||||
* APIドキュメント刷新
|
||||
* /api/v1/instance/peers 復活
|
||||
* 「返信が遷移後も残り続ける問題を修正」([9beddc9](https://github.com/syuilo/misskey/commit/9beddc941a716f1322ae0b7d71d159edd642a399)) によって遷移前に返信が表示されなくなった問題を修正
|
||||
* デッキモードにてユーザーのプロフィールを連続で見たとき、アクティビティや画像が前のユーザーのもののまま表示される問題を修正
|
||||
|
||||
10.88.0
|
||||
----------
|
||||
* アカウントの削除を試験的に実装
|
||||
* デッキでメディア投稿のみ表示するオプションが機能していない問題を修正
|
||||
* デッキでユーザーを表示したときにタイムラインが残存する問題を修正
|
||||
* モバイルのユーザーページで、ユーザーAのタイムラインから他のユーザーBを選択してユーザーBのタイムラインに移動したとき、ユーザーAのタイムラインが残る問題を修正
|
||||
* ハイライトでミュートしているユーザーの投稿が含まれる問題を修正
|
||||
* 「みつける」でミュートしているユーザーが含まれる問題を修正
|
||||
* デザインの調整
|
||||
|
||||
10.87.5
|
||||
----------
|
||||
* モバイル版でも連携サービスを表示するように
|
||||
* webfingerのacceptが反映されない問題を修正
|
||||
* 返信が遷移後も残り続ける問題を修正
|
||||
* デザインの調整
|
||||
|
||||
10.87.4
|
||||
----------
|
||||
* フォローリクエストを許可するときにエラーになる問題を修正
|
||||
|
@ -106,7 +106,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/3?token-time=2145916800&token-hash=c8HeVqLtmdgH-gSBJg8i10gmOcwllM87MDHeznl3el0%3D" alt="Melilot" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/3?token-time=2145916800&token-hash=LtV2lRi3L2jOWMLwccr9qWYfPrFlzIo2jYZHKzHEb6k%3D" alt="Xeltica" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/3384329/8b713330cb27404ea6e9fac50ff96efe/1?token-time=2145916800&token-hash=Ch3iF81ZGP0LMo894Y9ajpLisgtE91SnxtZE7fxsgrM%3D" alt="べすれい" width="100"></td>
|
||||
<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>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/weepjp">weep</a></td>
|
||||
@ -114,7 +113,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
<td><a href="https://www.patreon.com/Xeltica">Xeltica</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=3384329">べすれい</a></td>
|
||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
@ -152,7 +150,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Fri, 15 Feb 2019 19:12:06 UTC
|
||||
**Last updated:** Thu, 21 Feb 2019 16:10:06 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
BIN
assets/api-doc.png
Normal file
BIN
assets/api-doc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
@ -55,14 +55,9 @@ As root:
|
||||
*6.* Build Misskey
|
||||
----------------------------------------------------------------
|
||||
|
||||
Before build, you need to set `NODE_ENV` to `production`. like this:
|
||||
* Linux: `export NODE_ENV=production`
|
||||
* Windows (PowerShell): `$env:NODE_ENV="production"`
|
||||
* Windows (CMD): `set NODE_ENV=production`
|
||||
|
||||
Build misskey with the following:
|
||||
|
||||
`npm run build`
|
||||
`NODE_ENV=production npm run build`
|
||||
|
||||
If you're on Debian, you will need to install the `build-essential`, `python` package.
|
||||
|
||||
@ -71,14 +66,14 @@ If you're still encountering errors about some modules, use node-gyp:
|
||||
1. `npm install -g node-gyp`
|
||||
2. `node-gyp configure`
|
||||
3. `node-gyp build`
|
||||
4. `npm run build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
|
||||
*7.* That is it.
|
||||
----------------------------------------------------------------
|
||||
Well done! Now, you have an environment that run to Misskey.
|
||||
|
||||
### Launch normally
|
||||
Just `npm start`. GLHF!
|
||||
Just `NODE_ENV=production npm start`. GLHF!
|
||||
|
||||
### Launch with systemd
|
||||
|
||||
@ -94,6 +89,7 @@ Type=simple
|
||||
User=misskey
|
||||
ExecStart=/usr/bin/npm start
|
||||
WorkingDirectory=/home/misskey/misskey
|
||||
Environment="NODE_ENV=production"
|
||||
TimeoutSec=60
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
@ -113,7 +109,7 @@ You can check if the service is running with `systemctl status misskey`.
|
||||
1. `git fetch`
|
||||
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||
3. `npm install`
|
||||
4. `npm run build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
5. Check [ChangeLog](../CHANGELOG.md) for migration information
|
||||
6. Restart your Misskey process to apply changes
|
||||
7. Enjoy
|
||||
|
@ -57,7 +57,7 @@ En root :
|
||||
|
||||
Construisez Misskey comme ceci :
|
||||
|
||||
`npm run build`
|
||||
`NODE_ENV=production npm run build`
|
||||
|
||||
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`.
|
||||
|
||||
@ -66,14 +66,14 @@ Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
|
||||
1. `npm install -g node-gyp`
|
||||
2. `node-gyp configure`
|
||||
3. `node-gyp build`
|
||||
4. `npm run build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
|
||||
*7.* C'est tout.
|
||||
----------------------------------------------------------------
|
||||
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
|
||||
|
||||
### Lancement conventionnel
|
||||
Lancez tout simplement `npm start`. Bonne chance et amusez-vous bien !
|
||||
Lancez tout simplement `NODE_ENV=production npm start`. Bonne chance et amusez-vous bien !
|
||||
|
||||
### Démarrage avec systemd
|
||||
|
||||
@ -89,6 +89,7 @@ Type=simple
|
||||
User=misskey
|
||||
ExecStart=/usr/bin/npm start
|
||||
WorkingDirectory=/home/misskey/misskey
|
||||
Environment="NODE_ENV=production"
|
||||
TimeoutSec=60
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
@ -108,7 +109,7 @@ Vous pouvez vérifier si le service a démarré en utilisant la commande `system
|
||||
1. `git fetch`
|
||||
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||
3. `npm install`
|
||||
4. `npm run build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
@ -62,14 +62,9 @@ adduser --disabled-password --disabled-login misskey
|
||||
*6.* Misskeyのビルド
|
||||
----------------------------------------------------------------
|
||||
|
||||
ビルドする前に、`NODE_ENV`を`production`にする必要があります。例:
|
||||
* Linux: `export NODE_ENV=production`
|
||||
* Windows (PowerShell): `$env:NODE_ENV="production"`
|
||||
* Windows (CMD): `set NODE_ENV=production`
|
||||
|
||||
次のコマンドでMisskeyをビルドしてください:
|
||||
|
||||
`npm run build`
|
||||
`NODE_ENV=production npm run build`
|
||||
|
||||
Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。
|
||||
|
||||
@ -77,14 +72,14 @@ Debianをお使いであれば、`build-essential`パッケージをインスト
|
||||
1. `npm install -g node-gyp`
|
||||
2. `node-gyp configure`
|
||||
3. `node-gyp build`
|
||||
4. `npm run build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
|
||||
*7.* 以上です!
|
||||
----------------------------------------------------------------
|
||||
お疲れ様でした。これでMisskeyを動かす準備は整いました。
|
||||
|
||||
### 通常起動
|
||||
`npm start`するだけです。GLHF!
|
||||
`NODE_ENV=production npm start`するだけです。GLHF!
|
||||
|
||||
### systemdを用いた起動
|
||||
1. systemdサービスのファイルを作成: `/etc/systemd/system/misskey.service`
|
||||
@ -99,6 +94,7 @@ Type=simple
|
||||
User=misskey
|
||||
ExecStart=/usr/bin/npm start
|
||||
WorkingDirectory=/home/misskey/misskey
|
||||
Environment="NODE_ENV=production"
|
||||
TimeoutSec=60
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
@ -119,7 +115,7 @@ CentOSで1024以下のポートを使用してMisskeyを使用する場合は`Ex
|
||||
1. `git fetch`
|
||||
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||
3. `npm install`
|
||||
4. `npm run build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
|
||||
|
||||
なにか問題が発生した場合は、`npm run clean`すると直る場合があります。
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "ゴミ箱"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "やり直す"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renoteに失敗しました"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "Papierkorb"
|
||||
drive: "Drive"
|
||||
messaging: "Unterhaltungen"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "Erneut versuchen"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Weitersagen fehlgeschlagen"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "Bist du dir sicher, dass du das reposten willst?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "Favoriten"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -8,14 +8,14 @@ common:
|
||||
about: "Thank you for finding Misskey. Misskey is a <b>decentralized microblogging platform</b> born on Earth. Since it exists within the Fediverse (a universe where various social media platforms are organized), it is mutually linked with other social media platforms. Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet?"
|
||||
intro:
|
||||
title: "What is Misskey?"
|
||||
about: "Misskey is an open-source, <b>decentralized microblogging service</b>. Sophisticated, fully customizable UI, varieties of reactions for posts, free file storage providing an integrated management system and other advanced features are available. In addition, Misskey connects to a network system called the “Fediverse”, that enables us to communicate with users on other SNSs. For example, when you post something, it will be sent not only to Misskey, but also to Mastodon, Osada and Pleroma. Just imagine that the planet is sending a radio transmission to another planet to communicate."
|
||||
about: "Misskey is an open-source, <b>decentralized microblogging software</b>. It has a sophisticated, fully customizable user interface, a variety of ways for expressing a reaction to posts, free file storage providing an integrated management system, and other advanced features are available. In addition, Misskey connects to a network system called the “Fediverse”, which enables us to communicate with users on other SNSs. For example, when you post something, it will be sent not only to Misskey users, but also those on Mastodon and Pleroma. Just imagine that the planet is sending a radio transmission to another planet, in order to communicate."
|
||||
features: "Features"
|
||||
rich-contents: "Post"
|
||||
rich-contents-desc: "Just post your idea, hot topics, and anything you want to share. You may want to decorate your words, attach your favorite pictures, send files, including videos, or create a poll - those are some of the things you can do with Misskey!"
|
||||
reaction: "Reactions"
|
||||
reaction-desc: "Easiest way to tell your emotions. Misskey allows you to add various type of reactions to other’s post. The emotional experience on Misskey will never be on other SNSs which only able to push “likes”."
|
||||
reaction-desc: "The easiest way to express your emotions. Misskey allows you to add various kinds of reactions to other's posts. The emotional experience on Misskey will never be on other SNSs, which are only able to push “likes”."
|
||||
ui: "Interface"
|
||||
ui-desc: "No UI fits for everyone. Therefore, Misskey has a highly customizable UI for your taste. Make your original home by editing, adjusting layouts of timeline and placing selectable widgets you can easily customize."
|
||||
ui-desc: "No single UI can suit everyone. Therefore, Misskey has a highly customizable UI for your tastes. You can make your home original by editing the layout of your timeline, and moving around selectable widgets that you can easily adjust to make this place your own."
|
||||
drive: "Drive"
|
||||
drive-desc: "Wanna post a picture you have already uploaded? Wish to organize, name and create a folder for your uploaded files? Misskey Drive is the best solution for you. Very easy to share your files online."
|
||||
outro: "Check Misskey-unique features by seeing them with your own eyes! If you feel like this instance is not for you, try other instances, as Misskey is a decentralized SNS, so that you can easily find your mates. Then, GLHF!"
|
||||
@ -28,7 +28,7 @@ common:
|
||||
load-more: "Load more"
|
||||
enter-password: "Please enter the Password"
|
||||
2fa: "Two-factor authentication"
|
||||
customize-home: "Customize your home layout"
|
||||
customize-home: "Customize home layout"
|
||||
featured-notes: "Featured notes"
|
||||
got-it: "Got it!"
|
||||
customization-tips:
|
||||
@ -58,9 +58,10 @@ common:
|
||||
trash: "Trash"
|
||||
drive: "Drive"
|
||||
messaging: "Talk"
|
||||
home: "Home"
|
||||
deck: "Deck"
|
||||
timeline: "Timeline"
|
||||
explore: "Discover"
|
||||
explore: "Explore"
|
||||
following: "Following"
|
||||
followers: "Followers"
|
||||
empty-timeline-info:
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "These post contents are mirrored."
|
||||
view-on-remote: "For completion, view it remotely."
|
||||
renoted-by: "Renoted by {user}"
|
||||
no-notes: "Without any notes"
|
||||
error:
|
||||
title: "Something happened :("
|
||||
retry: "Retry"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "List of muted accounts"
|
||||
blocking-list: "List of blocked accounts"
|
||||
export-requested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive."
|
||||
enter-password: "Please enter your password"
|
||||
danger-zone: "Cautious options"
|
||||
delete-account: "Remove the account"
|
||||
account-deleted: "The account has been deleted. It may take some time until all of the data disappears."
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "User"
|
||||
rename: "Rename list"
|
||||
@ -572,7 +578,7 @@ common/views/widgets/memo.vue:
|
||||
memo: "Write here!"
|
||||
save: "Save"
|
||||
common/views/widgets/slideshow.vue:
|
||||
folder-customize-mode: "To specify a folder, please exit customize mode"
|
||||
folder-customize-mode: "To specify a folder, please exit customization mode"
|
||||
folder: "Please click and specify a folder"
|
||||
no-image: "There is no image in this folder"
|
||||
common/views/widgets/tips.vue:
|
||||
@ -583,7 +589,7 @@ common/views/widgets/tips.vue:
|
||||
tips-line5: "You can upload files by dragging and dropping them to Drive."
|
||||
tips-line6: "You can move a folder by dragging it within the Drive."
|
||||
tips-line7: "You can move folders by dragging them within the Drive."
|
||||
tips-line8: "Home can be customized from the settings."
|
||||
tips-line8: "The Home layout can be customized from the settings."
|
||||
tips-line9: "Misskey is licensed under AGPLv3."
|
||||
tips-line10: "Using the Time Machine widget makes it easy to trace back to the past timeline."
|
||||
tips-line11: "You can pin posts to user page by clicking on \"...\""
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Failed to Repost"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "Do you want to Repost it?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "Without any notes"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}'s following"
|
||||
followers: "{user}'s follower"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "Following"
|
||||
followers: "Followers"
|
||||
is-bot: "This account is a Bot"
|
||||
no-description: "The user has not written their profile introduction"
|
||||
years-old: "{age} years old"
|
||||
year: "/"
|
||||
month: "/"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "Posts and replies"
|
||||
with-media: "Media"
|
||||
my-posts: "My posts"
|
||||
empty: "This user doesn't seem to have posted anything yet."
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "Message"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "What do you want to do? (Please enter a number): <1 → Upload a file | 2 → Upload a file from a URL | 3 → Create a folder | 4 → Change this folder's name | 5 → Move this folder | 6 → Delete this folder>"
|
||||
deletion-alert: "Sorry! Deleting a folder is not yet implemented."
|
||||
folder-name: "Folder name"
|
||||
root-rename-alert: "You're in the root; it can't be renamed because it's not a folder. Navigate to a folder you want to rename and try again."
|
||||
root-move-alert: "You're in the root; it can't be moved because it's not a folder. Navigate to a folder you want to move and try again."
|
||||
here-is-root: "Currently, you are on the root, not inside of any folder."
|
||||
url-prompt: "URL of the file you want to upload"
|
||||
uploading: "Upload requested. It may take a while for the upload to finish."
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "This post has been deleted"
|
||||
media-count: "{} media attached"
|
||||
poll: "Poll"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "No notes"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "Welcome back, "
|
||||
adjective: "Sir"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "Settings"
|
||||
admin: "Admin"
|
||||
about: "About Misskey"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "It seems this user hasn't posted anything yet."
|
||||
no-notes-with-media: "There are no notes with media attachments"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "Favorites"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "Global"
|
||||
mentions: "Mentions"
|
||||
messages: "Messages"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "Without any notes"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "No posts contains \"{q}\" found."
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "Frequent mentions"
|
||||
followers-you-know: "Followers you know"
|
||||
last-used-at: "Last active:"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "No notes"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "No photos"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "This timeline has been disabled by the server administrator."
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "Only media posts"
|
||||
is-media-view: "Media view"
|
||||
edit: "Options"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "Follows you"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "Papelera"
|
||||
drive: "Drive"
|
||||
messaging: "Conversación"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "やり直す"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Usuarios"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "La publicación ha fallado"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "¿Seguro qué quieres volver a publicarlo?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user} sigue a"
|
||||
followers: "Seguidores de {user}"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "Corbeille"
|
||||
drive: "Drive"
|
||||
messaging: "Conversations"
|
||||
home: "ホーム"
|
||||
deck: "Deck"
|
||||
timeline: "Fil"
|
||||
explore: "Découvrir"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "Ceci est une publication distante."
|
||||
view-on-remote: " Consulter le profil complet"
|
||||
renoted-by: "Renoté par {user}"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "Une erreur est survenue"
|
||||
retry: "Réessayer"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "Liste des comptes mis en sourdine"
|
||||
blocking-list: "Liste des comptes bloqués"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Utilisateur·rice"
|
||||
rename: "Renommer la liste"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "La renote a échoué"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "Êtes vous sûr de vouloir renote cette note?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "Sans aucune note"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user} suit"
|
||||
followers: "Abonné·e·s de {user}"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "Suit"
|
||||
followers: "Abonné·e·s"
|
||||
is-bot: "Ce compte est un Bot"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age} ans"
|
||||
year: "/"
|
||||
month: "/"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "Publications et réponses"
|
||||
with-media: "Média"
|
||||
my-posts: "Mes Messages"
|
||||
empty: "Cet·te utilisateur·rice n'a rien posté encore."
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "Messagerie"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "Que veux-tu faire ? (Entrez un nombre): <1 → Télécharger le fichier | 2 → Télécharger le fichier avec l'URL | 3 → Créer le dossier | 4 → Modifier le nom du dossier | 5 → Déplacer ce dossier | 6 → Supprimer ce dossier >"
|
||||
deletion-alert: "Désolé ! La suppression d’un dossier n’est pas encore implémentée."
|
||||
folder-name: "Nom du dossier"
|
||||
root-rename-alert: "L'emplacement actuel est la racine, pas le dossier, vous ne pouvez donc pas le renommer. Veuillez vous déplacer dans le dossier dont vous souhaitez modifier le nom."
|
||||
root-move-alert: "L'emplacement actuel est la racine, ce n'est pas un dossier et il ne peut pas être déplacé. Veuillez vous déplacer dans le dossier que vous souhaitez déplacer."
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "URL du fichier que vous souhaitez téléverser"
|
||||
uploading: "Envoi demandé. Le téléversement pourrait prendre un certain temps avant de s'achever."
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "cette publication a été supprimée"
|
||||
media-count: "{} médias attachés"
|
||||
poll: "Sondage"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "Pas de notes"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "Content de vous revoir ! "
|
||||
adjective: "M."
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "Réglages"
|
||||
admin: "Admin"
|
||||
about: "À propos de Misskey"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "Il semble que cet·te utilisateur·rice n’a rien publié pour le moment."
|
||||
no-notes-with-media: "Aucune note comprenant des médias"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "Favoris"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "Global"
|
||||
mentions: "Mentions"
|
||||
messages: "Messages"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "Sans aucune note"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "Aucune publication ayant pour hashtag « {q} » n’a été trouvée."
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "Mentions fréquentes"
|
||||
followers-you-know: "Abonné·e·s que vous connaissez"
|
||||
last-used-at: "Dernière connexion il y a"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "Pas de notes"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "Pas de photos"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "Ce fil a été désactivé par l'administrateur du serveur."
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "Les publications médias uniquement"
|
||||
is-media-view: "Vue média"
|
||||
edit: "Option"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "Vous suit"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "ゴミ箱"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "やり直す"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renoteに失敗しました"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -60,6 +60,7 @@ common:
|
||||
trash: "ゴミ箱"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -154,6 +155,9 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
turn-on-darkmode: "闇に飲まれる"
|
||||
turn-off-darkmode: "光あれ"
|
||||
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
@ -588,6 +592,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
@ -879,9 +887,6 @@ desktop/views/components/renote-form.vue:
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1518,7 +1523,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
@ -1560,8 +1564,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
|
||||
@ -1638,9 +1641,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1659,10 +1659,6 @@ mobile/views/components/ui.nav.vue:
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
|
||||
@ -1687,9 +1683,6 @@ mobile/views/pages/home.vue:
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
|
||||
@ -1796,9 +1789,6 @@ mobile/views/pages/user/home.vue:
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
|
||||
@ -1828,7 +1818,6 @@ deck:
|
||||
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
|
||||
deck/deck.user-column.vue:
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "ゴミ箱"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "ちゃんとした情報見せてや!"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が起こったわ"
|
||||
retry: "もっぺん"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renoteでけへん"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしてもええか?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotや"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿しとらんようや。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何すんの?(数字を入れてや): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "フォルダの削除は未実装やねん...。堪忍な!"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在おる場所はルートで、フォルダとちゃうから名前の変更はできへん。名前を変更したいフォルダに移動してからやってな。"
|
||||
root-move-alert: "現在おる場所はルートで、フォルダとちゃうから移動はできへん。移動したいフォルダに移動してからやってな。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "このURLのファイルをアップロードしたいねん"
|
||||
uploading: "アップロードをリクエストしたで。アップロードが完了するまで時間がかかるかも分からん、知らんけど。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿なんか無くなってもうたわ"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿はあらへん"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえり、"
|
||||
adjective: "はん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyってなんや?"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿しとらんようや。"
|
||||
no-notes-with-media: "メディア付き投稿はあらへん。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あんた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿はあらへんかった。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知っとるフォロワー"
|
||||
last-used-at: "最後いつ来た?"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はあらへん"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はあらへんで"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿だけや"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "휴지통"
|
||||
drive: "드라이브"
|
||||
messaging: "대화"
|
||||
home: "ホーム"
|
||||
deck: "덱"
|
||||
timeline: "타임라인"
|
||||
explore: "발견"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "이 글 정보는 복사본입니다."
|
||||
view-on-remote: "정확한 정보 보기"
|
||||
renoted-by: "{user}이(가) 리노트"
|
||||
no-notes: "글이 없습니다"
|
||||
error:
|
||||
title: "오류가 발생했습니다"
|
||||
retry: "다시 시도"
|
||||
@ -214,7 +216,7 @@ common/views/pages/explore.vue:
|
||||
recently-registered-users: "신규 사용자"
|
||||
popular-tags: "인기 태그"
|
||||
federated: "연합"
|
||||
explore: "{host}를 탐색"
|
||||
explore: "{host}을(를) 탐색"
|
||||
users-info: "현재 {users} 사용자가 등록되어 있습니다"
|
||||
common/views/components/user-list.vue:
|
||||
no-users: "사용자가 없습니다"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "뮤트"
|
||||
blocking-list: "차단"
|
||||
export-requested: "내보내기를 요청하였습니다. 이 작업은 시간이 걸릴 수 있습니다. 내보내기가 완료되면 드라이브에 파일이 추가됩니다."
|
||||
enter-password: "비밀번호를 입력하여 주십시오"
|
||||
danger-zone: "위험한 설정"
|
||||
delete-account: "계정 삭제"
|
||||
account-deleted: "계정이 삭제되었습니다. 데이터가 사라질 때까지 시간이 걸릴 수 있습니다."
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "사용자"
|
||||
rename: "리스트 이름 바꾸기"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "리노트에 실패하였습니다"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "이 글을 리노트하시겠습니까?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "글이 없습니다"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}의 팔로잉"
|
||||
followers: "{user}의 팔로워"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "팔로잉"
|
||||
followers: "팔로워"
|
||||
is-bot: "이 계정은 Bot입니다"
|
||||
no-description: "자기소개가 없습니다"
|
||||
years-old: "{age}세"
|
||||
year: "년"
|
||||
month: "월"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "글과 답글"
|
||||
with-media: "미디어"
|
||||
my-posts: "내 글"
|
||||
empty: "이 사용자는 아직 아무것도 게시하지 않은 것 같습니다."
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "메시지"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "무엇을 하시겠습니까? (숫자를 입력하여 주십시오): <1 → 파일 업로드 | 2 → 파일을 URL에서 업로드 | 3 → 폴더 만들기 | 4 → 이 폴더의 이름을 변경 | 5 → 현재 폴더 이동| 6 → 현재 폴더 삭제>"
|
||||
deletion-alert: "죄송합니다! 폴더 삭제는 아직 구현되지 않았습니다..."
|
||||
folder-name: "폴더 이름"
|
||||
root-rename-alert: "현재 위치가 루트이고, 폴더가 아니므로 이름을 변경할 수 없습니다. 이름을 바꾸고 싶은 폴더로 이동하여 주십시오."
|
||||
root-move-alert: "현재 위치가 루트이므로, 폴더가 아니므로 이동할 수 없습니다. 이동하고 싶은 폴더로 이동하여 주십시오."
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "업로드 하려는 파일의 URL"
|
||||
uploading: "업로드를 요청하였습니다. 업로드가 완료될 때까지 시간이 소요될 수 있습니다."
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "이 글은 삭제되었습니다"
|
||||
media-count: "{}개의 미디어"
|
||||
poll: "투표"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "글이 없습니다"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "돌아오신 걸 환영합니다."
|
||||
adjective: "님"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "설정"
|
||||
admin: "관리"
|
||||
about: "Misskey에 대하여"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "이 사용자는 작성한 글이 없는 것 같습니다."
|
||||
no-notes-with-media: "미디어가 첨부된 글이 없습니다."
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "즐겨찾기"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "글로벌"
|
||||
mentions: "받은 멘션"
|
||||
messages: "메시지"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "글이 없습니다"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "해시태그 \"{q}\"가 붙은 글을 찾을 수 없습니다."
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "자주 언급되는 사용자"
|
||||
followers-you-know: "아는 사람의 팔로워"
|
||||
last-used-at: "마지막 로그인"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "글이 없습니다"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "사진이 없습니다"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "서버 운영자에 의해 이 타임라인이 사용할 수 없도록 설정되어 있습니다."
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "미디어가 달린 글만"
|
||||
is-media-view: "미디어 보기"
|
||||
edit: "옵션"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "당신을 팔로우합니다"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "ゴミ箱"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "やり直す"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renote mislukt"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "Weet je zeker dat je deze notitie wilt renoten?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "Berichten en antwoorden"
|
||||
with-media: "Media"
|
||||
my-posts: "私の投稿"
|
||||
empty: "Deze gebruiker heeft nog niks geplaatst."
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "Gesprekken"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{} media"
|
||||
poll: "Peiling"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "Geen notities"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "Instellingen"
|
||||
admin: "管理"
|
||||
about: "Over Misskey"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "Het lijkt erop dat deze gebruiker nog niks heeft geplaatst"
|
||||
no-notes-with-media: "Er zijn geen notities met bijgevoegde media"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "Volgers die je kent"
|
||||
last-used-at: "Laatst actief"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "Geen notities"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "Geen foto's"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "Papirkurv"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "やり直す"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renoteに失敗しました"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "Følger"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "Innlegg og svar"
|
||||
with-media: "Media"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "Melding"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "Mr."
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "Innstillinger"
|
||||
admin: "Admin"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "Favoritter"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "Globalt"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -9,10 +9,10 @@ common:
|
||||
intro:
|
||||
title: "Czym jest Misskey?"
|
||||
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
|
||||
features: "特徴"
|
||||
features: "Funkcje"
|
||||
rich-contents: "Wpis"
|
||||
rich-contents-desc: "Po prostu opublikuj swój pomysł, gorące tematy i wszystko, co chcesz udostępnić. Możesz ozdobić swoje słowa, dołączyć swoje ulubione zdjęcia, wysłać pliki, w tym filmy i utworzyć ankietę - to są rzeczy, które możesz zrobić w Misskey!"
|
||||
reaction: "Reakcje"
|
||||
reaction: "Reakcja"
|
||||
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
|
||||
ui: "Interfejs"
|
||||
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
|
||||
@ -22,7 +22,7 @@ common:
|
||||
adblock:
|
||||
detected: "Spróbuj wyłączyć blokadę reklam."
|
||||
warning: "<strong>Misskey nie zawiera reklam</strong>, ale część funkcji może nie działać prawidłowo z włączonym blokowaniem reklam."
|
||||
application-authorization: "アプリの連携"
|
||||
application-authorization: "Współpraca aplikacji"
|
||||
close: "Zamknij"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "Załaduj więcej"
|
||||
@ -58,9 +58,10 @@ common:
|
||||
trash: "Kosz"
|
||||
drive: "Dysk"
|
||||
messaging: "Rozmowy"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
explore: "Znajdź"
|
||||
following: "フォロー中"
|
||||
followers: "フォロワー"
|
||||
empty-timeline-info:
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "Dla dopełnienia, zobacz to zdalnie."
|
||||
renoted-by: "{user} udostępnił(a)"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "Coś poszło nie tak"
|
||||
retry: "Ponów próbę"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Użytkownicy"
|
||||
rename: "Zmień nazwę listy"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Nie udało się udostępnić"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "Czy na pewno chcesz udostępnić ten wpis?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "Śledzeni"
|
||||
followers: "Śledzący"
|
||||
is-bot: "To konto jest botem"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age} lat"
|
||||
year: "/"
|
||||
month: "/"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "Wpisy i odpowiedzi"
|
||||
with-media: "Multimedia"
|
||||
my-posts: "Moje wpisy"
|
||||
empty: "Ten użytkownik nie umieścił jeszcze niczego."
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "Wiadomości"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "Co chcesz zrobić? (wprowadź odpowiednią cyfrę): <1 → Wysłać plik | 2 → Wysłać plik z adresu URL | 3 → Utworzyć katalog | 4 → Zmienić nazwę tego katalogu | 5 → Przenieść ten katalog | 6 → Usunąć ten katalog>"
|
||||
deletion-alert: "Przepraszamy. Usuwanie katalogów nie zostało jeszcze zaimplementowane."
|
||||
folder-name: "Nazwa katalogu"
|
||||
root-rename-alert: "Nie można zmienić nazwy katalogu głównego. Przejdź do katalogu, którego nazwę chcesz zmienić."
|
||||
root-move-alert: "Nie można przenieść tego katalogu, ponieważ jest on katalogiem głównym. Przejdź do katalogu, który chcesz przenieść."
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "Adres URL pliku, który chcesz wysłać"
|
||||
uploading: "Rozpoczęto wysyłanie. Może to trochę potrwać."
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "ten wpis został usunięty"
|
||||
media-count: "{}zawartości multimedialnej"
|
||||
poll: "Ankieta"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "Brak wpisów"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "Witaj ponownie, "
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "Ustawienia"
|
||||
admin: "Admin"
|
||||
about: "O Misskey"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "Wygląda na to, że ten użytkownik nie opublikował jeszcze niczego"
|
||||
no-notes-with-media: "Brak wpisów z zawartością multimedialną"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "Ulubione"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "Globalne"
|
||||
mentions: "Wspomnienia"
|
||||
messages: "Wiadomości"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "Śledzący których znasz"
|
||||
last-used-at: "Ostatnio aktywny"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "Brak wpisów"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "Brak zdjęć"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "Tylko wpisy z zawartością multimedialną"
|
||||
is-media-view: "Widok multimediów"
|
||||
edit: "Opcje"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "Lixo"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "問題が発生しました"
|
||||
retry: "やり直す"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renoteに失敗しました"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "Seguidores que você conhece"
|
||||
last-used-at: "Ativo pela última vez"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "Nenhuma mensagem"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "Sem fotos"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "Мусорное ведро"
|
||||
drive: "Drive"
|
||||
messaging: "Чат"
|
||||
home: "ホーム"
|
||||
deck: "デッキ"
|
||||
timeline: "タイムライン"
|
||||
explore: "みつける"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
no-notes: "投稿がありません"
|
||||
error:
|
||||
title: "Что-то пошло не так :("
|
||||
retry: "Повторить"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "ミュート"
|
||||
blocking-list: "ブロック"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
enter-password: "パスワードを入力してください"
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "Renoteに失敗しました"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "この投稿をRenoteしますか?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "投稿がありません"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}のフォロー"
|
||||
followers: "{user}のフォロワー"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
is-bot: "このアカウントはBotです"
|
||||
no-description: "自己紹介はありません"
|
||||
years-old: "{age}歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "投稿と返信"
|
||||
with-media: "メディア"
|
||||
my-posts: "私の投稿"
|
||||
empty: "このユーザーはまだ何も投稿していないようです。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "メッセージ"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
|
||||
deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。"
|
||||
folder-name: "フォルダー名"
|
||||
root-rename-alert: "現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。"
|
||||
root-move-alert: "現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "アップロードしたいファイルのURL"
|
||||
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "この投稿は削除されました"
|
||||
media-count: "{}つのメディア"
|
||||
poll: "アンケート"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "おかえりなさい、"
|
||||
adjective: "さん"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "設定"
|
||||
admin: "管理"
|
||||
about: "Misskeyについて"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "このユーザーは投稿していないようです。"
|
||||
no-notes-with-media: "メディア付き投稿はありません。"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "お気に入り"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "グローバル"
|
||||
mentions: "あなた宛て"
|
||||
messages: "メッセージ"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "投稿がありません"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "よく話すユーザー"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "投稿はありません"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "写真はありません"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "メディア投稿のみ"
|
||||
is-media-view: "メディアビュー"
|
||||
edit: "オプション"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "フォローされています"
|
||||
|
@ -58,6 +58,7 @@ common:
|
||||
trash: "垃圾箱"
|
||||
drive: "网盘"
|
||||
messaging: "聊天"
|
||||
home: "ホーム"
|
||||
deck: "Deck"
|
||||
timeline: "时间线"
|
||||
explore: "发现"
|
||||
@ -144,6 +145,7 @@ common:
|
||||
is-remote-post: "该投稿已被复制."
|
||||
view-on-remote: "查看准确的信息"
|
||||
renoted-by: "由 {user} Renote"
|
||||
no-notes: "没有帖子"
|
||||
error:
|
||||
title: "出现问题"
|
||||
retry: "重试"
|
||||
@ -537,6 +539,10 @@ common/views/components/profile-editor.vue:
|
||||
mute-list: "屏蔽列表"
|
||||
blocking-list: "黑名单"
|
||||
export-requested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
|
||||
enter-password: "请输入您的密码"
|
||||
danger-zone: "危险选项"
|
||||
delete-account: "删除帐户"
|
||||
account-deleted: "帐户已被删除。 数据会在一段时间之后清除。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "用户"
|
||||
rename: "重命名列表"
|
||||
@ -785,8 +791,6 @@ desktop/views/components/renote-form.vue:
|
||||
failure: "重新发送失败"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "您是否要重新发送?"
|
||||
desktop/views/components/timeline.core.vue:
|
||||
empty: "没有帖子"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}的正在关注"
|
||||
followers: "{user}的关注者"
|
||||
@ -1353,6 +1357,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "关注中"
|
||||
followers: "关注者"
|
||||
is-bot: "这个账户是Bot"
|
||||
no-description: "没有自我介绍"
|
||||
years-old: "{age}岁"
|
||||
year: "年"
|
||||
month: "月"
|
||||
@ -1363,7 +1368,6 @@ desktop/views/pages/user/user.timeline.vue:
|
||||
with-replies: "帖子与回复"
|
||||
with-media: "媒体"
|
||||
my-posts: "我的帖子"
|
||||
empty: "看起来这个用户还没有发布什么呢。"
|
||||
desktop/views/widgets/messaging.vue:
|
||||
title: "信息"
|
||||
desktop/views/widgets/notifications.vue:
|
||||
@ -1397,8 +1401,7 @@ mobile/views/components/drive.vue:
|
||||
prompt: "您想要干什么呢?(请输入数字):<1 → 上传文件 | 2 → 从URL上传文件 | 3 → 创建新文件夹 | 4 → 更改这个文件夹的名称 | 5 → 移动这个文件夹 | 6 → 删除这个文件夹>"
|
||||
deletion-alert: "抱歉! 删除文件夹功能尚未实现。"
|
||||
folder-name: "文件夹名称"
|
||||
root-rename-alert: "您目前在root模式; 它无法重命名,因为它不是文件夹。 导航到要重命名的文件夹,然后重试。"
|
||||
root-move-alert: "您目前在root模式; 它无法移动,因为它不是文件夹。 导航到要移动的文件夹,然后重试。"
|
||||
here-is-root: "現在いる場所はルートで、フォルダではありません。"
|
||||
url-prompt: "要上传的文件的URL"
|
||||
uploading: "已请求上传。 上传完成可能需要一段时间。"
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
@ -1461,8 +1464,6 @@ mobile/views/components/sub-note-content.vue:
|
||||
deleted: "帖子已删除"
|
||||
media-count: "附加{}媒体"
|
||||
poll: "投票"
|
||||
mobile/views/components/timeline.vue:
|
||||
empty: "无帖子"
|
||||
mobile/views/components/ui.header.vue:
|
||||
welcome-back: "欢迎回来!"
|
||||
adjective: "先生"
|
||||
@ -1479,9 +1480,6 @@ mobile/views/components/ui.nav.vue:
|
||||
settings: "设置"
|
||||
admin: "管理"
|
||||
about: "关于 Misskey"
|
||||
mobile/views/components/user-timeline.vue:
|
||||
no-notes: "看起来该用户还没有发表任何东西哎。"
|
||||
no-notes-with-media: "媒体附件没有备注"
|
||||
mobile/views/pages/favorites.vue:
|
||||
title: "收藏"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
@ -1500,8 +1498,6 @@ mobile/views/pages/home.vue:
|
||||
global: "Global"
|
||||
mentions: "Mentions"
|
||||
messages: "信息"
|
||||
mobile/views/pages/home.timeline.vue:
|
||||
empty: "没有帖子"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "没有找到带有主题标签“{q}”的帖子"
|
||||
mobile/views/pages/welcome.vue:
|
||||
@ -1594,8 +1590,6 @@ mobile/views/pages/user/home.vue:
|
||||
frequently-replied-users: "活跃用户"
|
||||
followers-you-know: "您可能认识的关注者"
|
||||
last-used-at: "上次登录:"
|
||||
mobile/views/pages/user/home.notes.vue:
|
||||
no-notes: "没有帖子"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
no-photos: "没有图片"
|
||||
deck:
|
||||
@ -1623,7 +1617,6 @@ deck:
|
||||
description: "服务器管理员已禁用时间线。"
|
||||
deck/deck.tl-column.vue:
|
||||
is-media-only: "只有媒体的帖子"
|
||||
is-media-view: "媒体视图"
|
||||
edit: "选项"
|
||||
deck/deck.user-column.vue:
|
||||
follows-you: "关注您"
|
||||
|
18
package.json
18
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.87.4",
|
||||
"version": "10.89.1",
|
||||
"codename": "nighthike",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -95,14 +95,14 @@
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "3.0.1",
|
||||
"apexcharts": "3.3.0",
|
||||
"apexcharts": "3.4.1",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bee-queue": "1.2.2",
|
||||
"bootstrap-vue": "2.0.0-rc.11",
|
||||
"cafy": "14.0.1",
|
||||
"cafy": "15.1.0",
|
||||
"chai": "4.2.0",
|
||||
"chai-http": "4.2.1",
|
||||
"chalk": "2.4.2",
|
||||
@ -221,27 +221,27 @@
|
||||
"ts-node": "7.0.1",
|
||||
"tslint": "5.12.1",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typescript": "3.2.4",
|
||||
"typescript": "3.3.3333",
|
||||
"typescript-eslint-parser": "21.0.2",
|
||||
"uglify-es": "3.3.9",
|
||||
"url-loader": "1.1.2",
|
||||
"uuid": "3.3.2",
|
||||
"v-animate-css": "0.0.3",
|
||||
"video-thumbnail-generator": "1.1.3",
|
||||
"vue": "2.6.6",
|
||||
"vue": "2.6.7",
|
||||
"vue-color": "2.7.0",
|
||||
"vue-content-loading": "1.5.3",
|
||||
"vue-cropperjs": "3.0.0",
|
||||
"vue-i18n": "8.8.1",
|
||||
"vue-i18n": "8.8.2",
|
||||
"vue-js-modal": "1.3.28",
|
||||
"vue-loader": "15.6.2",
|
||||
"vue-loader": "15.6.4",
|
||||
"vue-marquee-text-component": "1.1.1",
|
||||
"vue-prism-component": "1.1.1",
|
||||
"vue-router": "3.0.2",
|
||||
"vue-sequential-entrance": "1.1.3",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.2.10",
|
||||
"vue-template-compiler": "2.6.6",
|
||||
"vue-template-compiler": "2.6.7",
|
||||
"vuedraggable": "2.17.0",
|
||||
"vuewordcloud": "18.7.11",
|
||||
"vuex": "3.1.0",
|
||||
@ -251,7 +251,7 @@
|
||||
"webpack": "4.28.4",
|
||||
"webpack-cli": "3.2.1",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "6.1.3",
|
||||
"ws": "6.1.4",
|
||||
"xev": "2.0.1"
|
||||
}
|
||||
}
|
||||
|
16
src/@types/webfinger.js.d.ts
vendored
16
src/@types/webfinger.js.d.ts
vendored
@ -26,14 +26,14 @@ declare module 'webfinger.js' {
|
||||
}
|
||||
|
||||
interface IIDXLinks {
|
||||
'avatar': IJRDLink[];
|
||||
'remotestorage': IJRDLink[];
|
||||
'blog': IJRDLink[];
|
||||
'vcard': IJRDLink[];
|
||||
'updates': IJRDLink[];
|
||||
'share': IJRDLink[];
|
||||
'profile': IJRDLink[];
|
||||
'webfist': IJRDLink[];
|
||||
'avatar': IJRDLink[];
|
||||
'remotestorage': IJRDLink[];
|
||||
'blog': IJRDLink[];
|
||||
'vcard': IJRDLink[];
|
||||
'updates': IJRDLink[];
|
||||
'share': IJRDLink[];
|
||||
'profile': IJRDLink[];
|
||||
'webfist': IJRDLink[];
|
||||
'camlistore': IJRDLink[];
|
||||
[type: string]: IJRDLink[];
|
||||
}
|
||||
|
@ -108,16 +108,11 @@
|
||||
app = isMobile ? 'mobile' : 'desktop';
|
||||
}
|
||||
|
||||
// Get salt query
|
||||
const salt = localStorage.getItem('salt')
|
||||
? `?salt=${localStorage.getItem('salt')}`
|
||||
: '';
|
||||
|
||||
// Load an app script
|
||||
// Note: 'async' make it possible to load the script asyncly.
|
||||
// 'defer' make it possible to run the script when the dom loaded.
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('src', `/assets/${app}.${ver}.js${salt}`);
|
||||
script.setAttribute('src', `/assets/${app}.${ver}.js`);
|
||||
script.setAttribute('async', 'true');
|
||||
script.setAttribute('defer', 'true');
|
||||
head.appendChild(script);
|
||||
@ -155,9 +150,6 @@
|
||||
|
||||
localStorage.removeItem('locale');
|
||||
|
||||
// Random
|
||||
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
||||
|
||||
// Clear cache (service worker)
|
||||
try {
|
||||
navigator.serviceWorker.controller.postMessage('clear');
|
||||
|
@ -128,15 +128,16 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$el.style.pointerEvents = 'none';
|
||||
(this.$refs.bg as any).style.pointerEvents = 'none';
|
||||
(this.$refs.main as any).style.pointerEvents = 'none';
|
||||
|
||||
anime({
|
||||
targets: this.$refs.bg,
|
||||
opacity: 0,
|
||||
duration: 300,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
(this.$refs.main as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.main,
|
||||
opacity: 0,
|
||||
|
@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<a class="zxrjzpcj" :href="url" :class="service" target="_blank">
|
||||
<fa :icon="icon" size="lg" fixed-width /><span>{{ text }}</span>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['url', 'text', 'icon', 'service']
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.zxrjzpcj
|
||||
display inline-block
|
||||
padding 6px 8px 6px 6px
|
||||
margin-top 4px
|
||||
margin-bottom 4px
|
||||
border-radius 32px
|
||||
white-space nowrap
|
||||
|
||||
&:hover
|
||||
text-decoration none
|
||||
|
||||
&.twitter
|
||||
color #fff
|
||||
background #1da1f3
|
||||
|
||||
&:hover
|
||||
background #0c87cf
|
||||
|
||||
&.github
|
||||
color #fff
|
||||
background #171515
|
||||
|
||||
&:hover
|
||||
background #000
|
||||
|
||||
&.discord
|
||||
color #fff
|
||||
background #7289da
|
||||
|
||||
&:hover
|
||||
background #4968ce
|
||||
|
||||
</style>
|
26
src/client/app/common/views/components/integrations.vue
Normal file
26
src/client/app/common/views/components/integrations.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="nbogcrmo" :v-if="user.twitter || user.github || user.discord">
|
||||
<x-integration v-if="user.twitter" service="twitter" :url="`https://twitter.com/${user.twitter.screenName}`" :text="user.twitter.screenName" :icon="['fab', 'twitter']"/>
|
||||
<x-integration v-if="user.github" service="github" :url="`https://github.com/${user.github.login}`" :text="user.github.login" :icon="['fab', 'github']"/>
|
||||
<x-integration v-if="user.discord" service="discord" :url="`https://discordapp.com/users/${user.discord.id}`" :text="`${user.discord.username}#${user.discord.discriminator}`" :icon="['fab', 'discord']"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XIntegration from './integrations.integration.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XIntegration
|
||||
},
|
||||
props: ['user']
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.nbogcrmo
|
||||
> *
|
||||
margin-right 10px
|
||||
|
||||
</style>
|
@ -44,7 +44,7 @@ export default Vue.extend({
|
||||
},
|
||||
mounted() {
|
||||
const audioTag = this.$refs.audio as HTMLAudioElement;
|
||||
audioTag.volume = this.$store.state.device.mediaVolume;
|
||||
if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume;
|
||||
},
|
||||
methods: {
|
||||
volumechange() {
|
||||
|
@ -101,6 +101,13 @@
|
||||
<ui-button @click="doExport()"><fa :icon="faDownload"/> {{ $t('export') }}</ui-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<details>
|
||||
<summary>{{ $t('danger-zone') }}</summary>
|
||||
<ui-button @click="deleteAccount()">{{ $t('delete-account') }}</ui-button>
|
||||
</details>
|
||||
</section>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
@ -283,6 +290,25 @@ export default Vue.extend({
|
||||
type: 'info',
|
||||
text: this.$t('export-requested')
|
||||
});
|
||||
},
|
||||
|
||||
async deleteAccount() {
|
||||
const { canceled: canceled, result: password } = await this.$root.dialog({
|
||||
title: this.$t('enter-password'),
|
||||
input: {
|
||||
type: 'password'
|
||||
}
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
this.$root.api('i/delete-account', {
|
||||
password
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('account-deleted')
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -10,13 +10,15 @@
|
||||
<span>{{ $t('username') }}</span>
|
||||
<template #prefix>@</template>
|
||||
<template #suffix>@{{ host }}</template>
|
||||
<template #desc v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner" pulse fixed-width/> {{ $t('checking') }}</template>
|
||||
<template #desc v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</template>
|
||||
<template #desc v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</template>
|
||||
<template #desc v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</template>
|
||||
<template #desc v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('invalid-format') }}</template>
|
||||
<template #desc v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-short') }}</template>
|
||||
<template #desc v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-long') }}</template>
|
||||
<template #desc>
|
||||
<span v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner" pulse fixed-width/> {{ $t('checking') }}</span>
|
||||
<span v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</span>
|
||||
<span v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</span>
|
||||
<span v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</span>
|
||||
<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('invalid-format') }}</span>
|
||||
<span v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-short') }}</span>
|
||||
<span v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-long') }}</span>
|
||||
</template>
|
||||
</ui-input>
|
||||
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill">
|
||||
<span>{{ $t('password') }}</span>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</div>
|
||||
</ui-container>
|
||||
|
||||
<ui-container :body-togglable="true" ref="tags">
|
||||
<ui-container :body-togglable="true" :expanded="tag == null" ref="tags">
|
||||
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
|
||||
|
||||
<div class="vxjfqztj">
|
||||
|
@ -22,7 +22,7 @@ export default define({
|
||||
name: 'rss',
|
||||
props: () => ({
|
||||
compact: false,
|
||||
url: 'http://news.yahoo.co.jp/pickup/rss.xml'
|
||||
url: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews'
|
||||
})
|
||||
}).extend({
|
||||
i18n: i18n(),
|
||||
@ -78,6 +78,9 @@ export default define({
|
||||
padding 4px 0
|
||||
color var(--text)
|
||||
border-bottom dashed var(--lineWidth) var(--faceDivider)
|
||||
white-space nowrap
|
||||
text-overflow ellipsis
|
||||
overflow hidden
|
||||
|
||||
&:last-child
|
||||
border-bottom none
|
||||
|
@ -48,16 +48,19 @@
|
||||
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
|
||||
</button>
|
||||
<button v-if="['public', 'home'].includes(appearNote.visibility)" class="renoteButton" @click="renote()" :title="$t('renote')">
|
||||
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
|
||||
<fa icon="retweet"/>
|
||||
<p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
|
||||
</button>
|
||||
<button v-else class="inhibitedButton">
|
||||
<fa icon="ban"/>
|
||||
</button>
|
||||
<button v-if="!isMyNote && appearNote.myReaction == null" class="reactionButton" @click="react()" ref="reactButton" :title="$t('add-reaction')">
|
||||
<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>
|
||||
</button>
|
||||
<button v-if="!isMyNote && appearNote.myReaction != null" class="reactionButton reacted" @click="undoReact(appearNote)" ref="reactButton" :title="$t('undo-reaction')">
|
||||
<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>
|
||||
</button>
|
||||
<button @click="menu()" ref="menuButton">
|
||||
<fa icon="ellipsis-h"/>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
|
||||
|
||||
<slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
|
||||
<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
|
||||
|
||||
<mk-error v-if="!fetching && !inited" @retry="init()"/>
|
||||
|
||||
@ -209,6 +209,11 @@ export default Vue.extend({
|
||||
> *
|
||||
transition transform .3s ease, opacity .3s ease
|
||||
|
||||
> .empty
|
||||
padding 16px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
> .placeholder
|
||||
padding 32px
|
||||
opacity 0.3
|
||||
|
@ -71,7 +71,7 @@
|
||||
|
||||
<section>
|
||||
<ui-switch v-model="showPostFormOnTopOfTl">{{ $t('post-form-on-timeline') }}</ui-switch>
|
||||
<ui-button @click="customizeHome">{{ $t('customize') }}</ui-button>
|
||||
<ui-button @click="customizeHome">{{ $t('@.customize-home') }}</ui-button>
|
||||
</section>
|
||||
<section>
|
||||
<header>{{ $t('wallpaper') }}</header>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<header v-if="showHeader">
|
||||
<div class="title"><slot name="header"></slot></div>
|
||||
<slot name="func"></slot>
|
||||
<button v-if="bodyTogglable" @click="() => showBody = !showBody">
|
||||
<button v-if="bodyTogglable" @click="toggleContent(!showBody)">
|
||||
<template v-if="showBody"><fa icon="angle-up"/></template>
|
||||
<template v-else><fa icon="angle-down"/></template>
|
||||
</button>
|
||||
@ -30,6 +30,10 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expanded: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
inject: {
|
||||
inDeck: {
|
||||
@ -38,12 +42,13 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showBody: true
|
||||
showBody: this.expanded
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleContent(show: boolean) {
|
||||
this.showBody = show;
|
||||
this.$emit('toggle', show);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -62,16 +62,14 @@
|
||||
<ul>
|
||||
<li @click="toggleDeckMode">
|
||||
<p>
|
||||
<span>{{ $t('@.deck') }}</span>
|
||||
<template v-if="$store.state.device.inDeckMode"><i><fa :icon="faHome"/></i></template>
|
||||
<template v-else><i><fa :icon="faColumns"/></i></template>
|
||||
<template v-if="$store.state.device.inDeckMode"><span>{{ $t('@.home') }}</span><i><fa :icon="faHome"/></i></template>
|
||||
<template v-else><span>{{ $t('@.deck') }}</span><i><fa :icon="faColumns"/></i></template>
|
||||
</p>
|
||||
</li>
|
||||
<li @click="dark">
|
||||
<p>
|
||||
<span>{{ $t('dark') }}</span>
|
||||
<template v-if="$store.state.device.darkmode"><i><fa icon="moon"/></i></template>
|
||||
<template v-else><i><fa :icon="['far', 'moon']"/></i></template>
|
||||
<span>{{ $store.state.device.darkmode ? $t('@.turn-off-darkmode') : $t('@.turn-on-darkmode') }}</span>
|
||||
<template><i><fa :icon="$store.state.device.darkmode ? faSun : faMoon"/></i></template>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
@ -98,13 +96,14 @@ import MkSettingsWindow from './settings-window.vue';
|
||||
import MkDriveWindow from './drive-window.vue';
|
||||
import contains from '../../../common/scripts/contains';
|
||||
import { faHome, faColumns } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('desktop/views/components/ui.header.account.vue'),
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
faHome, faColumns
|
||||
faHome, faColumns, faMoon, faSun
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" :media-view="mediaView" @inited="() => $emit('loaded')"/>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -22,11 +22,6 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" :media-view="mediaView" @inited="() => $emit('loaded')"/>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -22,11 +22,6 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
|
||||
<slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
|
||||
<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
|
||||
|
||||
<mk-error v-if="!fetching && !inited" @retry="init()"/>
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
:note="note"
|
||||
:key="note.id"
|
||||
@update:note="onNoteUpdated(i, $event)"
|
||||
:media-view="mediaView"
|
||||
:compact="true"
|
||||
:mini="true"/>
|
||||
<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
|
||||
@ -56,11 +55,6 @@ export default Vue.extend({
|
||||
props: {
|
||||
makePromise: {
|
||||
required: true
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
@ -91,6 +85,9 @@ export default Vue.extend({
|
||||
watch: {
|
||||
queue(q) {
|
||||
this.count(q.length);
|
||||
},
|
||||
makePromise() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
|
||||
@ -115,12 +112,12 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
reload() {
|
||||
this.queue = [];
|
||||
this.notes = [];
|
||||
this.init();
|
||||
},
|
||||
|
||||
init() {
|
||||
this.queue = [];
|
||||
this.notes = [];
|
||||
this.fetching = true;
|
||||
this.makePromise().then(x => {
|
||||
if (Array.isArray(x)) {
|
||||
@ -204,6 +201,11 @@ export default Vue.extend({
|
||||
> *
|
||||
transition transform .3s ease, opacity .3s ease
|
||||
|
||||
> .empty
|
||||
padding 16px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
> .placeholder
|
||||
padding 16px
|
||||
opacity 0.3
|
||||
|
@ -12,25 +12,21 @@
|
||||
|
||||
<div class="editor" style="padding:12px" v-if="edit">
|
||||
<ui-switch v-model="column.isMediaOnly" @change="onChangeSettings">{{ $t('is-media-only') }}</ui-switch>
|
||||
<ui-switch v-model="column.isMediaView" @change="onChangeSettings">{{ $t('is-media-view') }}</ui-switch>
|
||||
</div>
|
||||
|
||||
<x-list-tl v-if="column.type == 'list'"
|
||||
:list="column.list"
|
||||
:media-only="column.isMediaOnly"
|
||||
:media-view="column.isMediaView"
|
||||
ref="tl"
|
||||
/>
|
||||
<x-hashtag-tl v-else-if="column.type == 'hashtag'"
|
||||
:tag-tl="$store.state.settings.tagTimelines.find(x => x.id == column.tagTlId)"
|
||||
:media-only="column.isMediaOnly"
|
||||
:media-view="column.isMediaView"
|
||||
ref="tl"
|
||||
/>
|
||||
<x-tl v-else
|
||||
:src="column.type"
|
||||
:media-only="column.isMediaOnly"
|
||||
:media-view="column.isMediaView"
|
||||
ref="tl"
|
||||
/>
|
||||
</x-column>
|
||||
|
@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p class="desc">{{ $t('disabled-timeline.description') }}</p>
|
||||
</div>
|
||||
<x-notes v-else ref="timeline" :make-promise="makePromise" :media-view="mediaView" @inited="() => $emit('loaded')"/>
|
||||
<x-notes v-else ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -34,11 +34,6 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
mediaView: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
@ -73,16 +68,18 @@ export default Vue.extend({
|
||||
|
||||
watch: {
|
||||
mediaOnly() {
|
||||
this.fetch();
|
||||
(this.$refs.timeline as any).reload();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.makePromise = cursor => this.$root.api(this.endpoint, {
|
||||
limit: fetchLimit + 1,
|
||||
untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
|
||||
untilId: cursor ? cursor : undefined,
|
||||
...this.baseQuery, ...this.query
|
||||
withFiles: this.mediaOnly,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
|
||||
}).then(notes => {
|
||||
if (notes.length == fetchLimit + 1) {
|
||||
notes.pop();
|
||||
|
@ -6,7 +6,9 @@
|
||||
<x-note v-for="n in user.pinnedNotes" :key="n.id" :note="n" :mini="true"/>
|
||||
</div>
|
||||
</ui-container>
|
||||
<ui-container v-if="images.length > 0" :body-togglable="true">
|
||||
<ui-container v-if="images.length > 0" :body-togglable="true"
|
||||
:expanded="$store.state.device.expandUsersPhotos"
|
||||
@toggle="expanded => $store.commit('device/set', { key: 'expandUsersPhotos', value: expanded })">
|
||||
<template #header><fa :icon="['far', 'images']"/> {{ $t('images') }}</template>
|
||||
<div class="sainvnaq">
|
||||
<router-link v-for="image in images"
|
||||
@ -17,7 +19,9 @@
|
||||
></router-link>
|
||||
</div>
|
||||
</ui-container>
|
||||
<ui-container :body-togglable="true">
|
||||
<ui-container :body-togglable="true"
|
||||
:expanded="$store.state.device.expandUsersActivity"
|
||||
@toggle="expanded => $store.commit('device/set', { key: 'expandUsersActivity', value: expanded })">
|
||||
<template #header><fa :icon="['far', 'chart-bar']"/> {{ $t('activity') }}</template>
|
||||
<div>
|
||||
<div ref="chart"></div>
|
||||
@ -61,7 +65,26 @@ export default Vue.extend({
|
||||
return {
|
||||
withFiles: false,
|
||||
images: [],
|
||||
makePromise: cursor => this.$root.api('users/notes', {
|
||||
makePromise: null,
|
||||
chart: null as ApexCharts
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
user() {
|
||||
this.fetch();
|
||||
this.genPromiseMaker();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetch();
|
||||
this.genPromiseMaker();
|
||||
},
|
||||
|
||||
methods: {
|
||||
genPromiseMaker() {
|
||||
this.makePromise = cursor => this.$root.api('users/notes', {
|
||||
userId: this.user.id,
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
@ -82,15 +105,9 @@ export default Vue.extend({
|
||||
cursor: null
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetch();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
const image = [
|
||||
'image/jpeg',
|
||||
@ -144,7 +161,9 @@ export default Vue.extend({
|
||||
]);
|
||||
}
|
||||
|
||||
const chart = new ApexCharts(this.$refs.chart, {
|
||||
if (this.chart) this.chart.destroy();
|
||||
|
||||
this.chart = new ApexCharts(this.$refs.chart, {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
stacked: true,
|
||||
@ -190,7 +209,7 @@ export default Vue.extend({
|
||||
}
|
||||
});
|
||||
|
||||
chart.render();
|
||||
this.chart.render();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
@ -8,9 +8,6 @@
|
||||
<router-link to="/explore">{{ $t('@.empty-timeline-info.explore') }}</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<fa :icon="['far', 'comments']"/>{{ $t('empty') }}
|
||||
</template>
|
||||
</mk-notes>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -23,6 +23,7 @@
|
||||
<div class="description">
|
||||
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
|
||||
<p v-else class="empty">{{ $t('no-description') }}</p>
|
||||
<x-integrations :user="user" style="margin-top:16px;"/>
|
||||
</div>
|
||||
<div class="fields" v-if="user.fields">
|
||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||
@ -52,9 +53,13 @@ import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import * as age from 's-age';
|
||||
import XUserMenu from '../../../../common/views/components/user-menu.vue';
|
||||
import XIntegrations from '../../../../common/views/components/integrations.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('desktop/views/pages/user/user.header.vue'),
|
||||
components: {
|
||||
XIntegrations
|
||||
},
|
||||
props: ['user'],
|
||||
computed: {
|
||||
style(): any {
|
||||
@ -215,6 +220,8 @@ export default Vue.extend({
|
||||
color var(--text)
|
||||
|
||||
> .description
|
||||
font-size 15px
|
||||
|
||||
> .empty
|
||||
margin 0
|
||||
opacity 0.5
|
||||
@ -251,6 +258,7 @@ export default Vue.extend({
|
||||
margin-top 16px
|
||||
padding-top 16px
|
||||
border-top solid 1px var(--faceDivider)
|
||||
font-size 15px
|
||||
|
||||
&:empty
|
||||
display none
|
||||
|
@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<div class="lnctpgve">
|
||||
<x-integrations :user="user" v-if="user.twitter || user.github || user.discord"/>
|
||||
<mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/>
|
||||
<!--<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>-->
|
||||
<div class="activity">
|
||||
<ui-container :body-togglable="true">
|
||||
<ui-container :body-togglable="true"
|
||||
:expanded="$store.state.device.expandUsersActivity"
|
||||
@toggle="expanded => $store.commit('device/set', { key: 'expandUsersActivity', value: expanded })">
|
||||
<template #header><fa icon="chart-bar"/>{{ $t('activity') }}</template>
|
||||
<x-activity :user="user" :limit="35" style="padding: 16px;"/>
|
||||
</ui-container>
|
||||
@ -17,11 +18,8 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import parseAcct from '../../../../../../misc/acct/parse';
|
||||
import Progress from '../../../../common/scripts/loading';
|
||||
import XTimeline from './user.timeline.vue';
|
||||
import XPhotos from './user.photos.vue';
|
||||
import XIntegrations from './user.integrations.vue';
|
||||
import XActivity from '../../../../common/views/components/activity.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
@ -29,7 +27,6 @@ export default Vue.extend({
|
||||
components: {
|
||||
XTimeline,
|
||||
XPhotos,
|
||||
XIntegrations,
|
||||
XActivity
|
||||
},
|
||||
props: {
|
||||
|
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<a :href="url" :class="service" target="_blank">
|
||||
<fa :icon="icon" size="lg" fixed-width />
|
||||
<div>{{ text }}</div>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['url', 'text', 'icon', 'service']
|
||||
});
|
||||
</script>
|
@ -1,66 +0,0 @@
|
||||
<template>
|
||||
<div class="usertwitxxxgithxxdiscxxxintegrat" :v-if="user.twitter || user.github || user.discord">
|
||||
<x-integration v-if="user.twitter" service="twitter" :url="`https://twitter.com/${user.twitter.screenName}`" :text="user.twitter.screenName" :icon="['fab', 'twitter']"/>
|
||||
<x-integration v-if="user.github" service="github" :url="`https://github.com/${user.github.login}`" :text="user.github.login" :icon="['fab', 'github']"/>
|
||||
<x-integration v-if="user.discord" service="discord" :url="`https://discordapp.com/users/${user.discord.id}`" :text="`${user.discord.username}#${user.discord.discriminator}`" :icon="['fab', 'discord']"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XIntegration from './user.integrations.integration.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XIntegration
|
||||
},
|
||||
props: ['user']
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.usertwitxxxgithxxdiscxxxintegrat
|
||||
display flex
|
||||
|
||||
> a
|
||||
display flex
|
||||
flex 1
|
||||
align-items center
|
||||
padding 16px
|
||||
box-shadow var(--shadow)
|
||||
border-radius var(--round)
|
||||
|
||||
&:not(:last-child)
|
||||
margin-right 16px
|
||||
|
||||
&:hover
|
||||
text-decoration none
|
||||
|
||||
> div
|
||||
padding-left .2em
|
||||
line-height 1.3em
|
||||
flex 1 0
|
||||
word-wrap anywhere
|
||||
|
||||
&.twitter
|
||||
color #fff
|
||||
background #1da1f3
|
||||
|
||||
&:hover
|
||||
background #0c87cf
|
||||
|
||||
&.github
|
||||
color #fff
|
||||
background #171515
|
||||
|
||||
&:hover
|
||||
background #000
|
||||
|
||||
&.discord
|
||||
color #fff
|
||||
background #7289da
|
||||
|
||||
&:hover
|
||||
background #4968ce
|
||||
|
||||
</style>
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<ui-container :body-togglable="true">
|
||||
<ui-container :body-togglable="true"
|
||||
:expanded="$store.state.device.expandUsersPhotos"
|
||||
@toggle="expanded => $store.commit('device/set', { key: 'expandUsersPhotos', value: expanded })">
|
||||
<template #header><fa icon="camera"/> {{ $t('title') }}</template>
|
||||
|
||||
<div class="dzsuvbsrrrwobdxifudxuefculdfiaxd">
|
||||
|
@ -9,7 +9,6 @@
|
||||
<span :data-active="mode == 'my-posts'" @click="mode = 'my-posts'"><fa icon="user"/> {{ $t('my-posts') }}</span>
|
||||
</header>
|
||||
</template>
|
||||
<template #empty><fa :icon="['far', 'comments']"/>{{ $t('empty') }}</template>
|
||||
</mk-notes>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -399,7 +399,7 @@ export default Vue.extend({
|
||||
this.moveFolder();
|
||||
break;
|
||||
case '6':
|
||||
alert(this.$t('deletion-alert'));
|
||||
this.deleteFolder();
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -421,7 +421,10 @@ export default Vue.extend({
|
||||
|
||||
renameFolder() {
|
||||
if (this.folder == null) {
|
||||
alert(this.$t('root-rename-alert'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('here-is-root')
|
||||
});
|
||||
return;
|
||||
}
|
||||
const name = window.prompt(this.$t('folder-name'), this.folder.name);
|
||||
@ -436,7 +439,10 @@ export default Vue.extend({
|
||||
|
||||
moveFolder() {
|
||||
if (this.folder == null) {
|
||||
alert(this.$t('root-move-alert'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('here-is-root')
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$chooseDriveFolder().then(folder => {
|
||||
@ -456,13 +462,31 @@ export default Vue.extend({
|
||||
url: url,
|
||||
folderId: this.folder ? this.folder.id : undefined
|
||||
});
|
||||
alert(this.$t('uploading'));
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: this.$t('uploading')
|
||||
});
|
||||
},
|
||||
|
||||
onChangeLocalFile() {
|
||||
for (const f of Array.from((this.$refs.file as any).files)) {
|
||||
(this.$refs.uploader as any).upload(f, this.folder);
|
||||
}
|
||||
},
|
||||
|
||||
deleteFolder() {
|
||||
if (this.folder == null) {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('here-is-root')
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$root.api('drive/folders/delete', {
|
||||
folderId: this.folder.id
|
||||
}).then(folder => {
|
||||
this.cd(this.folder.parentId);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -123,18 +123,26 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Get replies
|
||||
if (!this.compact) {
|
||||
this.fetchReplies();
|
||||
},
|
||||
|
||||
watch: {
|
||||
note() {
|
||||
this.fetchReplies();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchReplies() {
|
||||
if (this.compact) return;
|
||||
this.$root.api('notes/replies', {
|
||||
noteId: this.appearNote.id,
|
||||
limit: 8
|
||||
}).then(replies => {
|
||||
this.replies = replies;
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConversation() {
|
||||
this.conversationFetching = true;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="ivaojijs">
|
||||
<slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
|
||||
<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
|
||||
|
||||
<mk-error v-if="!fetching && !inited" @retry="init()"/>
|
||||
|
||||
@ -197,6 +197,11 @@ export default Vue.extend({
|
||||
@media (min-width 500px)
|
||||
box-shadow 0 8px 32px rgba(#000, 0.1)
|
||||
|
||||
> .empty
|
||||
padding 16px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
.transition
|
||||
.mk-notes-enter
|
||||
.mk-notes-leave-to
|
||||
|
@ -30,10 +30,14 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expanded: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showBody: true
|
||||
showBody: this.expanded
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -33,7 +33,7 @@
|
||||
<li><a @click="search"><i><fa icon="search" fixed-width/></i>{{ $t('search') }}<i><fa icon="angle-right"/></i></a></li>
|
||||
<li><router-link to="/i/settings" :data-active="$route.name == 'settings'"><i><fa icon="cog" fixed-width/></i>{{ $t('settings') }}<i><fa icon="angle-right"/></i></router-link></li>
|
||||
<li v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)"><a href="/admin"><i><fa icon="terminal" fixed-width/></i><span>{{ $t('admin') }}</span><i><fa icon="angle-right"/></i></a></li>
|
||||
<li @click="dark"><p><template v-if="$store.state.device.darkmode"><i><fa icon="moon" fixed-width/></i></template><template v-else><i><fa :icon="['far', 'moon']"/></i></template><span>{{ $t('darkmode') }}</span></p></li>
|
||||
<li @click="dark"><p><template><i><fa :icon="$store.state.device.darkmode ? faSun : faMoon" fixed-width/></i></template><span>{{ $store.state.device.darkmode ? $t('@.turn-off-darkmode') : $t('@.turn-on-darkmode') }}</span></p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="announcements" v-if="announcements && announcements.length > 0">
|
||||
@ -53,6 +53,7 @@ import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { lang } from '../../../config';
|
||||
import { faNewspaper, faHashtag } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('mobile/views/components/ui.nav.vue'),
|
||||
@ -65,7 +66,7 @@ export default Vue.extend({
|
||||
aboutUrl: `/docs/${lang}/about`,
|
||||
announcements: [],
|
||||
searching: false,
|
||||
faNewspaper, faHashtag
|
||||
faNewspaper, faHashtag, faMoon, faSun
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<div class="mk-user-timeline">
|
||||
<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
|
||||
<template #empty>
|
||||
<fa :icon="['far', 'comments']"/>
|
||||
{{ withMedia ? this.$t('no-notes-with-media') : this.$t('no-notes') }}
|
||||
</template>
|
||||
</mk-notes>
|
||||
<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -7,11 +7,7 @@
|
||||
</div>
|
||||
</ui-container>
|
||||
|
||||
<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
|
||||
<template #empty>
|
||||
<fa :icon="['far', 'comments']"/>{{ $t('empty') }}
|
||||
</template>
|
||||
</mk-notes>
|
||||
<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div v-if="!fetching && notes.length > 0">
|
||||
<mk-note-card v-for="note in notes" :key="note.id" :note="note"/>
|
||||
</div>
|
||||
<p class="empty" v-if="!fetching && notes.length == 0">{{ $t('no-notes') }}</p>
|
||||
<p class="empty" v-if="!fetching && notes.length == 0">{{ $t('@.no-notes') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<mk-ui>
|
||||
<template #header v-if="!fetching"><img :src="avator" alt="">
|
||||
<mk-user-name :user="user"/>
|
||||
<template #header v-if="!fetching">
|
||||
<img :src="avator" alt=""><mk-user-name :user="user"/>
|
||||
</template>
|
||||
<div class="wwtwuxyh" v-if="!fetching">
|
||||
<div class="is-suspended" v-if="user.isSuspended"><p><fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}</p></div>
|
||||
@ -23,6 +23,7 @@
|
||||
</div>
|
||||
<div class="description">
|
||||
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
|
||||
<x-integrations :user="user" style="margin:20px 0;"/>
|
||||
</div>
|
||||
<div class="fields" v-if="user.fields">
|
||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||
@ -67,9 +68,9 @@
|
||||
</nav>
|
||||
<main>
|
||||
<template v-if="$route.name == 'user'">
|
||||
<x-home v-if="page == 'home'" :user="user"/>
|
||||
<mk-user-timeline v-if="page == 'notes'" :user="user" key="tl"/>
|
||||
<mk-user-timeline v-if="page == 'media'" :user="user" :with-media="true" key="media"/>
|
||||
<x-home v-if="page == 'home'" :user="user" :key="user.id"/>
|
||||
<mk-user-timeline v-if="page == 'notes'" :user="user" :key="`tl:${user.id}`"/>
|
||||
<mk-user-timeline v-if="page == 'media'" :user="user" :with-media="true" :key="`media:${user.id}`"/>
|
||||
</template>
|
||||
<router-view :user="user"></router-view>
|
||||
</main>
|
||||
@ -86,11 +87,13 @@ import Progress from '../../../../common/scripts/loading';
|
||||
import XUserMenu from '../../../../common/views/components/user-menu.vue';
|
||||
import XHome from './home.vue';
|
||||
import { getStaticImageUrl } from '../../../../common/scripts/get-static-image-url';
|
||||
import XIntegrations from '../../../../common/views/components/integrations.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('mobile/views/pages/user.vue'),
|
||||
components: {
|
||||
XHome
|
||||
XHome,
|
||||
XIntegrations
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -245,6 +248,9 @@ export default Vue.extend({
|
||||
margin 8px 0
|
||||
color var(--mobileUserPageDescription)
|
||||
|
||||
@media (max-width 450px)
|
||||
font-size 15px
|
||||
|
||||
> .fields
|
||||
margin 8px 0
|
||||
|
||||
@ -276,6 +282,9 @@ export default Vue.extend({
|
||||
> .info
|
||||
margin 8px 0
|
||||
|
||||
@media (max-width 450px)
|
||||
font-size 15px
|
||||
|
||||
> p
|
||||
display inline
|
||||
margin 0 16px 0 0
|
||||
|
@ -68,7 +68,9 @@ const defaultDeviceSettings = {
|
||||
mobileNotificationPosition: 'bottom',
|
||||
deckMode: false,
|
||||
useOsDefaultEmojis: false,
|
||||
disableShowingAnimatedImages: false
|
||||
disableShowingAnimatedImages: false,
|
||||
expandUsersPhotos: true,
|
||||
expandUsersActivity: true,
|
||||
};
|
||||
|
||||
export default (os: MiOS) => new Vuex.Store({
|
||||
|
24
src/client/assets/redoc.html
Normal file
24
src/client/assets/redoc.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Misskey API</title>
|
||||
<!-- needed for adaptive design -->
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
|
||||
<!--
|
||||
ReDoc doesn't change outer page styles
|
||||
-->
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url='/api.json'></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
||||
</body>
|
||||
</html>
|
@ -32,16 +32,12 @@ export default function load() {
|
||||
mixin.host = url.host;
|
||||
mixin.hostname = url.hostname;
|
||||
mixin.scheme = url.protocol.replace(/:$/, '');
|
||||
mixin.ws_scheme = mixin.scheme.replace('http', 'ws');
|
||||
mixin.ws_url = `${mixin.ws_scheme}://${mixin.host}`;
|
||||
mixin.api_url = `${mixin.scheme}://${mixin.host}/api`;
|
||||
mixin.auth_url = `${mixin.scheme}://${mixin.host}/auth`;
|
||||
mixin.dev_url = `${mixin.scheme}://${mixin.host}/dev`;
|
||||
mixin.docs_url = `${mixin.scheme}://${mixin.host}/docs`;
|
||||
mixin.stats_url = `${mixin.scheme}://${mixin.host}/stats`;
|
||||
mixin.status_url = `${mixin.scheme}://${mixin.host}/status`;
|
||||
mixin.drive_url = `${mixin.scheme}://${mixin.host}/files`;
|
||||
mixin.user_agent = `Misskey/${pkg.version} (${config.url})`;
|
||||
mixin.wsScheme = mixin.scheme.replace('http', 'ws');
|
||||
mixin.wsUrl = `${mixin.wsScheme}://${mixin.host}`;
|
||||
mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`;
|
||||
mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`;
|
||||
mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`;
|
||||
mixin.userAgent = `Misskey/${pkg.version} (${config.url})`;
|
||||
|
||||
if (config.autoAdmin == null) config.autoAdmin = false;
|
||||
|
||||
|
@ -49,16 +49,12 @@ export type Mixin = {
|
||||
host: string;
|
||||
hostname: string;
|
||||
scheme: string;
|
||||
ws_scheme: string;
|
||||
api_url: string;
|
||||
ws_url: string;
|
||||
auth_url: string;
|
||||
docs_url: string;
|
||||
stats_url: string;
|
||||
status_url: string;
|
||||
dev_url: string;
|
||||
drive_url: string;
|
||||
user_agent: string;
|
||||
wsScheme: string;
|
||||
apiUrl: string;
|
||||
wsUrl: string;
|
||||
authUrl: string;
|
||||
driveUrl: string;
|
||||
userAgent: string;
|
||||
};
|
||||
|
||||
export type Config = Source & Mixin;
|
||||
|
@ -1,80 +1,3 @@
|
||||
# Misskey API
|
||||
|
||||
MisskeyのWeb APIを使って、プログラムからMisskeyの様々な機能にアクセスすることができます。
|
||||
APIを自分のアカウントから利用する場合(自分のアカウントのみ操作したい場合)と、アプリケーションから利用する場合(不特定のアカウントを操作したい場合)とで利用手順が異なりますので、それぞれのケースについて説明します。
|
||||
|
||||
## 自分の所有するアカウントからAPIにアクセスする場合
|
||||
「設定 > API」で、APIにアクセスするのに必要なAPIキーを取得してください。
|
||||
APIにアクセスする際には、リクエストにAPIキーを「i」というパラメータ名で含めます。
|
||||
|
||||
<div class="ui info warn">
|
||||
<p><i class="fas fa-exclamation-triangle"></i> アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。</p>
|
||||
</div>
|
||||
|
||||
APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。
|
||||
|
||||
## アプリケーションからAPIにアクセスする場合
|
||||
直接ユーザーのAPIキーをアプリケーションが扱うのは危険なので、
|
||||
アプリケーションからAPIを利用する際には、アプリケーションとアプリケーションを利用するユーザーが結び付けられた専用のトークン(アクセストークン)をMisskeyに発行してもらい、
|
||||
そのトークンをリクエストのパラメータに含める必要があります。
|
||||
|
||||
<div class="ui info">
|
||||
<p><i class="fas fa-info-circle"></i> アクセストークンは、ユーザーが自分のアカウントにあなたのアプリケーションがアクセスすることを許可した場合のみ発行されます</p>
|
||||
</div>
|
||||
|
||||
### 1.アプリケーションを登録する
|
||||
まず、あなたのアプリケーションやWebサービス(以後、あなたのアプリと呼びます)をMisskeyに登録します。
|
||||
[デベロッパーセンター](/dev)にアクセスし、「アプリ > アプリ作成」からアプリを作成してください。
|
||||
フォームの記入欄の説明は以下の通りです:
|
||||
|
||||
| 名前 | 説明 |
|
||||
|---|---|
|
||||
| アプリケーション名 | あなたのアプリの名称。 |
|
||||
| アプリの概要 | あなたのアプリの簡単な説明や紹介。 |
|
||||
| コールバックURL | ユーザーが後述する認証フォームで認証を終えた際にリダイレクトするURLを設定できます。あなたのアプリがWebサービスである場合に有用です。 |
|
||||
| 権限 | あなたのアプリが要求する権限。ここで要求した機能だけがAPIからアクセスできます。 |
|
||||
|
||||
登録が済むとあなたのアプリのシークレットキーが入手できます。このシークレットキーは後で使用します。
|
||||
|
||||
<div class="ui info warn">
|
||||
<p><i class="fas fa-exclamation-triangle"></i> アプリに成りすまされる可能性があるため、極力このシークレットキーは公開しないようにしてください。</p>
|
||||
</div>
|
||||
|
||||
### 2.ユーザーに認証させる
|
||||
アプリを使ってもらうには、ユーザーにアカウントへのアクセスの許可をもらう必要があります。
|
||||
|
||||
認証セッションを開始するには、%API_URL%/auth/session/generate へパラメータに appSecret としてシークレットキーを含めたリクエストを送信します。
|
||||
リクエスト形式はJSONで、メソッドはPOSTです。
|
||||
レスポンスとして認証セッションのトークンや認証フォームのURLが取得できるので、認証フォームのURLをブラウザで表示し、ユーザーにフォームを提示してください。
|
||||
|
||||
あなたのアプリがコールバックURLを設定している場合、
|
||||
ユーザーがあなたのアプリの連携を許可すると設定しているコールバックURLに token という名前でセッションのトークンが含まれたクエリを付けてリダイレクトします。
|
||||
|
||||
あなたのアプリがコールバックURLを設定していない場合、ユーザーがあなたのアプリの連携を許可したことを(何らかの方法で(たとえばボタンを押させるなど))確認出来るようにしてください。
|
||||
|
||||
### 3.ユーザーのアクセストークンを取得する
|
||||
ユーザーが連携を許可したら、%API_URL%/auth/session/userkey へ次のパラメータを含むリクエストを送信します:
|
||||
|
||||
| 名前 | 型 | 説明 |
|
||||
|---|---|---|
|
||||
| appSecret | string | アプリのシークレットキー |
|
||||
| token | string | セッションのトークン |
|
||||
|
||||
上手くいけば、認証したユーザーのアクセストークンがレスポンスとして取得できます。おめでとうございます!
|
||||
|
||||
アクセストークンが取得できたら、「ユーザーのアクセストークン+あなたのアプリのシークレットキーをsha256したもの」を「i」というパラメータでリクエストに含めると、APIにアクセスすることができます。
|
||||
|
||||
「i」パラメータの生成方法を擬似コードで表すと次のようになります:
|
||||
<pre><code>const i = sha256(accessToken + secretKey);</code></pre>
|
||||
|
||||
APIの詳しい使用法は「Misskey APIの利用」セクションをご覧ください。
|
||||
|
||||
## Misskey APIの利用
|
||||
APIはすべてリクエストのパラメータ・レスポンスともにJSON形式です。また、すべてのエンドポイントはPOSTメソッドのみ受け付けます。
|
||||
|
||||
ストリーミングAPIも提供しています。
|
||||
|
||||
APIリファレンスもご確認ください。
|
||||
|
||||
### レートリミット
|
||||
Misskey APIにはレートリミットがあり、短時間のうちに多数のリクエストを送信すると、一定時間APIを利用することができなくなることがあります。
|
||||
[APIリファレンス](/api-doc)
|
||||
|
@ -1,40 +0,0 @@
|
||||
@import "../style"
|
||||
|
||||
#url
|
||||
padding 8px 12px 8px 8px
|
||||
font-family Consolas, 'Courier New', Courier, Monaco, monospace
|
||||
color #fff
|
||||
background #222e40
|
||||
border-radius 4px
|
||||
overflow auto
|
||||
white-space nowrap
|
||||
|
||||
> .method
|
||||
display inline-block
|
||||
margin 0 8px 0 0
|
||||
padding 0 6px
|
||||
color #fff
|
||||
background #17afc7
|
||||
border-radius 4px
|
||||
user-select none
|
||||
pointer-events none
|
||||
|
||||
> .host
|
||||
opacity 0.7
|
||||
|
||||
#stability
|
||||
padding 8px 12px
|
||||
color #fff
|
||||
border-radius 4px
|
||||
|
||||
&.deprecated
|
||||
background #f42443
|
||||
|
||||
&.experimental
|
||||
background #f2781a
|
||||
|
||||
&.stable
|
||||
background #3dcc90
|
||||
|
||||
> b
|
||||
margin-left 4px
|
@ -1,81 +0,0 @@
|
||||
extends ../../base
|
||||
include ../mixins
|
||||
|
||||
block meta
|
||||
link(rel="stylesheet" href="/docs/assets/api/endpoints/style.css")
|
||||
|
||||
block main
|
||||
h1= title
|
||||
|
||||
p#url
|
||||
span.method POST
|
||||
span.host
|
||||
= endpointUrl.host
|
||||
| /
|
||||
span.path= endpointUrl.path
|
||||
|
||||
- var stability = endpoint.stability || 'experimental';
|
||||
p#stability(class=stability)
|
||||
| Stability:
|
||||
b= stability
|
||||
|
||||
if endpoint.desc
|
||||
p#desc= endpoint.desc[lang] || endpoint.desc['ja-JP']
|
||||
|
||||
if endpoint.requireCredential
|
||||
div.ui.info: p
|
||||
i.fas.fa-id-card-alt(style="margin-right: 4px")
|
||||
= i18n('docs.api.endpoints.require-credential')
|
||||
|
||||
if endpoint.kind
|
||||
div.ui.info: p
|
||||
i.fas.fa-unlock-alt(style="margin-right: 4px")
|
||||
!= i18n('docs.api.endpoints.require-permission').replace('{permission}', `<code>${endpoint.kind}</code>`)
|
||||
|
||||
if endpoint.limit
|
||||
div.ui.info.warn: p
|
||||
i.far.fa-clock(style="margin-right: 4px")
|
||||
b!= i18n('docs.api.endpoints.has-limit')
|
||||
if endpoint.limit.duration
|
||||
!= i18n('docs.api.endpoints.duration-limit').replace('{duration}', endpoint.limit.duration).replace('{max}', endpoint.limit.max)
|
||||
if endpoint.limit.minInterval
|
||||
!= i18n('docs.api.endpoints.min-interval-limit').replace('{interval}', endpoint.limit.minInterval)
|
||||
|
||||
if params && Object.keys(params).length > 0
|
||||
section
|
||||
h2= i18n('docs.api.endpoints.params')
|
||||
+propTable(params)
|
||||
|
||||
if paramDefs
|
||||
each paramDef in paramDefs
|
||||
section(id= paramDef.name)
|
||||
h3= paramDef.name
|
||||
+propTable(paramDef.params)
|
||||
if params && Object.keys(params).length == 0
|
||||
section
|
||||
h2= i18n('docs.api.endpoints.params')
|
||||
p= i18n('docs.api.endpoints.no-params')
|
||||
|
||||
if res
|
||||
section
|
||||
h2= i18n('docs.api.endpoints.res')
|
||||
|
||||
if resProps
|
||||
+propTable(resProps)
|
||||
|
||||
if resDefs
|
||||
each resDef in resDefs
|
||||
section(id= resDef.name)
|
||||
h3= resDef.name
|
||||
+propTable(resDef.props)
|
||||
else
|
||||
if res.type.startsWith('entity')
|
||||
a(href=`/docs/${lang}/api/entities/${kebab(res.entity)}`)= res.entity
|
||||
|
||||
block footer
|
||||
div.ui.info: p
|
||||
i.fas.fa-info-circle(style="margin-right: 4px")
|
||||
= i18n('docs.api.endpoints.generated')
|
||||
p
|
||||
= i18n('docs.api.endpoints.show-src')
|
||||
a(href=src target="_blank")= i18n('docs.api.endpoints.show-src-link')
|
@ -1,90 +0,0 @@
|
||||
name: "DriveFile"
|
||||
|
||||
desc:
|
||||
ja-JP: "ドライブのファイル。"
|
||||
en-US: "A file of Drive."
|
||||
|
||||
props:
|
||||
id:
|
||||
type: "id"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ファイルID"
|
||||
en-US: "The ID of this file"
|
||||
|
||||
createdAt:
|
||||
type: "date"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "アップロード日時"
|
||||
en-US: "The upload date of this file"
|
||||
|
||||
userId:
|
||||
type: "id(User)"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "所有者ID"
|
||||
en-US: "The ID of the owner of this file"
|
||||
|
||||
user:
|
||||
type: "entity(User)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "所有者"
|
||||
en-US: "The owner of this file"
|
||||
|
||||
name:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ファイル名"
|
||||
en-US: "The name of this file"
|
||||
|
||||
md5:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ファイルのMD5ハッシュ値"
|
||||
en-US: "The md5 hash value of this file"
|
||||
|
||||
type:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ファイルの種類"
|
||||
en-US: "The type of this file"
|
||||
|
||||
datasize:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ファイルサイズ(bytes)"
|
||||
en-US: "The size of this file (bytes)"
|
||||
|
||||
url:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ファイルのURL"
|
||||
en-US: "The URL of this file"
|
||||
|
||||
folderId:
|
||||
type: "id(DriveFolder)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "フォルダID"
|
||||
en-US: "The ID of the folder of this file"
|
||||
|
||||
folder:
|
||||
type: "entity(DriveFolder)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "フォルダ"
|
||||
en-US: "The folder of this file"
|
||||
|
||||
isSensitive:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "このメディアが「閲覧注意」(NSFW)かどうか"
|
||||
en-US: "Whether this media is NSFW"
|
@ -1,41 +0,0 @@
|
||||
name: "DriveFolder"
|
||||
|
||||
desc:
|
||||
ja-JP: "ドライブのフォルダを表します。"
|
||||
en-US: "A folder of Drive."
|
||||
|
||||
props:
|
||||
id:
|
||||
type: "id"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "フォルダID"
|
||||
en-US: "The ID of this folder"
|
||||
|
||||
createdAt:
|
||||
type: "date"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "作成日時"
|
||||
en-US: "The created date of this folder"
|
||||
|
||||
userId:
|
||||
type: "id(User)"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "所有者ID"
|
||||
en-US: "The ID of the owner of this folder"
|
||||
|
||||
parentId:
|
||||
type: "entity(DriveFolder)"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "親フォルダのID (ルートなら null)"
|
||||
en-US: "The ID of parent folder"
|
||||
|
||||
name:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "フォルダ名"
|
||||
en-US: "The name of this folder"
|
@ -1,211 +0,0 @@
|
||||
name: "Note"
|
||||
|
||||
desc:
|
||||
ja-JP: "投稿。"
|
||||
en-US: "A note."
|
||||
|
||||
props:
|
||||
id:
|
||||
type: "id"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "投稿ID"
|
||||
en-US: "The ID of this note"
|
||||
|
||||
createdAt:
|
||||
type: "date"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "投稿日時"
|
||||
en-US: "The posted date of this note"
|
||||
|
||||
viaMobile:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
||||
en-US: "Whether this note sent via a mobile device"
|
||||
|
||||
localOnly:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "ローカルのみに公開する投稿か否か"
|
||||
en-US: "Whether this note is no federation"
|
||||
|
||||
text:
|
||||
type: "string"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "投稿の本文"
|
||||
en-US: "The text of this note"
|
||||
|
||||
fileIds:
|
||||
type: "id(DriveFile)[]"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "添付されているファイルのID (なければレスポンスでは空配列)"
|
||||
en-US: "The IDs of the attached files (empty array for response if no files is attached)"
|
||||
|
||||
files:
|
||||
type: "entity(DriveFile)[]"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "添付されているファイル"
|
||||
en-US: "The attached files"
|
||||
|
||||
userId:
|
||||
type: "id(User)"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "投稿者ID"
|
||||
en-US: "The ID of author of this note"
|
||||
|
||||
user:
|
||||
type: "entity(User)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "投稿者"
|
||||
en-US: "The author of this note"
|
||||
|
||||
myReaction:
|
||||
type: "string"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "この投稿に対する自分の<a href='/docs/api/reactions'>リアクション</a>"
|
||||
en-US: "The your <a href='/docs/api/reactions'>reaction</a> of this note"
|
||||
|
||||
renoteCount:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "この投稿がRenoteされた数"
|
||||
en-US: "The number of renotes for this post"
|
||||
|
||||
repliesCount:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "この投稿に返信された数"
|
||||
en-US: "The number of replies to this post"
|
||||
|
||||
reactionCounts:
|
||||
type: "object"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "<a href='/docs/api/reactions'>リアクション</a>をキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
|
||||
|
||||
replyId:
|
||||
type: "id(Note)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "返信した投稿のID"
|
||||
en-US: "The ID of the replyed note"
|
||||
|
||||
reply:
|
||||
type: "entity(Note)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "返信した投稿"
|
||||
en-US: "The replyed note"
|
||||
|
||||
renoteId:
|
||||
type: "id(Note)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "引用した投稿のID"
|
||||
en-US: "The ID of the quoted note"
|
||||
|
||||
renote:
|
||||
type: "entity(Note)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "引用した投稿"
|
||||
en-US: "The quoted note"
|
||||
|
||||
poll:
|
||||
type: "object"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "投票"
|
||||
en-US: "The poll"
|
||||
|
||||
props:
|
||||
choices:
|
||||
type: "object[]"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "投票の選択肢"
|
||||
en-US: "The choices of this poll"
|
||||
|
||||
props:
|
||||
id:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "選択肢ID"
|
||||
en-US: "The ID of this choice"
|
||||
|
||||
isVoted:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "自分がこの選択肢に投票したかどうか"
|
||||
en-US: "Whether you voted to this choice"
|
||||
|
||||
text:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "選択肢本文"
|
||||
en-US: "The text of this choice"
|
||||
|
||||
votes:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "この選択肢に投票された数"
|
||||
en-US: "The number voted for this choice"
|
||||
geo:
|
||||
type: "object"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "位置情報"
|
||||
en-US: "Geo location"
|
||||
|
||||
props:
|
||||
coordinates:
|
||||
type: "number[]"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "座標。最初に経度:-180〜180で表す。最後に緯度:-90〜90で表す。"
|
||||
|
||||
altitude:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "高度。メートル単位で表す。"
|
||||
|
||||
accuracy:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "緯度、経度の精度。メートル単位で表す。"
|
||||
|
||||
altitudeAccuracy:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "高度の精度。メートル単位で表す。"
|
||||
|
||||
heading:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "方角。0〜360の角度で表す。0が北、90が東、180が南、270が西。"
|
||||
|
||||
speed:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "速度。メートル / 秒数で表す。"
|
@ -1 +0,0 @@
|
||||
@import "../style"
|
@ -1,174 +0,0 @@
|
||||
name: "User"
|
||||
|
||||
desc:
|
||||
ja-JP: "ユーザー。"
|
||||
en-US: "A user."
|
||||
|
||||
props:
|
||||
id:
|
||||
type: "id"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ユーザーID"
|
||||
en-US: "The ID of this user"
|
||||
|
||||
createdAt:
|
||||
type: "date"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "アカウント作成日時"
|
||||
en-US: "The registered date of this user"
|
||||
|
||||
username:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ユーザー名"
|
||||
en-US: "The username of this user"
|
||||
|
||||
description:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "アカウントの説明(自己紹介)"
|
||||
en-US: "The description of this user"
|
||||
|
||||
avatarId:
|
||||
type: "id(DriveFile)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "アバターのID"
|
||||
en-US: "The ID of the avatar of this user"
|
||||
|
||||
avatarUrl:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "アバターのURL"
|
||||
en-US: "The URL of the avatar of this user"
|
||||
|
||||
bannerId:
|
||||
type: "id(DriveFile)"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "バナーのID"
|
||||
en-US: "The ID of the banner of this user"
|
||||
|
||||
bannerUrl:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "バナーのURL"
|
||||
en-US: "The URL of the banner of this user"
|
||||
|
||||
followersCount:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "フォロワーの数"
|
||||
en-US: "The number of the followers for this user"
|
||||
|
||||
followingCount:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "フォローしているユーザーの数"
|
||||
en-US: "The number of the following users for this user"
|
||||
|
||||
isFollowing:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "自分がこのユーザーをフォローしているか"
|
||||
|
||||
isFollowed:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "自分がこのユーザーにフォローされているか"
|
||||
|
||||
isMuted:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "自分がこのユーザーをミュートしているか"
|
||||
en-US: "Whether you muted this user"
|
||||
|
||||
notesCount:
|
||||
type: "number"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "投稿の数"
|
||||
en-US: "The number of the notes of this user"
|
||||
|
||||
pinnedNotes:
|
||||
type: "entity(Note)[]"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "ピン留めされた投稿"
|
||||
en-US: "The pinned note of this user"
|
||||
|
||||
pinnedNoteIds:
|
||||
type: "id(Note)[]"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "ピン留めされた投稿のID"
|
||||
en-US: "The ID of the pinned note of this user"
|
||||
|
||||
host:
|
||||
type: "string | null"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ホスト (例: example.com:3000)"
|
||||
en-US: "Host (e.g. example.com:3000)"
|
||||
|
||||
twitter:
|
||||
type: "object"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "連携されているTwitterアカウント情報"
|
||||
en-US: "The info of the connected twitter account of this user"
|
||||
|
||||
props:
|
||||
userId:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ユーザーID"
|
||||
en-US: "The user ID"
|
||||
|
||||
screenName:
|
||||
type: "string"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "ユーザー名"
|
||||
en-US: "The screen name of this user"
|
||||
|
||||
isBot:
|
||||
type: "boolean"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "botか否か(自己申告であることに留意)"
|
||||
en-US: "Whether is bot or not"
|
||||
|
||||
profile:
|
||||
type: "object"
|
||||
optional: false
|
||||
desc:
|
||||
ja-JP: "プロフィール"
|
||||
en-US: "The profile of this user"
|
||||
|
||||
props:
|
||||
location:
|
||||
type: "string"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "場所"
|
||||
en-US: "The location of this user"
|
||||
|
||||
birthday:
|
||||
type: "string"
|
||||
optional: true
|
||||
desc:
|
||||
ja-JP: "誕生日 (YYYY-MM-DD)"
|
||||
en-US: "The birthday of this user (YYYY-MM-DD)"
|
@ -1,20 +0,0 @@
|
||||
extends ../../base
|
||||
include ../mixins
|
||||
|
||||
block meta
|
||||
link(rel="stylesheet" href="/docs/assets/api/entities/style.css")
|
||||
|
||||
block main
|
||||
h1= name
|
||||
|
||||
p#desc= desc[lang] || desc['ja-JP']
|
||||
|
||||
section
|
||||
h2= i18n('docs.api.entities.properties')
|
||||
+propTable(props)
|
||||
|
||||
if propDefs
|
||||
each propDef in propDefs
|
||||
section(id= propDef.name)
|
||||
h3= propDef.name
|
||||
+propTable(propDef.props)
|
@ -1,34 +0,0 @@
|
||||
mixin type(prop)
|
||||
i= prop.type
|
||||
if prop.kind == 'id'
|
||||
if prop.entity
|
||||
| (
|
||||
a(href=`/docs/${lang}/api/entities/${kebab(prop.entity)}`)= prop.entity
|
||||
| ID)
|
||||
else
|
||||
| (ID)
|
||||
else if prop.kind == 'entity'
|
||||
| (
|
||||
a(href=`/docs/${lang}/api/entities/${kebab(prop.entity)}`)= prop.entity
|
||||
| )
|
||||
else if prop.kind == 'object'
|
||||
if prop.hasDef
|
||||
| (
|
||||
a(href=`#${prop.name}`)= prop.name
|
||||
| )
|
||||
else if prop.kind == 'date'
|
||||
| (Date)
|
||||
|
||||
mixin propTable(props)
|
||||
table.props
|
||||
thead: tr
|
||||
th= i18n('docs.api.props.name')
|
||||
th= i18n('docs.api.props.type')
|
||||
th= i18n('docs.api.props.description')
|
||||
tbody
|
||||
each prop in props
|
||||
tr
|
||||
td.name= prop.name
|
||||
td.type
|
||||
+type(prop)
|
||||
td.desc!= prop.desc ? prop.desc[lang] || prop.desc['ja-JP'] : null
|
@ -1,11 +0,0 @@
|
||||
@import "../style"
|
||||
|
||||
table.props
|
||||
.name
|
||||
font-weight bold
|
||||
|
||||
.name
|
||||
.type
|
||||
.optional
|
||||
font-family Consolas, 'Courier New', Courier, Monaco, monospace
|
||||
|
@ -17,17 +17,6 @@ html(lang= lang)
|
||||
ul
|
||||
each doc in docs
|
||||
li: a(href=`/docs/${lang}/${doc.name}`)= doc.title[lang] || doc.title['ja-JP']
|
||||
section
|
||||
h2 API
|
||||
ul
|
||||
li Entities
|
||||
ul
|
||||
each entity in entities
|
||||
li: a(href=`/docs/${lang}/api/entities/${kebab(entity)}`)= entity
|
||||
li Endpoints
|
||||
ul
|
||||
each endpoint in endpoints
|
||||
li: a(href=`/docs/${lang}/api/endpoints/${kebab(endpoint.name)}`)= endpoint.name
|
||||
main
|
||||
article
|
||||
block main
|
||||
|
@ -26,6 +26,8 @@ export type ObjectId = mongo.ObjectID;
|
||||
* ID
|
||||
*/
|
||||
export default class ID<Maybe = string> extends Context<string | Maybe> {
|
||||
public readonly name = 'ID';
|
||||
|
||||
constructor(optional = false, nullable = false) {
|
||||
super(optional, nullable);
|
||||
|
||||
@ -38,7 +40,7 @@ export default class ID<Maybe = string> extends Context<string | Maybe> {
|
||||
}
|
||||
|
||||
public getType() {
|
||||
return super.getType('string');
|
||||
return super.getType('String');
|
||||
}
|
||||
|
||||
public makeOptional(): ID<undefined> {
|
||||
|
@ -14,9 +14,9 @@ export default function(file: IDriveFile, thumbnail = false): string {
|
||||
}
|
||||
} else {
|
||||
if (thumbnail) {
|
||||
return `${config.drive_url}/${file._id}?thumbnail`;
|
||||
return `${config.driveUrl}/${file._id}?thumbnail`;
|
||||
} else {
|
||||
return `${config.drive_url}/${file._id}?web`;
|
||||
return `${config.driveUrl}/${file._id}?web`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,5 +27,5 @@ export function getOriginalUrl(file: IDriveFile) {
|
||||
}
|
||||
|
||||
const accessKey = file.metadata ? file.metadata.accessKey : null;
|
||||
return `${config.drive_url}/${file._id}${accessKey ? '?original=' + accessKey : ''}`;
|
||||
return `${config.driveUrl}/${file._id}${accessKey ? '?original=' + accessKey : ''}`;
|
||||
}
|
||||
|
13
src/misc/identifiable-error.ts
Normal file
13
src/misc/identifiable-error.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* ID付きエラー
|
||||
*/
|
||||
export class IdentifiableError extends Error {
|
||||
public message: string;
|
||||
public id: string;
|
||||
|
||||
constructor(id: string, message?: string) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@ -1,15 +1,13 @@
|
||||
import * as os from 'os';
|
||||
import * as sysUtils from 'systeminformation';
|
||||
import Logger from "./logger";
|
||||
import Logger from './logger';
|
||||
|
||||
export async function showMachineInfo(parentLogger: Logger) {
|
||||
const logger = parentLogger.createSubLogger('machine');
|
||||
logger.debug(`Hostname: ${os.hostname()}`);
|
||||
logger.debug(`Platform: ${process.platform}`);
|
||||
logger.debug(`Architecture: ${process.arch}`);
|
||||
logger.debug(`CPU: ${os.cpus().length} core`);
|
||||
logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`);
|
||||
const mem = await sysUtils.mem();
|
||||
const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1);
|
||||
const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1);
|
||||
logger.debug(`MEM: ${totalmem}GB (available: ${availmem}GB)`);
|
||||
logger.debug(`CPU: ${os.cpus().length} core MEM: ${totalmem}GB (available: ${availmem}GB)`);
|
||||
}
|
||||
|
@ -76,8 +76,8 @@ export const pack = (
|
||||
}
|
||||
|
||||
_app.iconUrl = _app.icon != null
|
||||
? `${config.drive_url}/${_app.icon}`
|
||||
: `${config.drive_url}/app-default.jpg`;
|
||||
? `${config.driveUrl}/${_app.icon}`
|
||||
: `${config.driveUrl}/app-default.jpg`;
|
||||
|
||||
if (me) {
|
||||
// 既に連携しているか
|
||||
|
@ -55,6 +55,8 @@ type IUserBase = {
|
||||
emojis?: string[];
|
||||
tags?: string[];
|
||||
|
||||
isDeleted: boolean;
|
||||
|
||||
/**
|
||||
* 凍結されているか否か
|
||||
*/
|
||||
@ -170,7 +172,7 @@ export const isRemoteUser = (user: any): user is IRemoteUser =>
|
||||
!isLocalUser(user);
|
||||
|
||||
//#region Validators
|
||||
export function validateUsername(username: string, remote?: boolean): boolean {
|
||||
export function validateUsername(username: string, remote = false): boolean {
|
||||
return typeof username == 'string' && (remote ? /^\w([\w-]*\w)?$/ : /^\w{1,20}$/).test(username);
|
||||
}
|
||||
|
||||
@ -348,7 +350,7 @@ export const pack = (
|
||||
}
|
||||
|
||||
if (_user.avatarUrl == null) {
|
||||
_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
|
||||
_user.avatarUrl = `${config.driveUrl}/default-avatar.jpg`;
|
||||
}
|
||||
|
||||
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
||||
|
38
src/prelude/schema.ts
Normal file
38
src/prelude/schema.ts
Normal file
@ -0,0 +1,38 @@
|
||||
export type Schema = {
|
||||
type: 'number' | 'string' | 'array' | 'object' | any;
|
||||
optional?: boolean;
|
||||
items?: Schema;
|
||||
properties?: Obj;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type Obj = { [key: string]: Schema };
|
||||
|
||||
export type ObjType<s extends Obj> = { [P in keyof s]: SchemaType<s[P]> };
|
||||
|
||||
// https://qiita.com/hrsh7th@github/items/84e8968c3601009cdcf2
|
||||
type MyType<T extends Schema> = {
|
||||
0: any;
|
||||
1: SchemaType<T>;
|
||||
}[T extends Schema ? 1 : 0];
|
||||
|
||||
export type SchemaType<p extends Schema> =
|
||||
p['type'] extends 'number' ? number :
|
||||
p['type'] extends 'string' ? string :
|
||||
p['type'] extends 'array' ? MyType<p['items']>[] :
|
||||
p['type'] extends 'object' ? ObjType<p['properties']> :
|
||||
any;
|
||||
|
||||
export function convertOpenApiSchema(schema: Schema) {
|
||||
const x = JSON.parse(JSON.stringify(schema)); // copy
|
||||
if (!['string', 'number', 'boolean', 'array', 'object'].includes(x.type)) {
|
||||
x['$ref'] = `#/components/schemas/${x.type}`;
|
||||
}
|
||||
if (x.type === 'object' && x.properties) {
|
||||
x.required = Object.entries(x.properties).filter(([k, v]: any) => !v.isOptional).map(([k, v]: any) => k);
|
||||
for (const k of Object.keys(x.properties)) {
|
||||
x.properties[k] = convertOpenApiSchema(x.properties[k]);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
41
src/prelude/xml.ts
Normal file
41
src/prelude/xml.ts
Normal file
@ -0,0 +1,41 @@
|
||||
const map: Record<string, string> = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'\'': '''
|
||||
};
|
||||
|
||||
const beginingOfCDATA = '<![CDATA[';
|
||||
const endOfCDATA = ']]>';
|
||||
|
||||
export function escapeValue(x: string): string {
|
||||
let insideOfCDATA = false;
|
||||
let builder = '';
|
||||
for (
|
||||
let i = 0;
|
||||
i < x.length;
|
||||
) {
|
||||
if (insideOfCDATA) {
|
||||
if (x.slice(i, i + beginingOfCDATA.length) === beginingOfCDATA) {
|
||||
insideOfCDATA = true;
|
||||
i += beginingOfCDATA.length;
|
||||
} else {
|
||||
builder += x[i++];
|
||||
}
|
||||
} else {
|
||||
if (x.slice(i, i + endOfCDATA.length) === endOfCDATA) {
|
||||
insideOfCDATA = false;
|
||||
i += endOfCDATA.length;
|
||||
} else {
|
||||
const b = x[i++];
|
||||
builder += map[b] || b;
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
export function escapeAttribute(x: string): string {
|
||||
return Object.entries(map).reduce((a, [k, v]) => a.replace(k, v), x);
|
||||
}
|
@ -70,6 +70,32 @@ export function processInbox(activity: any, signature: httpSignature.IParsedSign
|
||||
}
|
||||
}
|
||||
|
||||
export function createDeleteNotesJob(user: ILocalUser) {
|
||||
const data = {
|
||||
type: 'deleteNotes',
|
||||
user: user
|
||||
};
|
||||
|
||||
if (queueAvailable && enableQueueProcessing) {
|
||||
return queue.createJob(data).save();
|
||||
} else {
|
||||
return handler({ data }, () => {});
|
||||
}
|
||||
}
|
||||
|
||||
export function createDeleteDriveFilesJob(user: ILocalUser) {
|
||||
const data = {
|
||||
type: 'deleteDriveFiles',
|
||||
user: user
|
||||
};
|
||||
|
||||
if (queueAvailable && enableQueueProcessing) {
|
||||
return queue.createJob(data).save();
|
||||
} else {
|
||||
return handler({ data }, () => {});
|
||||
}
|
||||
}
|
||||
|
||||
export function createExportNotesJob(user: ILocalUser) {
|
||||
const data = {
|
||||
type: 'exportNotes',
|
||||
|
55
src/queue/processors/delete-drive-files.ts
Normal file
55
src/queue/processors/delete-drive-files.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import * as bq from 'bee-queue';
|
||||
import * as mongo from 'mongodb';
|
||||
|
||||
import { queueLogger } from '../logger';
|
||||
import User from '../../models/user';
|
||||
import DriveFile from '../../models/drive-file';
|
||||
import deleteFile from '../../services/drive/delete-file';
|
||||
|
||||
const logger = queueLogger.createSubLogger('delete-drive-files');
|
||||
|
||||
export async function deleteDriveFiles(job: bq.Job, done: any): Promise<void> {
|
||||
logger.info(`Deleting drive files of ${job.data.user._id} ...`);
|
||||
|
||||
const user = await User.findOne({
|
||||
_id: new mongo.ObjectID(job.data.user._id.toString())
|
||||
});
|
||||
|
||||
let deletedCount = 0;
|
||||
let ended = false;
|
||||
let cursor: any = null;
|
||||
|
||||
while (!ended) {
|
||||
const files = await DriveFile.find({
|
||||
userId: user._id,
|
||||
...(cursor ? { _id: { $gt: cursor } } : {})
|
||||
}, {
|
||||
limit: 100,
|
||||
sort: {
|
||||
_id: 1
|
||||
}
|
||||
});
|
||||
|
||||
if (files.length === 0) {
|
||||
ended = true;
|
||||
if (job.reportProgress) job.reportProgress(100);
|
||||
break;
|
||||
}
|
||||
|
||||
cursor = files[files.length - 1]._id;
|
||||
|
||||
for (const file of files) {
|
||||
await deleteFile(file);
|
||||
deletedCount++;
|
||||
}
|
||||
|
||||
const total = await DriveFile.count({
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
if (job.reportProgress) job.reportProgress(deletedCount / total);
|
||||
}
|
||||
|
||||
logger.succ(`All drive files (${deletedCount}) of ${user._id} has been deleted.`);
|
||||
done();
|
||||
}
|
55
src/queue/processors/delete-notes.ts
Normal file
55
src/queue/processors/delete-notes.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import * as bq from 'bee-queue';
|
||||
import * as mongo from 'mongodb';
|
||||
|
||||
import { queueLogger } from '../logger';
|
||||
import Note from '../../models/note';
|
||||
import deleteNote from '../../services/note/delete';
|
||||
import User from '../../models/user';
|
||||
|
||||
const logger = queueLogger.createSubLogger('delete-notes');
|
||||
|
||||
export async function deleteNotes(job: bq.Job, done: any): Promise<void> {
|
||||
logger.info(`Deleting notes of ${job.data.user._id} ...`);
|
||||
|
||||
const user = await User.findOne({
|
||||
_id: new mongo.ObjectID(job.data.user._id.toString())
|
||||
});
|
||||
|
||||
let deletedCount = 0;
|
||||
let ended = false;
|
||||
let cursor: any = null;
|
||||
|
||||
while (!ended) {
|
||||
const notes = await Note.find({
|
||||
userId: user._id,
|
||||
...(cursor ? { _id: { $gt: cursor } } : {})
|
||||
}, {
|
||||
limit: 100,
|
||||
sort: {
|
||||
_id: 1
|
||||
}
|
||||
});
|
||||
|
||||
if (notes.length === 0) {
|
||||
ended = true;
|
||||
if (job.reportProgress) job.reportProgress(100);
|
||||
break;
|
||||
}
|
||||
|
||||
cursor = notes[notes.length - 1]._id;
|
||||
|
||||
for (const note of notes) {
|
||||
await deleteNote(user, note, true);
|
||||
deletedCount++;
|
||||
}
|
||||
|
||||
const total = await Note.count({
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
if (job.reportProgress) job.reportProgress(deletedCount / total);
|
||||
}
|
||||
|
||||
logger.succ(`All notes (${deletedCount}) of ${user._id} has been deleted.`);
|
||||
done();
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import deliver from './http/deliver';
|
||||
import processInbox from './http/process-inbox';
|
||||
import { deleteNotes } from './delete-notes';
|
||||
import { deleteDriveFiles } from './delete-drive-files';
|
||||
import { exportNotes } from './export-notes';
|
||||
import { exportFollowing } from './export-following';
|
||||
import { exportMute } from './export-mute';
|
||||
@ -9,6 +11,8 @@ import { queueLogger } from '../logger';
|
||||
const handlers: any = {
|
||||
deliver,
|
||||
processInbox,
|
||||
deleteNotes,
|
||||
deleteDriveFiles,
|
||||
exportNotes,
|
||||
exportFollowing,
|
||||
exportMute,
|
||||
|
@ -45,7 +45,7 @@ export default (user: ILocalUser, url: string, object: any) => new Promise(async
|
||||
timeout,
|
||||
headers: {
|
||||
'Host': host,
|
||||
'User-Agent': config.user_agent,
|
||||
'User-Agent': config.userAgent,
|
||||
'Content-Type': 'application/activity+json',
|
||||
'Digest': `SHA-256=${hash}`
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ export default class Resolver {
|
||||
proxy: config.proxy,
|
||||
timeout: this.timeout,
|
||||
headers: {
|
||||
'User-Agent': config.user_agent,
|
||||
'User-Agent': config.userAgent,
|
||||
Accept: 'application/activity+json, application/ld+json'
|
||||
},
|
||||
json: true
|
||||
|
@ -48,7 +48,6 @@ export default async (ctx: Router.IRouterContext) => {
|
||||
const partOf = `${config.url}/users/${userId}/followers`;
|
||||
|
||||
if (page) {
|
||||
// Construct query
|
||||
const query = {
|
||||
followeeId: user._id
|
||||
} as any;
|
||||
|
@ -48,7 +48,6 @@ export default async (ctx: Router.IRouterContext) => {
|
||||
const partOf = `${config.url}/users/${userId}/following`;
|
||||
|
||||
if (page) {
|
||||
// Construct query
|
||||
const query = {
|
||||
followerId: user._id
|
||||
} as any;
|
||||
|
@ -78,7 +78,6 @@ export default async (ctx: Router.IRouterContext) => {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// Issue query
|
||||
const notes = await Note
|
||||
.find(query, {
|
||||
limit: limit,
|
||||
|
@ -3,45 +3,44 @@ import * as Koa from 'koa';
|
||||
import { IEndpoint } from './endpoints';
|
||||
import authenticate from './authenticate';
|
||||
import call from './call';
|
||||
import { IUser } from '../../models/user';
|
||||
import { IApp } from '../../models/app';
|
||||
import { ApiError } from './error';
|
||||
|
||||
export default async (endpoint: IEndpoint, ctx: Koa.BaseContext) => {
|
||||
export default (endpoint: IEndpoint, ctx: Koa.BaseContext) => new Promise((res) => {
|
||||
const body = ctx.is('multipart/form-data') ? (ctx.req as any).body : ctx.request.body;
|
||||
|
||||
const reply = (x?: any, y?: any) => {
|
||||
if (x === undefined) {
|
||||
const reply = (x?: any, y?: ApiError) => {
|
||||
if (x == null) {
|
||||
ctx.status = 204;
|
||||
} else if (typeof x === 'number') {
|
||||
ctx.status = x;
|
||||
ctx.body = {
|
||||
error: x === 500 ? 'INTERNAL_ERROR' : y
|
||||
error: {
|
||||
message: y.message,
|
||||
code: y.code,
|
||||
id: y.id,
|
||||
kind: y.kind,
|
||||
...(y.info ? { info: y.info } : {})
|
||||
}
|
||||
};
|
||||
} else {
|
||||
ctx.body = x;
|
||||
}
|
||||
res();
|
||||
};
|
||||
|
||||
let user: IUser;
|
||||
let app: IApp;
|
||||
|
||||
// Authentication
|
||||
try {
|
||||
[user, app] = await authenticate(body['i']);
|
||||
} catch (e) {
|
||||
reply(403, 'AUTHENTICATION_FAILED');
|
||||
return;
|
||||
}
|
||||
|
||||
let res;
|
||||
|
||||
// API invoking
|
||||
try {
|
||||
res = await call(endpoint.name, user, app, body, (ctx.req as any).file);
|
||||
} catch (e) {
|
||||
reply(400, e);
|
||||
return;
|
||||
}
|
||||
|
||||
reply(res);
|
||||
};
|
||||
authenticate(body['i']).then(([user, app]) => {
|
||||
// API invoking
|
||||
call(endpoint.name, user, app, body, (ctx.req as any).file).then(res => {
|
||||
reply(res);
|
||||
}).catch(e => {
|
||||
reply(e.httpStatusCode ? e.httpStatusCode : e.kind == 'client' ? 400 : 500, e);
|
||||
});
|
||||
}).catch(() => {
|
||||
reply(403, new ApiError({
|
||||
message: 'Authentication failed. Please ensure your token is correct.',
|
||||
code: 'AUTHENTICATION_FAILED',
|
||||
id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14'
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,14 @@ import limiter from './limiter';
|
||||
import { IUser } from '../../models/user';
|
||||
import { IApp } from '../../models/app';
|
||||
import endpoints from './endpoints';
|
||||
import { ApiError } from './error';
|
||||
import { apiLogger } from './logger';
|
||||
|
||||
const accessDenied = {
|
||||
message: 'Access denied.',
|
||||
code: 'ACCESS_DENIED',
|
||||
id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e'
|
||||
};
|
||||
|
||||
export default async (endpoint: string, user: IUser, app: IApp, data: any, file?: any) => {
|
||||
const isSecure = user != null && app == null;
|
||||
@ -9,58 +17,72 @@ export default async (endpoint: string, user: IUser, app: IApp, data: any, file?
|
||||
const ep = endpoints.find(e => e.name === endpoint);
|
||||
|
||||
if (ep == null) {
|
||||
throw 'ENDPOINT_NOT_FOUND';
|
||||
throw new ApiError({
|
||||
message: 'No such endpoint.',
|
||||
code: 'NO_SUCH_ENDPOINT',
|
||||
id: 'f8080b67-5f9c-4eb7-8c18-7f1eeae8f709',
|
||||
httpStatusCode: 404
|
||||
});
|
||||
}
|
||||
|
||||
if (ep.meta.secure && !isSecure) {
|
||||
throw 'ACCESS_DENIED';
|
||||
throw new ApiError(accessDenied);
|
||||
}
|
||||
|
||||
if (ep.meta.requireCredential && user == null) {
|
||||
throw 'CREDENTIAL_REQUIRED';
|
||||
throw new ApiError({
|
||||
message: 'Credential required.',
|
||||
code: 'CREDENTIAL_REQUIRED',
|
||||
id: '1384574d-a912-4b81-8601-c7b1c4085df1',
|
||||
httpStatusCode: 401
|
||||
});
|
||||
}
|
||||
|
||||
if (ep.meta.requireCredential && user.isSuspended) {
|
||||
throw 'YOUR_ACCOUNT_HAS_BEEN_SUSPENDED';
|
||||
throw new ApiError(accessDenied, { reason: 'Your account has been suspended.' });
|
||||
}
|
||||
|
||||
if (ep.meta.requireAdmin && !user.isAdmin) {
|
||||
throw 'YOU_ARE_NOT_ADMIN';
|
||||
throw new ApiError(accessDenied, { reason: 'You are not the admin.' });
|
||||
}
|
||||
|
||||
if (ep.meta.requireModerator && !user.isAdmin && !user.isModerator) {
|
||||
throw 'YOU_ARE_NOT_MODERATOR';
|
||||
throw new ApiError(accessDenied, { reason: 'You are not a moderator.' });
|
||||
}
|
||||
|
||||
if (app && ep.meta.kind && !app.permission.some(p => p === ep.meta.kind)) {
|
||||
throw 'PERMISSION_DENIED';
|
||||
throw new ApiError({
|
||||
message: 'Your app does not have the necessary permissions to use this endpoint.',
|
||||
code: 'PERMISSION_DENIED',
|
||||
id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838',
|
||||
});
|
||||
}
|
||||
|
||||
if (ep.meta.requireCredential && ep.meta.limit) {
|
||||
try {
|
||||
await limiter(ep, user); // Rate limit
|
||||
} catch (e) {
|
||||
// drop request if limit exceeded
|
||||
throw 'RATE_LIMIT_EXCEEDED';
|
||||
}
|
||||
// Rate limit
|
||||
await limiter(ep, user).catch(e => {
|
||||
throw new ApiError({
|
||||
message: 'Rate limit exceeded. Please try again later.',
|
||||
code: 'RATE_LIMIT_EXCEEDED',
|
||||
id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef',
|
||||
httpStatusCode: 429
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let res;
|
||||
|
||||
// API invoking
|
||||
try {
|
||||
res = await ep.exec(data, user, app, file);
|
||||
} catch (e) {
|
||||
if (e && e.name == 'INVALID_PARAM') {
|
||||
throw {
|
||||
code: e.name,
|
||||
param: e.param,
|
||||
reason: e.message
|
||||
};
|
||||
} else {
|
||||
return await ep.exec(data, user, app, file).catch((e: Error) => {
|
||||
if (e instanceof ApiError) {
|
||||
throw e;
|
||||
} else {
|
||||
apiLogger.error(e);
|
||||
throw new ApiError(null, {
|
||||
e: {
|
||||
message: e.message,
|
||||
code: e.name,
|
||||
stack: e.stack
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
@ -1,18 +1,19 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import Note from "../../../models/note";
|
||||
import User, { isRemoteUser, isLocalUser } from "../../../models/user";
|
||||
import Note from '../../../models/note';
|
||||
import User, { isRemoteUser, isLocalUser } from '../../../models/user';
|
||||
import { IdentifiableError } from '../../../misc/identifiable-error';
|
||||
|
||||
/**
|
||||
* Get valied note for API processing
|
||||
* Get note for API processing
|
||||
*/
|
||||
export async function getValiedNote(noteId: mongo.ObjectID) {
|
||||
export async function getNote(noteId: mongo.ObjectID) {
|
||||
const note = await Note.findOne({
|
||||
_id: noteId,
|
||||
deletedAt: { $exists: false }
|
||||
});
|
||||
|
||||
if (note === null) {
|
||||
throw 'note not found';
|
||||
throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
|
||||
}
|
||||
|
||||
return note;
|
||||
@ -23,11 +24,22 @@ export async function getValiedNote(noteId: mongo.ObjectID) {
|
||||
*/
|
||||
export async function getUser(userId: mongo.ObjectID) {
|
||||
const user = await User.findOne({
|
||||
_id: userId
|
||||
_id: userId,
|
||||
$or: [{
|
||||
isDeleted: { $exists: false }
|
||||
}, {
|
||||
isDeleted: false
|
||||
}]
|
||||
}, {
|
||||
fields: {
|
||||
data: false,
|
||||
profile: false,
|
||||
clientSettings: false
|
||||
}
|
||||
});
|
||||
|
||||
if (user == null) {
|
||||
throw 'user not found';
|
||||
if (user === null) {
|
||||
throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.');
|
||||
}
|
||||
|
||||
return user;
|
||||
|
@ -2,6 +2,7 @@ import * as fs from 'fs';
|
||||
import { ILocalUser } from '../../models/user';
|
||||
import { IApp } from '../../models/app';
|
||||
import { IEndpointMeta } from './endpoints';
|
||||
import { ApiError } from './error';
|
||||
|
||||
type Params<T extends IEndpointMeta> = {
|
||||
[P in keyof T['params']]: T['params'][P]['transform'] extends Function
|
||||
@ -9,13 +10,19 @@ type Params<T extends IEndpointMeta> = {
|
||||
: ReturnType<T['params'][P]['validator']['get']>[0];
|
||||
};
|
||||
|
||||
export default function <T extends IEndpointMeta>(meta: T, cb: (params: Params<T>, user: ILocalUser, app: IApp, file?: any, cleanup?: Function) => Promise<any>): (params: any, user: ILocalUser, app: IApp, file?: any) => Promise<any> {
|
||||
export type Response = Record<string, any> | void;
|
||||
|
||||
export default function <T extends IEndpointMeta>(meta: T, cb: (params: Params<T>, user: ILocalUser, app: IApp, file?: any, cleanup?: Function) => Promise<Response>): (params: any, user: ILocalUser, app: IApp, file?: any) => Promise<any> {
|
||||
return (params: any, user: ILocalUser, app: IApp, file?: any) => {
|
||||
function cleanup() {
|
||||
fs.unlink(file.path, () => {});
|
||||
}
|
||||
|
||||
if (meta.requireFile && file == null) return Promise.reject('file required');
|
||||
if (meta.requireFile && file == null) return Promise.reject(new ApiError({
|
||||
message: 'File required.',
|
||||
code: 'FILE_REQUIRED',
|
||||
id: '4267801e-70d1-416a-b011-4ee502885d8b',
|
||||
}));
|
||||
|
||||
const [ps, pserr] = getParams(meta, params);
|
||||
if (pserr) {
|
||||
@ -27,17 +34,22 @@ export default function <T extends IEndpointMeta>(meta: T, cb: (params: Params<T
|
||||
};
|
||||
}
|
||||
|
||||
function getParams<T extends IEndpointMeta>(defs: T, params: any): [Params<T>, Error] {
|
||||
function getParams<T extends IEndpointMeta>(defs: T, params: any): [Params<T>, ApiError] {
|
||||
if (defs.params == null) return [params, null];
|
||||
|
||||
const x: any = {};
|
||||
let err: Error = null;
|
||||
let err: ApiError = null;
|
||||
Object.entries(defs.params).some(([k, def]) => {
|
||||
const [v, e] = def.validator.get(params[k]);
|
||||
if (e) {
|
||||
err = new Error(e.message);
|
||||
err.name = 'INVALID_PARAM';
|
||||
(err as any).param = k;
|
||||
err = new ApiError({
|
||||
message: 'Invalid param.',
|
||||
code: 'INVALID_PARAM',
|
||||
id: '3d81ceae-475f-4600-b2a8-2bc116157532',
|
||||
}, {
|
||||
param: k,
|
||||
reason: e.message
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
if (v === undefined && def.hasOwnProperty('default')) {
|
||||
|
@ -1,23 +1,36 @@
|
||||
import { Context } from 'cafy';
|
||||
import * as path from 'path';
|
||||
import * as glob from 'glob';
|
||||
import { Schema } from '../../prelude/schema';
|
||||
|
||||
export type Param = {
|
||||
validator: Context<any>;
|
||||
transform?: any;
|
||||
default?: any;
|
||||
desc?: { [key: string]: string };
|
||||
ref?: string;
|
||||
};
|
||||
|
||||
export interface IEndpointMeta {
|
||||
stability?: string; //'deprecated' | 'experimental' | 'stable';
|
||||
|
||||
desc?: { [key: string]: string };
|
||||
|
||||
tags?: string[];
|
||||
|
||||
params?: {
|
||||
[key: string]: Param;
|
||||
};
|
||||
|
||||
errors?: {
|
||||
[key: string]: {
|
||||
validator: Context<any>;
|
||||
transform?: any;
|
||||
default?: any;
|
||||
desc?: { [key: string]: string };
|
||||
ref?: string;
|
||||
message: string;
|
||||
code: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
res?: any;
|
||||
res?: Schema;
|
||||
|
||||
/**
|
||||
* このエンドポイントにリクエストするのにユーザー情報が必須か否か
|
||||
|
@ -4,6 +4,8 @@ import Report, { packMany } from '../../../../models/abuse-user-report';
|
||||
import define from '../../define';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
@ -25,11 +27,7 @@ export const meta = {
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps) => new Promise(async (res, rej) => {
|
||||
if (ps.sinceId && ps.untilId) {
|
||||
return rej('cannot set sinceId and untilId');
|
||||
}
|
||||
|
||||
export default define(meta, async (ps) => {
|
||||
const sort = {
|
||||
_id: -1
|
||||
};
|
||||
@ -51,5 +49,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
|
||||
sort: sort
|
||||
});
|
||||
|
||||
res(await packMany(reports));
|
||||
}));
|
||||
return await packMany(reports);
|
||||
});
|
||||
|
@ -4,6 +4,8 @@ import define from '../../../define';
|
||||
import { fallback } from '../../../../../prelude/symbol';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: false,
|
||||
requireModerator: true,
|
||||
|
||||
@ -46,7 +48,7 @@ const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863
|
||||
[fallback]: { _id: -1 }
|
||||
};
|
||||
|
||||
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
export default define(meta, async (ps, me) => {
|
||||
const q = {
|
||||
'metadata.deletedAt': { $exists: false },
|
||||
} as any;
|
||||
@ -61,5 +63,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
skip: ps.offset
|
||||
});
|
||||
|
||||
res(await packMany(files, { detail: true, withUser: true, self: true }));
|
||||
}));
|
||||
return await packMany(files, { detail: true, withUser: true, self: true });
|
||||
});
|
||||
|
@ -2,8 +2,11 @@ import $ from 'cafy';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
import define from '../../../define';
|
||||
import DriveFile from '../../../../../models/drive-file';
|
||||
import { ApiError } from '../../../error';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
|
||||
@ -12,17 +15,25 @@ export const meta = {
|
||||
validator: $.type(ID),
|
||||
transform: transform,
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
noSuchFile: {
|
||||
message: 'No such file.',
|
||||
code: 'NO_SUCH_FILE',
|
||||
id: 'caf3ca38-c6e5-472e-a30c-b05377dcc240'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
export default define(meta, async (ps, me) => {
|
||||
const file = await DriveFile.findOne({
|
||||
_id: ps.fileId
|
||||
});
|
||||
|
||||
if (file == null) {
|
||||
return rej('file not found');
|
||||
throw new ApiError(meta.errors.noSuchFile);
|
||||
}
|
||||
|
||||
res(file);
|
||||
}));
|
||||
return file;
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user