Merge branch 'develop' into ci
This commit is contained in:
commit
609d1b5e8b
@ -6,8 +6,6 @@ mongodb:
|
||||
db: misskey
|
||||
user: syuilo
|
||||
pass: ''
|
||||
drive:
|
||||
storage: 'db'
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
|
@ -6,8 +6,6 @@ mongodb:
|
||||
db: test-misskey
|
||||
user: admin
|
||||
pass: ''
|
||||
drive:
|
||||
storage: 'db'
|
||||
# __REDIS__
|
||||
redis:
|
||||
host: localhost
|
||||
|
@ -62,6 +62,10 @@ db:
|
||||
user: example-misskey-user
|
||||
pass: example-misskey-pass
|
||||
|
||||
# Extra Connection options
|
||||
#extra:
|
||||
# ssl: true
|
||||
|
||||
# ┌─────────────────────┐
|
||||
#───┘ Redis configuration └─────────────────────────────────────
|
||||
|
||||
@ -78,61 +82,6 @@ redis:
|
||||
# port: 9200
|
||||
# pass: null
|
||||
|
||||
# ┌────────────────────────────────────┐
|
||||
#───┘ File storage (Drive) configuration └──────────────────────
|
||||
|
||||
drive:
|
||||
storage: 'fs'
|
||||
|
||||
# OR
|
||||
|
||||
#drive:
|
||||
# storage: 'minio'
|
||||
# bucket:
|
||||
# prefix:
|
||||
# config:
|
||||
# endPoint:
|
||||
# port:
|
||||
# useSSL:
|
||||
# accessKey:
|
||||
# secretKey:
|
||||
|
||||
# S3/GCS example
|
||||
#
|
||||
# * Replace <endpoint> to
|
||||
# S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
|
||||
# GCS: use 'storage.googleapis.com'
|
||||
#
|
||||
# * Replace <region> to
|
||||
# S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
|
||||
# GCS: not needed (just delete the region line)
|
||||
#
|
||||
#drive:
|
||||
# storage: 'minio'
|
||||
# bucket: bucket-name
|
||||
# prefix: files
|
||||
# baseUrl: https://bucket-name.<endpoint>
|
||||
# config:
|
||||
# endPoint: <endpoint>
|
||||
# region: <region>
|
||||
# useSSL: true
|
||||
# accessKey: XXX
|
||||
# secretKey: YYY
|
||||
|
||||
# S3/GCS example (with CDN, custom domain)
|
||||
#
|
||||
#drive:
|
||||
# storage: 'minio'
|
||||
# bucket: drive.example.com
|
||||
# prefix: files
|
||||
# baseUrl: https://drive.example.com
|
||||
# config:
|
||||
# endPoint: <endpoint>
|
||||
# region: <region>
|
||||
# useSSL: true
|
||||
# accessKey: XXX
|
||||
# secretKey: YYY
|
||||
|
||||
# ┌───────────────┐
|
||||
#───┘ ID generation └───────────────────────────────────────────
|
||||
|
||||
|
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: syuilo
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,11 +1,23 @@
|
||||
# Visual Studio Code
|
||||
/.vscode
|
||||
|
||||
# Intelij-IDEA
|
||||
/.idea
|
||||
|
||||
# Node.js
|
||||
/node_modules
|
||||
|
||||
# yarn
|
||||
yarn.lock
|
||||
|
||||
# config
|
||||
/.config/*
|
||||
!/.config/example.yml
|
||||
!/.config/mongo_initdb_example.js
|
||||
/.vscode
|
||||
/node_modules
|
||||
|
||||
# misskey
|
||||
/build
|
||||
/built
|
||||
built
|
||||
/data
|
||||
/.cache-loader
|
||||
/db
|
||||
@ -17,7 +29,6 @@ api-docs.json
|
||||
*.log
|
||||
/redis
|
||||
*.code-workspace
|
||||
yarn.lock
|
||||
.DS_Store
|
||||
/files
|
||||
ormconfig.json
|
||||
|
823
CHANGELOG.md
823
CHANGELOG.md
@ -8,43 +8,490 @@ If you encounter any problems with updating, please try the following:
|
||||
Migration
|
||||
------------------------------
|
||||
#### 1
|
||||
`ormconfig.json`という名前で、Misskeyのインストール場所(package.jsonとかがあるディレクトリ)に新たなファイルを作る。中身は次のようにします:
|
||||
``` json
|
||||
{
|
||||
"type": "postgres",
|
||||
"host": "PostgreSQLのホスト",
|
||||
"port": 5432,
|
||||
"username": "PostgreSQLのユーザー名",
|
||||
"password": "PostgreSQLのパスワード",
|
||||
"database": "PostgreSQLのデータベース名",
|
||||
"entities": ["src/models/entities/*.ts"],
|
||||
"migrations": ["migration/*.ts"],
|
||||
"cli": {
|
||||
"migrationsDir": "migration"
|
||||
}
|
||||
}
|
||||
```
|
||||
上記の各種PostgreSQLの設定(ポートも)は、設定ファイルに書いてあるものをコピーしてください。
|
||||
|
||||
#### 2
|
||||
```
|
||||
npm i -g ts-node
|
||||
```
|
||||
|
||||
#### 3
|
||||
#### 2
|
||||
```
|
||||
ts-node ./node_modules/typeorm/cli.js migration:run
|
||||
npm run migrate
|
||||
```
|
||||
|
||||
How to migrate to v11 from v10
|
||||
------------------------------
|
||||
### 移行の注意点
|
||||
11.20.0 (2019/05/27)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 管理画面からリモートファイルのキャッシュをすべて削除できるように
|
||||
* 投稿フォームに投稿のURLをペーストしようとすると引用RNにできるように
|
||||
* モバイル版の投稿フォームにファイルをドロップできるように
|
||||
* モバイル版でも投稿の下書き自動保存ができるように
|
||||
* リモートファイルのキャッシュが期限切れになったときにサムネイルが無くならないように
|
||||
* ジョブキュー管理画面を強化
|
||||
|
||||
### 🐛Fixes
|
||||
* 投稿内のローカルなURLプレビューをクリックしたとき not found になることがある問題を修正
|
||||
* デスクトップでユーザーページに遷移するときページが再度読み込みされることがある問題を修正
|
||||
* フォロー申請自動承認オプションが常にオフで表示される問題を修正
|
||||
* ポートを設定せずに起動したときに適切なエラーメッセージが表示されない問題を修正
|
||||
* i18n
|
||||
|
||||
11.19.1 (2019/05/26)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* タグページを遷移してもタイムラインが変わらない問題を修正
|
||||
* インスタンス名を空にしてもMisskeyロゴが表示されない問題を修正
|
||||
* 投稿内のローカルなURLをクリックしたとき not found になることがある問題を修正
|
||||
* npm run init がプロセス終了しない問題を修正
|
||||
|
||||
11.19.0 (2019/05/25)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* グループのオーナーを変えられるように
|
||||
* PagesでURLプレビューを表示するように
|
||||
* サーバーログを削除できるように
|
||||
* リンク先が同じインスタンスの投稿内のURLをクリックしたときは新しいタブで開かないように
|
||||
* PostgreSQLの接続オプションを設定ファイルで設定できるように
|
||||
* いくつかのAPIでのパフォーマンスを向上
|
||||
* ユーザビリティの向上
|
||||
* デザインの調整
|
||||
|
||||
### 🐛Fixes
|
||||
* デッキでユーザーページを遷移したときに前のユーザーのタイムラインが残り続ける問題を修正
|
||||
* デッキでフォロー申請が見れない問題を修正
|
||||
* グループ情報が更新できない問題を修正
|
||||
* Firefoxでトークの下が隠れてしまう問題を修正
|
||||
* トーク履歴がないと履歴が読み込み中のままになる問題を修正
|
||||
* 通知で長い名前のユーザーがいると横スクロールが発生する問題を修正
|
||||
* iOSでの選択ダイアログの動作を修正
|
||||
|
||||
11.18.1 (2019/05/21)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 11.18.0からフォロー/フォロワーのもっと読み込む機能が作動していない問題を修正
|
||||
* iOSで投稿フォームに自動フォーカスしない問題を修正
|
||||
* ダークモードでグループ名がみにくい問題を修正
|
||||
* ハッシュタグタイムラインを読み込んだ時にプログレスバーが残り続ける問題を修正
|
||||
* ユーザビリティの修正
|
||||
|
||||
11.18.0 (2019/05/21)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* デザインの調整
|
||||
|
||||
### 🐛Fixes
|
||||
* 投稿の削除が連合しない問題を修正
|
||||
|
||||
11.17.1 (2019/05/20)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* トークの通知インジケーターが点灯し続ける問題を修正
|
||||
* ユーザーグループの読み込みでエラーになることがある問題を修正
|
||||
|
||||
11.17.0 (2019/05/19)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### ✨Improvements
|
||||
* ユーザーグループを招待制に
|
||||
|
||||
### 🐛Fixes
|
||||
* フォロー申請が表示されない問題を修正
|
||||
* トークの通知インジケーターが点灯し続ける問題を修正
|
||||
* 自分を自分のグループから削除できる問題を修正
|
||||
* 新しいタブでグループトークを開くことができない問題を修正
|
||||
|
||||
11.16.1 (2019/05/19)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* Dockerで起動できない問題を修正
|
||||
|
||||
11.16.0 (2019/05/19)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### ✨Improvements
|
||||
* ユーザーグループ機能を追加
|
||||
* ページに「いいね」できるように
|
||||
* UIの改善
|
||||
|
||||
### 🐛Fixes
|
||||
* トークを読み込むときに最大数指定できなかった問題を修正
|
||||
|
||||
11.15.0 (2019/05/16)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 管理画面でreCAPTCHAのプレビューを表示するように
|
||||
|
||||
### 🐛Fixes
|
||||
* オブジェクトストレージのリージョンの設定が反映されない問題を修正
|
||||
|
||||
11.14.0 (2019/05/16)
|
||||
--------------------
|
||||
### 注意
|
||||
このバージョンからオブジェクトストレージの設定は設定ファイルではなく管理画面から行うようになりました。
|
||||
オブジェクトストレージを使用している場合、アップデートした後管理画面にアクセスしオブジェクトストレージの設定を再度行ってください。
|
||||
|
||||
### ✨Improvements
|
||||
* 特定のユーザーのファイルをすべて削除できるように
|
||||
* インスタンスの設定画面を整理
|
||||
|
||||
### 🐛Fixes
|
||||
* GIF画像のサムネイルが生成されないのを修正
|
||||
* 管理画面の「ログ」で複数の除外条件を設定できない問題を修正
|
||||
|
||||
11.13.0 (2019/05/14)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### ✨Improvements
|
||||
* 利用規約URL、リポジトリURL、フィードバックURLを設定できるように
|
||||
* 特定のインスタンスのファイルをすべて削除できるように
|
||||
* _blankで外部リンクされる可能性がある箇所にnoopenerを追加
|
||||
* ユーザーや外部インスタンスが生成するリンクにnofollowを追加
|
||||
* リモートのユーザーページやノートページにnoindexを追加
|
||||
* 自分のユーザーメニューにはミュートなどを表示しないように
|
||||
* デザインの調整
|
||||
|
||||
### 🐛Fixes
|
||||
* インスタンスブロックを設定できない問題を修正
|
||||
* ピン留め投稿の表示順がおかしい問題を修正
|
||||
* 設定の「アップデートを確認」でメッセージが正しく表示されない問題を修正
|
||||
* Firefoxで自分のメニューが開けない問題を修正
|
||||
* Welcomeページのタグクラウドが動かない問題を修正
|
||||
|
||||
11.12.0 (2019/05/10)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### ✨ Improvements
|
||||
* インスタンス運営者がおすすめアカウントを設定できるように
|
||||
* MisskeyPagesでNAME環境変数がNULLにならないように
|
||||
* MisskeyPagesにNULL環境変数を追加
|
||||
* MisskeyPagesで変数を並べ替えられるように
|
||||
* MisskeyPagesのテキストのリスト内で変数埋め込みできるように
|
||||
* 自分の指定した投稿のRenoteを全て解除するAPIを追加
|
||||
|
||||
### 🐛Fixes
|
||||
* Noteをpull取得した時にhost名がvalidateされていない問題を修正
|
||||
* みつけるで人気のタグが表示されない問題を修正
|
||||
|
||||
### その他
|
||||
* アカウントのisVerifiedフラグを廃止
|
||||
|
||||
11.11.2 (2019/05/07)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* IPv4 onlyホストからDualstackホストにAP deliverできない問題を修正
|
||||
* ストリーミングに接続するまでラグがある問題を修正
|
||||
* 2段階認証のコードが0から始まる時正しく入力できない問題を修正
|
||||
* ユーザーの更新日時が新しい順で更新日時がnullのユーザーが先頭に来る問題を修正
|
||||
* 値選択時の問題を修正
|
||||
* リバーシでマップの変更が反映されない問題を修正
|
||||
* リバーシで対局終了直後に盤面を巻き戻してもすぐ最終ターンまでリセットされる問題を修正
|
||||
|
||||
11.11.1 (2019/05/05)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* MisskeyPagesのリストから選択関数が使えない問題を修正
|
||||
|
||||
11.11.0 (2019/05/05)
|
||||
--------------------
|
||||
### ✨ Improvements
|
||||
* MisskeyPagesにリストから選択関数を追加
|
||||
* MisskeyPagesに確率を指定できるテキストランダム選択関数を追加
|
||||
* 外部サービス連携ログインリンクにアイコン追加
|
||||
|
||||
### 🐛Fixes
|
||||
* MisskeyPagesでifを入れ子にできなくなっていた問題を修正
|
||||
* MisskeyPagesで数値入力を作成するとテキスト入力になる問題を修正
|
||||
* 外部サービス連携に関する問題を修正
|
||||
|
||||
11.10.1 (2019/05/04)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* MisskeyPagesでページブロックを削除できなくなっていた問題を修正
|
||||
|
||||
### その他
|
||||
* Node.js v12対応
|
||||
|
||||
11.10.0 (2019/05/03)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### ✨ Improvements
|
||||
* MisskeyPagesに割った余りを求める関数を追加
|
||||
* Mastodon v2.8.0 のフォローリストをインポートできるように
|
||||
* エクスポートリクエストに失敗したらエラーを表示するように
|
||||
* エクスポートファイルでは同一ハッシュチェックをしないように
|
||||
|
||||
### 🐛Fixes
|
||||
* 2段階認証を設定するとログインできなくなる問題を修正
|
||||
* ファイルをアップロードできないことがある問題を修正
|
||||
* リモートファイルをキャッシュしない設定だとサムネイル時にオリジナル画像が表示されない問題を修正
|
||||
* 外部サービス連携の不具合を修正
|
||||
|
||||
11.9.0 (2019/05/02)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* MisskeyPagesで編集時にページブロックをドラッグで並べ替えられるように
|
||||
* MisskeyPagesにカウンターボタンブロックを追加
|
||||
|
||||
11.8.1 (2019/05/02)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* リモートファイルをキャッシュしないオプション有効時にファイルが作成できない問題を修正
|
||||
|
||||
11.8.0-2 (2019/05/01)
|
||||
-------------------
|
||||
* 11.8.0 のリリース内容が 11.7.0 と同一だったのを修正
|
||||
|
||||
11.8.0 (2019/05/01)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* MisskeyPagesで関数を作成できるように
|
||||
* MisskeyPagesでソースを表示できるように
|
||||
* MisskeyPagesにシードを与えるランダム関数を追加
|
||||
* MisskeyPagesに複数行テキストをテキストのリストに変換する関数を追加
|
||||
|
||||
### 🐛Fixes
|
||||
* APIドキュメントが見れなくなっていたのを修正
|
||||
* mention (あなた宛て) streaming にミュートが効かない問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.7.0 (2019/04/30)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* MisskeyPagesに ifブロック を追加
|
||||
* MisskeyPagesに テキストエリア を追加
|
||||
* MisskeyPagesに 複数行テキスト入力 を追加
|
||||
* MisskeyPagesに 投稿フォーム を追加
|
||||
* MisskeyPagesに 変換系関数 を追加
|
||||
* MisskeyPagesに 環境変数 URL を追加
|
||||
* MisskeyPagesでボタンやスイッチなどのテキストに変数使えるように
|
||||
|
||||
### 🐛Fixes
|
||||
* OGPのサイト名を修正
|
||||
* デザインの調整
|
||||
|
||||
11.6.0 (2019/04/29)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* AiScriptにいくつかの文字列操作関数を追加
|
||||
* ページ編集画面にページへのリンクを表示するように
|
||||
|
||||
### 🐛Fixes
|
||||
* MisskeyPagesで数値入力が文字列として扱われる問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.5.1 (2019/04/29)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* MisskeyPagesで環境変数を別の変数内で使えない問題を修正
|
||||
* MisskeyPagesで値が0の変数が表示されない問題を修正
|
||||
|
||||
11.5.0 (2019/04/29)
|
||||
-------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](migration)の手順を実行してください
|
||||
|
||||
### New features
|
||||
#### MisskeyPages
|
||||
ページ(記事)を作成できるように。
|
||||
|
||||
* 後から何度でも編集できる
|
||||
* アイキャッチを設定できる
|
||||
* フォントを設定できる
|
||||
* 画像を好きな位置に挿入できる
|
||||
* URLを決められる
|
||||
* タイトルを設定できる
|
||||
* 見出しを設定できる
|
||||
* ページの要約を設定できる(URLプレビュー時などに便利)
|
||||
* 変数や式(aka AiScript)を使用して動的なページも作れる
|
||||
* 目次自動生成(coming soon)
|
||||
|
||||
ページを気に入ったら「いいね」しよう (coming soon)
|
||||
|
||||
### ✨ Improvements
|
||||
* APIコンソールでパラメータテンプレートを表示するように
|
||||
|
||||
### 🐛Fixes
|
||||
* おすすめユーザーに自分自身が含まれる問題を修正
|
||||
* ユーザーサジェストで表示名が変わらない問題を修正
|
||||
|
||||
11.4.0 (2019/04/25)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* 検索でローカルの投稿のみに絞れるように
|
||||
* 検索で特定のインスタンスの投稿のみに絞れるように
|
||||
* 検索で特定のユーザーの投稿のみに絞れるように
|
||||
|
||||
### 🐛Fixes
|
||||
* 投稿が増殖する問題を修正
|
||||
* ストリームで過去の投稿が流れてくる問題を修正
|
||||
* モバイル版のユーザーページで遷移してもユーザー名が変わらない問題を修正
|
||||
* お知らせを切り替えても内容が変わらない問題を修正
|
||||
|
||||
11.3.1 (2019/04/24)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* Webからファイルがアップロードできない問題を修正
|
||||
|
||||
11.3.0 (2019/04/24)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* お知らせにMFMを使えるように
|
||||
* お知らせに画像を添付できるように
|
||||
|
||||
### 🐛Fixes
|
||||
* 投稿のタグ検索APIで大文字小文字が区別されていたのを修正
|
||||
* 公開範囲がホームの投稿がグローバルTLに流れる問題を修正
|
||||
* モバイルビューの投稿詳細にて acct が長いとアイコンが圧迫面接される問題を修正
|
||||
|
||||
11.2.2 (2019/04/22)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* 2段階認証を有効にするとログインできない問題を修正
|
||||
* リモートユーザーの修復処理が自動的に実行されない問題を修正
|
||||
* リモートユーザー情報が更新されない問題を修正
|
||||
|
||||
11.2.1 (2019/04/21)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* MEIDが25桁になっているのを修正
|
||||
* リモートユーザー情報が更新されない問題を修正
|
||||
|
||||
11.2.0 (2019/04/18)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* 検索で日付(日時)を入力するとタイムラインをその時点まで遡るように
|
||||
* APIコンソールでエンドポイントをサジェストするように
|
||||
* モバイル版でドライブのメニューを使いやすく
|
||||
* サイレンス時に確認を表示するように
|
||||
* ユーザーメニューでブロックなどの操作を行う時に確認するように
|
||||
|
||||
### 🐛Fixes
|
||||
* アプリケーション連携画面でパーミッションが表示されない問題を修正
|
||||
* アンケートウィジットでもMFMを使用するように
|
||||
* フォローしてないユーザーのホーム投稿がSTLに流れてくる問題を修正
|
||||
* モバイル版でウィジェットを設定できない問題を修正
|
||||
* スプラッシュがクリックに反応するように
|
||||
|
||||
11.1.6 (2019/04/18)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* 未認知ユーザーからActivityが飛んできた場合に処理できない問題を修正
|
||||
* その投稿を見たのにも関わらずメンションインジケーターが点灯し続ける問題を修正
|
||||
* ハッシュタグの判定を改善
|
||||
* サーバーのエラーハンドリングを改善
|
||||
|
||||
11.1.5 (2019/04/17)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* ユーザー名に含まれているカスタム絵文字が表示されないことがある問題を修正
|
||||
* 壁紙の設定ができない問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.1.4 (2019/04/17)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* タイムライン取得時に削除されたファイルを添付している投稿が含まれているとサーバーでエラーになる問題を修正
|
||||
* 管理画面のインスタンスメニューで変更前の設定が読み込まれないことがある問題を修正
|
||||
* 猫ではないのに猫のままで表示される問題を修正
|
||||
* admin/driveのアイコンがずれてる問題を修正
|
||||
* チャートで大きな数値を扱えない問題を修正
|
||||
* UIの修正
|
||||
|
||||
11.1.3 (2019/04/16)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* アプリからAPIにリクエストするときにランダムなユーザーがリクエストしたことになる問題を修正
|
||||
|
||||
11.1.2 (2019/04/15)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* 画像描画の依存関係を変更
|
||||
* リモートユーザーのファイルを削除するときに古い方からではなく新しい方から削除されるのを修正
|
||||
* リアクションしてないのにリアクションしたことになる問題を修正
|
||||
* APIドキュメントの修正
|
||||
|
||||
11.1.1 (2019/04/15)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* Metaタグの application-name を Misskey で固定するように修正
|
||||
* トークメッセージが既読にならない問題を修正
|
||||
* デフォルトでHTLを表示するように
|
||||
|
||||
11.1.0 (2019/04/15)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* アイコン未設定時にランダムな画像を表示するように
|
||||
* 管理者やモデレーターはレートリミット無効に
|
||||
|
||||
### 🐛Fixes
|
||||
* メンションの「あなた」インジケーターが表示されない問題を修正
|
||||
* ブロックAPIでエラーが発生する問題を修正
|
||||
* プッシュ通知の購読に失敗する問題を修正
|
||||
|
||||
11.0.3 (2019/04/15)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* ハッシュタグ検索APIが動作しない問題を修正
|
||||
* モデレーターなのにアカウントメニューに「管理」が表示されない問題を修正
|
||||
* プッシュ通知の購読に失敗する問題を修正
|
||||
* ユーザー取得APIでユーザーを指定しない場合エラーになる問題を修正
|
||||
|
||||
11.0.2 (2019/04/15)
|
||||
-------------------
|
||||
### 🐛Fixes
|
||||
* アプリが作成できない問題を修正
|
||||
* 「ハイライト」が表示されない問題を修正
|
||||
* リモートの投稿に添付されている画像が小さい問題を修正
|
||||
* モバイル版でリストの名前が表示されない問題を修正
|
||||
* APIドキュメントにパーミッション一覧を追加
|
||||
|
||||
11.0.1 (2019/04/15)
|
||||
-------------------
|
||||
### ✨ Improvements
|
||||
* 不要な依存関係を削除
|
||||
|
||||
11.0.0 daybreak (2019/04/14)
|
||||
----------------------------
|
||||
### ✨ Improvements
|
||||
* **データベースがMongoDBからPostgreSQLに変更されました**
|
||||
* **Redisが必須に**
|
||||
* アカウントを完全に削除できるように
|
||||
* 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように
|
||||
* ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように
|
||||
|
||||
### 🐛Fixes
|
||||
* フォロー申請数が実際より1すくなくなる問題を修正
|
||||
* リストからアカウント削除したユーザーを削除できない問題を修正
|
||||
* リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正
|
||||
* リストTLでダイレクト投稿が流れない問題を修正
|
||||
* ミュートしているユーザーの投稿がタイムラインに流れてくることがある問題を修正
|
||||
|
||||
### APIの破壊的変更
|
||||
* v10時点で deprecated だったパラメータなどを削除
|
||||
* ユーザーリストの title が name に
|
||||
* リバーシの対局の`settings`プロパティがなくなり、その中にあったプロパティがすべて上の階層に
|
||||
* 例えば`game.settings.map`は`game.map`になる
|
||||
|
||||
### 既知の問題
|
||||
* ユーザー認証無しでのアプリが作成できない
|
||||
* 依存ライブラリの問題と思わるため、対応が難しい
|
||||
|
||||
### Migration
|
||||
#### 移行の注意点
|
||||
**以下のデータは引き継がれません**
|
||||
* 通知
|
||||
* リモートの投稿
|
||||
* リバーシの対局
|
||||
|
||||
### 手順
|
||||
#### 手順
|
||||
1. v11をインストールしたい場所に syuilo/misskey をクローン
|
||||
2. config を設定する
|
||||
* PostgreSQL(`db`)の設定とは別に、v10からMongoDBの設定をコピペしてくる(例は下にあります)
|
||||
@ -73,332 +520,6 @@ mongodb:
|
||||
8. master ブランチに戻す
|
||||
9. enjoy
|
||||
|
||||
11.12.0 (2019/05/10)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### Improvements
|
||||
* インスタンス運営者がおすすめアカウントを設定できるように
|
||||
* MisskeyPagesでNAME環境変数がNULLにならないように
|
||||
* MisskeyPagesにNULL環境変数を追加
|
||||
* MisskeyPagesで変数を並べ替えられるように
|
||||
* MisskeyPagesのテキストのリスト内で変数埋め込みできるように
|
||||
* 自分の指定した投稿のRenoteを全て解除するAPIを追加
|
||||
|
||||
### Fixes
|
||||
* Noteをpull取得した時にhost名がvalidateされていない問題を修正
|
||||
* みつけるで人気のタグが表示されない問題を修正
|
||||
|
||||
### その他
|
||||
* アカウントのisVerifiedフラグを廃止
|
||||
|
||||
11.11.2 (2019/05/07)
|
||||
--------------------
|
||||
### Fixes
|
||||
* IPv4 onlyホストからDualstackホストにAP deliverできない問題を修正
|
||||
* ストリーミングに接続するまでラグがある問題を修正
|
||||
* 2段階認証のコードが0から始まる時正しく入力できない問題を修正
|
||||
* ユーザーの更新日時が新しい順で更新日時がnullのユーザーが先頭に来る問題を修正
|
||||
* 値選択時の問題を修正
|
||||
* リバーシでマップの変更が反映されない問題を修正
|
||||
* リバーシで対局終了直後に盤面を巻き戻してもすぐ最終ターンまでリセットされる問題を修正
|
||||
|
||||
11.11.1 (2019/05/05)
|
||||
--------------------
|
||||
### Fixes
|
||||
* MisskeyPagesのリストから選択関数が使えない問題を修正
|
||||
|
||||
11.11.0 (2019/05/05)
|
||||
--------------------
|
||||
### Improvements
|
||||
* MisskeyPagesにリストから選択関数を追加
|
||||
* MisskeyPagesに確率を指定できるテキストランダム選択関数を追加
|
||||
* 外部サービス連携ログインリンクにアイコン追加
|
||||
|
||||
### Fixes
|
||||
* MisskeyPagesでifを入れ子にできなくなっていた問題を修正
|
||||
* MisskeyPagesで数値入力を作成するとテキスト入力になる問題を修正
|
||||
* 外部サービス連携に関する問題を修正
|
||||
|
||||
11.10.1 (2019/05/04)
|
||||
--------------------
|
||||
### Fixes
|
||||
* MisskeyPagesでページブロックを削除できなくなっていた問題を修正
|
||||
|
||||
### その他
|
||||
* Node.js v12対応
|
||||
|
||||
11.10.0 (2019/05/03)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### Improvements
|
||||
* MisskeyPagesに割った余りを求める関数を追加
|
||||
* Mastodon v2.8.0 のフォローリストをインポートできるように
|
||||
* エクスポートリクエストに失敗したらエラーを表示するように
|
||||
* エクスポートファイルでは同一ハッシュチェックをしないように
|
||||
|
||||
### Fixes
|
||||
* 2段階認証を設定するとログインできなくなる問題を修正
|
||||
* ファイルをアップロードできないことがある問題を修正
|
||||
* リモートファイルをキャッシュしない設定だとサムネイル時にオリジナル画像が表示されない問題を修正
|
||||
* 外部サービス連携の不具合を修正
|
||||
|
||||
11.9.0 (2019/05/02)
|
||||
-------------------
|
||||
### Improvements
|
||||
* MisskeyPagesで編集時にページブロックをドラッグで並べ替えられるように
|
||||
* MisskeyPagesにカウンターボタンブロックを追加
|
||||
|
||||
11.8.1 (2019/05/02)
|
||||
-------------------
|
||||
### Fixes
|
||||
* リモートファイルをキャッシュしないオプション有効時にファイルが作成できない問題を修正
|
||||
|
||||
11.8.0-2 (2019/05/01)
|
||||
-------------------
|
||||
* 11.8.0 のリリース内容が 11.7.0 と同一だったのを修正
|
||||
|
||||
11.8.0 (2019/05/01)
|
||||
-------------------
|
||||
### Improvements
|
||||
* MisskeyPagesで関数を作成できるように
|
||||
* MisskeyPagesでソースを表示できるように
|
||||
* MisskeyPagesにシードを与えるランダム関数を追加
|
||||
* MisskeyPagesに複数行テキストをテキストのリストに変換する関数を追加
|
||||
|
||||
### Fixes
|
||||
* APIドキュメントが見れなくなっていたのを修正
|
||||
* mention (あなた宛て) streaming にミュートが効かない問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.7.0 (2019/04/30)
|
||||
-------------------
|
||||
### Improvements
|
||||
* MisskeyPagesに ifブロック を追加
|
||||
* MisskeyPagesに テキストエリア を追加
|
||||
* MisskeyPagesに 複数行テキスト入力 を追加
|
||||
* MisskeyPagesに 投稿フォーム を追加
|
||||
* MisskeyPagesに 変換系関数 を追加
|
||||
* MisskeyPagesに 環境変数 URL を追加
|
||||
* MisskeyPagesでボタンやスイッチなどのテキストに変数使えるように
|
||||
|
||||
### Fixes
|
||||
* OGPのサイト名を修正
|
||||
* デザインの調整
|
||||
|
||||
11.6.0 (2019/04/29)
|
||||
-------------------
|
||||
### Improvements
|
||||
* AiScriptにいくつかの文字列操作関数を追加
|
||||
* ページ編集画面にページへのリンクを表示するように
|
||||
|
||||
### Fixes
|
||||
* MisskeyPagesで数値入力が文字列として扱われる問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.5.1 (2019/04/29)
|
||||
-------------------
|
||||
### Fixes
|
||||
* MisskeyPagesで環境変数を別の変数内で使えない問題を修正
|
||||
* MisskeyPagesで値が0の変数が表示されない問題を修正
|
||||
|
||||
11.5.0 (2019/04/29)
|
||||
-------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](migration)の手順を実行してください
|
||||
|
||||
### New features
|
||||
#### MisskeyPages
|
||||
ページ(記事)を作成できるように。
|
||||
|
||||
* 後から何度でも編集できる
|
||||
* アイキャッチを設定できる
|
||||
* フォントを設定できる
|
||||
* 画像を好きな位置に挿入できる
|
||||
* URLを決められる
|
||||
* タイトルを設定できる
|
||||
* 見出しを設定できる
|
||||
* ページの要約を設定できる(URLプレビュー時などに便利)
|
||||
* 変数や式(aka AiScript)を使用して動的なページも作れる
|
||||
* 目次自動生成(coming soon)
|
||||
|
||||
ページを気に入ったら「いいね」しよう (coming soon)
|
||||
|
||||
### Improvements
|
||||
* APIコンソールでパラメータテンプレートを表示するように
|
||||
|
||||
### Fixes
|
||||
* おすすめユーザーに自分自身が含まれる問題を修正
|
||||
* ユーザーサジェストで表示名が変わらない問題を修正
|
||||
|
||||
11.4.0 (2019/04/25)
|
||||
-------------------
|
||||
### Improvements
|
||||
* 検索でローカルの投稿のみに絞れるように
|
||||
* 検索で特定のインスタンスの投稿のみに絞れるように
|
||||
* 検索で特定のユーザーの投稿のみに絞れるように
|
||||
|
||||
### Fixes
|
||||
* 投稿が増殖する問題を修正
|
||||
* ストリームで過去の投稿が流れてくる問題を修正
|
||||
* モバイル版のユーザーページで遷移してもユーザー名が変わらない問題を修正
|
||||
* お知らせを切り替えても内容が変わらない問題を修正
|
||||
|
||||
11.3.1 (2019/04/24)
|
||||
-------------------
|
||||
### Fixes
|
||||
* Webからファイルがアップロードできない問題を修正
|
||||
|
||||
11.3.0 (2019/04/24)
|
||||
-------------------
|
||||
### Improvements
|
||||
* お知らせにMFMを使えるように
|
||||
* お知らせに画像を添付できるように
|
||||
|
||||
### Fixes
|
||||
* 投稿のタグ検索APIで大文字小文字が区別されていたのを修正
|
||||
* 公開範囲がホームの投稿がグローバルTLに流れる問題を修正
|
||||
* モバイルビューの投稿詳細にて acct が長いとアイコンが圧迫面接される問題を修正
|
||||
|
||||
11.2.2 (2019/04/22)
|
||||
-------------------
|
||||
### Fixes
|
||||
* 2段階認証を有効にするとログインできない問題を修正
|
||||
* リモートユーザーの修復処理が自動的に実行されない問題を修正
|
||||
* リモートユーザー情報が更新されない問題を修正
|
||||
|
||||
11.2.1 (2019/04/21)
|
||||
-------------------
|
||||
### Fixes
|
||||
* MEIDが25桁になっているのを修正
|
||||
* リモートユーザー情報が更新されない問題を修正
|
||||
|
||||
11.2.0 (2019/04/18)
|
||||
-------------------
|
||||
### Improvements
|
||||
* 検索で日付(日時)を入力するとタイムラインをその時点まで遡るように
|
||||
* APIコンソールでエンドポイントをサジェストするように
|
||||
* モバイル版でドライブのメニューを使いやすく
|
||||
* サイレンス時に確認を表示するように
|
||||
* ユーザーメニューでブロックなどの操作を行う時に確認するように
|
||||
|
||||
### Fixes
|
||||
* アプリケーション連携画面でパーミッションが表示されない問題を修正
|
||||
* アンケートウィジットでもMFMを使用するように
|
||||
* フォローしてないユーザーのホーム投稿がSTLに流れてくる問題を修正
|
||||
* モバイル版でウィジェットを設定できない問題を修正
|
||||
* スプラッシュがクリックに反応するように
|
||||
|
||||
11.1.6 (2019/04/18)
|
||||
-------------------
|
||||
### Fixes
|
||||
* 未認知ユーザーからActivityが飛んできた場合に処理できない問題を修正
|
||||
* その投稿を見たのにも関わらずメンションインジケーターが点灯し続ける問題を修正
|
||||
* ハッシュタグの判定を改善
|
||||
* サーバーのエラーハンドリングを改善
|
||||
|
||||
11.1.5 (2019/04/17)
|
||||
-------------------
|
||||
### Fixes
|
||||
* ユーザー名に含まれているカスタム絵文字が表示されないことがある問題を修正
|
||||
* 壁紙の設定ができない問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.1.4 (2019/04/17)
|
||||
-------------------
|
||||
### Fixes
|
||||
* タイムライン取得時に削除されたファイルを添付している投稿が含まれているとサーバーでエラーになる問題を修正
|
||||
* 管理画面のインスタンスメニューで変更前の設定が読み込まれないことがある問題を修正
|
||||
* 猫ではないのに猫のままで表示される問題を修正
|
||||
* admin/driveのアイコンがずれてる問題を修正
|
||||
* チャートで大きな数値を扱えない問題を修正
|
||||
* UIの修正
|
||||
|
||||
11.1.3 (2019/04/16)
|
||||
-------------------
|
||||
### Fixes
|
||||
* アプリからAPIにリクエストするときにランダムなユーザーがリクエストしたことになる問題を修正
|
||||
|
||||
11.1.2 (2019/04/15)
|
||||
-------------------
|
||||
### Fixes
|
||||
* 画像描画の依存関係を変更
|
||||
* リモートユーザーのファイルを削除するときに古い方からではなく新しい方から削除されるのを修正
|
||||
* リアクションしてないのにリアクションしたことになる問題を修正
|
||||
* APIドキュメントの修正
|
||||
|
||||
11.1.1 (2019/04/15)
|
||||
-------------------
|
||||
### Fixes
|
||||
* Metaタグの application-name を Misskey で固定するように修正
|
||||
* トークメッセージが既読にならない問題を修正
|
||||
* デフォルトでHTLを表示するように
|
||||
|
||||
11.1.0 (2019/04/15)
|
||||
-------------------
|
||||
### Improvements
|
||||
* アイコン未設定時にランダムな画像を表示するように
|
||||
* 管理者やモデレーターはレートリミット無効に
|
||||
|
||||
### Fixes
|
||||
* メンションの「あなた」インジケーターが表示されない問題を修正
|
||||
* ブロックAPIでエラーが発生する問題を修正
|
||||
* プッシュ通知の購読に失敗する問題を修正
|
||||
|
||||
11.0.3 (2019/04/15)
|
||||
-------------------
|
||||
### Fixes
|
||||
* ハッシュタグ検索APIが動作しない問題を修正
|
||||
* モデレーターなのにアカウントメニューに「管理」が表示されない問題を修正
|
||||
* プッシュ通知の購読に失敗する問題を修正
|
||||
* ユーザー取得APIでユーザーを指定しない場合エラーになる問題を修正
|
||||
|
||||
11.0.2 (2019/04/15)
|
||||
-------------------
|
||||
### Fixes
|
||||
* アプリが作成できない問題を修正
|
||||
* 「ハイライト」が表示されない問題を修正
|
||||
* リモートの投稿に添付されている画像が小さい問題を修正
|
||||
* モバイル版でリストの名前が表示されない問題を修正
|
||||
* APIドキュメントにパーミッション一覧を追加
|
||||
|
||||
11.0.1 (2019/04/15)
|
||||
-------------------
|
||||
### Improvements
|
||||
* 不要な依存関係を削除
|
||||
|
||||
11.0.0 daybreak (2019/04/14)
|
||||
----------------------------
|
||||
### Improvements
|
||||
* **データベースがMongoDBからPostgreSQLに変更されました**
|
||||
* **Redisが必須に**
|
||||
* アカウントを完全に削除できるように
|
||||
* 投稿フォームで添付ファイルの閲覧注意を確認/設定できるように
|
||||
* ミュート/ブロック時にそのユーザーの投稿のウォッチをすべて解除するように
|
||||
|
||||
### Fixes
|
||||
* フォロー申請数が実際より1すくなくなる問題を修正
|
||||
* リストからアカウント削除したユーザーを削除できない問題を修正
|
||||
* リストTLでフォローしていないユーザーの非公開投稿が流れる問題を修正
|
||||
* リストTLでダイレクト投稿が流れない問題を修正
|
||||
* ミュートしているユーザーの投稿がタイムラインに流れてくることがある問題を修正
|
||||
|
||||
### APIの破壊的変更
|
||||
* v10時点で deprecated だったパラメータなどを削除
|
||||
* ユーザーリストの title が name に
|
||||
* リバーシの対局の`settings`プロパティがなくなり、その中にあったプロパティがすべて上の階層に
|
||||
* 例えば`game.settings.map`は`game.map`になる
|
||||
|
||||
### 既知の問題
|
||||
* ユーザー認証無しでのアプリが作成できない
|
||||
* 依存ライブラリの問題と思わるため、対応が難しい
|
||||
|
||||
### Migration
|
||||
coming soon...
|
||||
|
||||
10.100.0
|
||||
----------
|
||||
* ユーザーリストでフォローボタンを表示するように
|
||||
|
@ -3,11 +3,17 @@
|
||||
|
||||
## Issues
|
||||
Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues .
|
||||
Before creating a new issue, please search existing issues to avoid duplication.
|
||||
If you find the existing issue, please add your reaction or comment to the issue.
|
||||
|
||||
* Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one.
|
||||
* If you have multiple independent issues, please submit them separately.
|
||||
|
||||
|
||||
## Localization (l10n)
|
||||
Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
|
||||
Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management.
|
||||
You can improve our translations with your Crowdin account.
|
||||
Changes you make in Crowdin will be merged into develop branch.
|
||||
|
||||
If you can't find the language you want to contribute with, please open an issue.
|
||||
|
||||

|
||||
|
||||
@ -15,16 +21,16 @@ Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
|
||||
Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
|
||||
|
||||
## Documentation
|
||||
* Documents for contributors are located in `/docs`.
|
||||
* Documents for instance admins are located in `/docs`.
|
||||
* Documents for end users are located in `src/docs`.
|
||||
* Documents for contributors are located in [`/docs`](/docs).
|
||||
* Documents for instance admins are located in [`/docs`](/docs).
|
||||
* Documents for end users are located in [`/src/docs`](/src/docs).
|
||||
|
||||
## Test
|
||||
* Test codes are located in `/test`.
|
||||
* Test codes are located in [`/test`](/test).
|
||||
|
||||
## Continuous integration
|
||||
Misskey uses CircleCI for automated test.
|
||||
Configuration files are located in `/.circleci`.
|
||||
Configuration files are located in [`/.circleci`](/.circleci).
|
||||
|
||||
## Glossary
|
||||
### AP
|
||||
@ -128,6 +134,20 @@ query.andWhere(new Brackets(qb => {
|
||||
}));
|
||||
```
|
||||
|
||||
### Not `null` in TypeORM
|
||||
```ts
|
||||
const foo = await Foos.findOne({
|
||||
bar: Not(null)
|
||||
});
|
||||
```
|
||||
のようなクエリ(`bar`が`null`ではない)は期待通りに動作しない。
|
||||
次のようにします:
|
||||
```ts
|
||||
const foo = await Foos.findOne({
|
||||
bar: Not(IsNull())
|
||||
});
|
||||
```
|
||||
|
||||
### `null` in SQL
|
||||
SQLを発行する際、パラメータが`null`になる可能性のある場合はSQL文を出し分けなければならない
|
||||
例えば
|
||||
@ -197,3 +217,13 @@ const user = await Users.findOne(userId).then(ensure);
|
||||
// }
|
||||
// の糖衣構文のような扱いです
|
||||
```
|
||||
|
||||
### Migration作成方法
|
||||
コードの変更をした後、`ormconfig.json`(`npm run ormconfig`で生成)を用意し、
|
||||
|
||||
```
|
||||
npm i -g ts-node
|
||||
ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前
|
||||
```
|
||||
|
||||
作成されたスクリプトは不必要な変更を含むため除去してください。
|
||||
|
@ -39,4 +39,4 @@ COPY --from=builder /misskey/node_modules ./node_modules
|
||||
COPY --from=builder /misskey/built ./built
|
||||
COPY . ./
|
||||
|
||||
CMD ["npm", "start"]
|
||||
CMD ["npm", "run", "migrateandstart"]
|
||||
|
18
README.md
18
README.md
@ -106,20 +106,23 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
|
||||
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1.png?token-time=2145916800&token-hash=FMV7cPKBD1TU2WTbl1jg6AcdKSvTb2BSFcDhgc-EO8w%3D" alt="gutfuckllc" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
|
||||
@ -130,6 +133,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
|
||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
@ -147,9 +151,10 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/10789744/97175095d8f04c0f86225ff47cb98d40/1.jpeg?token-time=2145916800&token-hash=l4AoMR7Nj7K4yAHrkrk2hAoggPkbSPm12m1nmbe9Pb8%3D" alt="Naoki Hirayama" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
|
||||
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
|
||||
@ -157,21 +162,24 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
|
||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
||||
<td><a href="https://www.patreon.com/noellabo">noellabo</a></td>
|
||||
<td><a href="https://www.patreon.com/Corset">CG</a></td>
|
||||
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
|
||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
|
||||
<td><a href="https://www.patreon.com/spinlock">Naoki Hirayama</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Tue, 07 May 2019 11:55:07 UTC
|
||||
**Last updated:** Tue, 04 Jun 2019 04:21:06 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
@ -249,9 +249,9 @@ common:
|
||||
update-available-title: "Aktualizace k dispozici"
|
||||
update-available: "Je k dispozici nová verze Misskey ({newer},vaše verze je {current}). Pro aplikování nové verze znovunačtěte stránku."
|
||||
my-token-regenerated: "Váš token byl regenerován, proto budete odhlášen/a."
|
||||
verified-user: "Ověřené účty"
|
||||
hide-password: "Skrýt heslo"
|
||||
show-password: "Zobrazit heslo"
|
||||
enter-username: "Zadejte uživatelské jméno"
|
||||
do-not-use-in-production: "Tohle je vývojářský build. Nepoužívejte v produkci."
|
||||
user-suspended: "Tomuto uživateli byl pozastaven účet."
|
||||
is-remote-user: "Informace o tomto uživateli nemusí být kompletní."
|
||||
@ -312,7 +312,6 @@ auth/views/index.vue:
|
||||
error: "Taková relace neexistuje."
|
||||
sign-in: "Prosím přihlaste se."
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Ověřené účty"
|
||||
popular-users: "Populární uživatelé"
|
||||
recently-updated-users: "Nedávno aktívni uživatelé"
|
||||
recently-registered-users: "Nedávno registrovaní uživatelé"
|
||||
@ -416,14 +415,12 @@ common/views/components/messaging.vue:
|
||||
search-user: "Najít uživatele"
|
||||
you: "Vy"
|
||||
no-history: "Žádná historie"
|
||||
user: "Uživatel"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "Žádné zprávy"
|
||||
new-message: "Máte novou zprávu"
|
||||
only-one-file-attached: "Jenom JEDEN soubor může být přiložen ke zprávě."
|
||||
common/views/components/messaging-room.form.vue:
|
||||
send: "Odeslat"
|
||||
attach-from-local: "Přiložit soubory z Vašeho zařízení"
|
||||
only-one-file-attached: "Jenom JEDEN soubor může být přiložen ke zprávě."
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "Přečtené"
|
||||
deleted: "Tato zpráva byla odstraněna"
|
||||
@ -619,8 +616,15 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "Odebrat z tohoto seznamu"
|
||||
delete-are-you-sure: "Smazat seznam \"$1\"?"
|
||||
deleted: "Smazáno"
|
||||
add-user: "Přidat uživatele"
|
||||
common/views/components/user-group-editor.vue:
|
||||
deleted: "Smazáno"
|
||||
invite: "Pozvat"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Seznamy"
|
||||
list-name: "Název seznamu"
|
||||
common/views/components/user-groups.vue:
|
||||
invites: "Pozvat"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Načítám"
|
||||
next: "Další"
|
||||
@ -855,11 +859,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Nový příspěvek"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Vyhledávání"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
accept: "Přijmout"
|
||||
reject: "Odmítnout"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Seznamy uživatelů"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Příspěvky"
|
||||
desktop/views/components/users-list.vue:
|
||||
@ -875,7 +874,6 @@ admin/views/index.vue:
|
||||
users: "Uživatelé"
|
||||
federation: "Z fedivesmíru"
|
||||
announcements: "Oznámení"
|
||||
hashtags: "Hashtagy"
|
||||
queue: "Fronta úloh"
|
||||
logs: "Logy"
|
||||
back-to-misskey: "Zpět na Misskey"
|
||||
@ -900,12 +898,12 @@ admin/views/instance.vue:
|
||||
maintainer-config: "Informace o administrátorovi"
|
||||
maintainer-name: "Jméno administrátora"
|
||||
maintainer-email: "Kontakt na administrátora"
|
||||
object-storage-endpoint: "Endpoint"
|
||||
mb: "V megabajtech"
|
||||
recaptcha-config: "nastavení služby reCAPTCHA"
|
||||
recaptcha-info: "reCAPTCHA token je povinný. Můžete jej získat na https://www.google.com/recaptcha/intro/"
|
||||
enable-recaptcha: "povolit reCAPTCHA"
|
||||
recaptcha-site-key: "reCAPTCHA klíč stránky (site key)"
|
||||
recaptcha-secret-key: "reCAPTCHA tajný klíč"
|
||||
recaptcha-preview: "Náhled"
|
||||
twitter-integration-config: "Nastavení spojení s Twitterem"
|
||||
twitter-integration-info: "The callback URL is set on {url}."
|
||||
enable-twitter-integration: "Povolit připojení k Twitteru"
|
||||
@ -924,7 +922,6 @@ admin/views/instance.vue:
|
||||
invite: "Pozvat"
|
||||
save: "Uložit"
|
||||
saved: "Uloženo"
|
||||
user-recommendation-config: "Doporučení uživatelé"
|
||||
email: "Emailová adresa"
|
||||
smtp-port: "SMTP Port"
|
||||
smtp-auth: "Provést SMTP autentikaci"
|
||||
@ -976,12 +973,6 @@ admin/views/users.vue:
|
||||
reset-password: "Resetovat heslo"
|
||||
reset-password-confirm: "Opravdu chcete resetovat Vaše heslo?"
|
||||
password-updated: "Heslo je nyní \"{password}\""
|
||||
verify: "Ověřit účet"
|
||||
verify-confirm: "Chcete aby toto byl ověřený účet?"
|
||||
verified: "Účet se nyní ověřuje"
|
||||
unverify: "Zrušit ověření účtu"
|
||||
unverify-confirm: "Opravdu chcete zrušit designaci \"ověřený účet\"?"
|
||||
unverified: "Ruší se potvrzení účtu"
|
||||
update-remote-user: "Aktualizovat informace o vzdáleném účtu"
|
||||
users:
|
||||
title: "Uživatel"
|
||||
@ -989,7 +980,6 @@ admin/views/users.vue:
|
||||
all: "Všechny"
|
||||
moderator: "Moderátor"
|
||||
adminOrModerator: "Admin/Moderátor"
|
||||
verified: "Ověřený účet"
|
||||
origin:
|
||||
title: "Původ"
|
||||
combined: "Lokální + Vzdálené"
|
||||
@ -1054,6 +1044,7 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "za hodinu"
|
||||
day: "za den"
|
||||
blocked-hosts: "Blokován"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "O Misskey"
|
||||
timeline: "Časová osa"
|
||||
@ -1168,8 +1159,6 @@ mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Nahrát soubor"
|
||||
create-folder: "Vytvořit složku"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Seznamy"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "Váš účet je připraven! 📦"
|
||||
mobile/views/pages/home.vue:
|
||||
@ -1186,9 +1175,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
activity: "Aktivita"
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "Sdílet na {name}"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
accept: "Přijmout"
|
||||
reject: "Odmítnout"
|
||||
mobile/views/pages/note.vue:
|
||||
prev: "Předchozí příspěvěk"
|
||||
next: "Následující příspěvek"
|
||||
@ -1221,6 +1207,7 @@ deck/deck.user-column.vue:
|
||||
dev/views/new-app.vue:
|
||||
app-name-desc: "Jméno vaší aplikace"
|
||||
pages:
|
||||
like: "Lajk"
|
||||
title: "Titulek"
|
||||
blocks:
|
||||
post: "Formulář pro psaní"
|
||||
|
1896
locales/da-DK.yml
Normal file
1896
locales/da-DK.yml
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,26 +10,35 @@ common:
|
||||
about: "Misskey ist eine Quelloffene, <b>dezentralisierte microblogging Software</b>. Es bietet eine erweiterbare Benutzeroberfläche, verschiedenste Möglichkeiten auf Beiträge zu reagieren, kostenlosen Datenspeicher, und andere fortschrittliche Funktionen. Zusätzlich ist Misskey dazu in der Lage, sich mittels des Fediverse mit beliebig vielen anderen ActivityPub-kompatiblen Diensten zu verbinden. Wenn du zum Beispiel einen Betrag mit Misskey abschickst, wird dieser auch für Nutzer von Mastodon oder Pleroma sichtbar sein. So ähnlich wie eine Radioübertragung zwischen Planeten."
|
||||
features: "Funktionen"
|
||||
rich-contents: "Notizen"
|
||||
rich-contents-desc: "Poste einfach deine Ideen, Interessen und alles, was du teilen möchtest. Gestalte deine Nachrichten, teile deine Lieblingsbilder, sende Dateien und Videos und erstelle Umfragen – das und mehr kann Misskey!"
|
||||
reaction: "Reaktionen"
|
||||
reaction-desc: "Der einfachste Weg, deine Gefühle mit anderen zu teilen. Mit Misskey kannst du auf verschiedenste Arten auf Beiträge reagieren, statt nur zu „liken“."
|
||||
ui: "Benutzeroberfläche"
|
||||
ui-desc: "Geschmäcker sind verschieden. Deswegen ist Misskeys Oberfläche hochanpassbar und modular. Mache die Startseite zu deiner Startseite, indem du das Layout deiner Timeline änderst und mit Widgets staffierst."
|
||||
drive: "Drive"
|
||||
drive-desc: "Du willst ein hochgeladenes Foto nochmal posten? Deine Dateien benennen und in Ordnern sortieren? Misskeys Drive ist der beste Ort dafür. Damit wird das Teilen zum Kinderspiel!"
|
||||
outro: "Probiere Misskey aus und entdecke Misskeys einzigartige Funktionen. Wenn dir diese Instanz nicht zusagt, nimm einfach eine andere. Dank Misskeys dezentralem System kannst du dich überall mit deinen Freunden verbinden. Also dann, GLHF!"
|
||||
adblock:
|
||||
detected: "Bitte deaktiviere den Werbeblocker."
|
||||
warning: "<strong>Misskey hat keine Werbung.</strong> Allerdings können Werbeblocker für unerwartete Probleme und fehlende Funktionen sorgen."
|
||||
application-authorization: "Autorisierte Anwendungen"
|
||||
close: "Schließen"
|
||||
do-not-copy-paste: "Bitte keinen Code einfügen. Ihr Account könnte gefährdet werden."
|
||||
load-more: "Mehr laden"
|
||||
enter-password: "Bitte Passwort eingeben"
|
||||
2fa: "Zwei-Faktor-Authentifizierung"
|
||||
customize-home: "Layout Anpassen"
|
||||
featured-notes: "Hervorgehobene Beiträge"
|
||||
featured-notes: "Beliebt"
|
||||
dark-mode: "Dunkler Modus"
|
||||
signin: "Einloggen"
|
||||
signup: "Registrieren"
|
||||
signout: "Ausloggen"
|
||||
reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?"
|
||||
fetching-as-ap-object: "Hole Daten…"
|
||||
got-it: "Verstanden!"
|
||||
customization-tips:
|
||||
title: "Anpassung-Tipps"
|
||||
paragraph: "<p>Du kannst deine Startseite anpassen, indem du Widgets hinzufügst und verschiebst.</p><p><strong>Klicke <strong>rechts</strong></strong> auf ein Widget, um dessen Aussehen zu verändern.</p><p>Um ein Widget zu löschen, klicke und ziehe es auf den <strong>Papierkorb</strong> am Kopfende der Seite.</p><p>Wenn du fertig bist, drücke auf den Beenden-Knopf oben rechts.</p>"
|
||||
gotit: "Verstanden!"
|
||||
notification:
|
||||
file-uploaded: "Datei hochgeladen!"
|
||||
@ -56,21 +65,32 @@ common:
|
||||
pages: "Seite"
|
||||
messaging: "Unterhaltungen"
|
||||
home: "Home"
|
||||
deck: "Stapel"
|
||||
deck: "Deck"
|
||||
timeline: "Zeitleiste"
|
||||
explore: "Entdecken"
|
||||
following: "Folgt"
|
||||
followers: "Folgende"
|
||||
favorites: "Diesen Beitrag favorisieren"
|
||||
favorites: "Favoriten"
|
||||
permissions:
|
||||
"read:account": "Accountinformationen anzeigen."
|
||||
"write:account": "Accountinformationen bearbeiten."
|
||||
"read:blocks": "Blöcke anzeigen"
|
||||
"write:blocks": "Auf Sperrungen zugreifen"
|
||||
"read:drive": "Dateien anzeigen"
|
||||
"write:drive": "Dateien bearbeiten"
|
||||
"read:favorites": "Favoriten anzeigen"
|
||||
"write:favorites": "Auf Favoriten zugreifen"
|
||||
"read:following": "Follower-Daten lesen"
|
||||
"write:following": "Folgestatus bearbeiten"
|
||||
"read:messaging": "Unterhaltung anzeigen"
|
||||
"write:messaging": "Unterhaltung bearbeiten"
|
||||
"read:mutes": "Stummschaltungen lesen"
|
||||
"write:mutes": "Stummschaltungen bearbeiten"
|
||||
"write:notes": "Beiträge löschen und verfassen"
|
||||
"read:notifications": "Benachrichtigungen lesen"
|
||||
"write:notifications": "Benachrichtigungen bearbeiten"
|
||||
"read:reactions": "Reaktionen sehen"
|
||||
"write:reactions": "Reaktionen hinzufügen und bearbeiten"
|
||||
"write:votes": "Abstimmen"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "Beiträge von Benutzern, denen du folgst, werden in der Zeitleiste angezeigt."
|
||||
@ -147,23 +167,116 @@ common:
|
||||
show-reversi-board-labels: "Zeige Reihen- und Spaltenbeschreibungen in Reversi an"
|
||||
use-avatar-reversi-stones: "Avatar als Stein in Reversi anzeigen"
|
||||
disable-animated-mfm: "Animierten Text in Beiträgen deaktivieren"
|
||||
disable-showing-animated-images: "Animierte Grafiken deaktivieren"
|
||||
suggest-recent-hashtags: "Beim Verfassen von Beiträgen letzte Hashtags anzeigen"
|
||||
always-show-nsfw: "Sensible Inhalte (NSFW) immer anzeigen"
|
||||
always-mark-nsfw: "Meine Anhänge immer als NSFW markieren"
|
||||
show-full-acct: "Servername bei Benutzernamen immer anzeigen"
|
||||
show-via: "„via“ anzeigen"
|
||||
reduce-motion: "Animationen der Benutzeroberfläche reduzieren"
|
||||
this-setting-is-this-device-only: "Nur auf diesem Gerät"
|
||||
use-os-default-emojis: "Betriebssystem-Emojis nutzen"
|
||||
line-width: "Linienstärke"
|
||||
line-width-thin: "Dünn"
|
||||
line-width-normal: "Normal"
|
||||
line-width-thick: "Dick"
|
||||
font-size: "Schriftgröße"
|
||||
font-size-x-small: "Sehr klein"
|
||||
font-size-small: "Klein"
|
||||
font-size-medium: "Normal"
|
||||
font-size-large: "Groß"
|
||||
font-size-x-large: "Sehr groß"
|
||||
deck-column-align: "Spaltenaufteilung der Deck-Ansicht"
|
||||
deck-column-align-center: "Mitte"
|
||||
deck-column-align-left: "Links"
|
||||
deck-column-align-flexible: "Flexibel"
|
||||
deck-column-width: "Spaltenbreite des Decks"
|
||||
deck-column-width-narrow: "Sehr eng"
|
||||
deck-column-width-narrower: "Eng"
|
||||
deck-column-width-normal: "Normal"
|
||||
deck-column-width-wider: "Breit"
|
||||
deck-column-width-wide: "Sehr breit"
|
||||
use-shadow: "Nutze Schatten"
|
||||
rounded-corners: "Abgerundete Ecken"
|
||||
circle-icons: "Kreisförmige Icons"
|
||||
contrasted-acct: "Nutzernamen kontrastreicher darstellen"
|
||||
wallpaper: "Hintergrund"
|
||||
choose-wallpaper: "Hintergrund auswählen"
|
||||
delete-wallpaper: "Hintergrund entfernen"
|
||||
post-form-on-timeline: "Beitragsformular über Timeline anzeigen"
|
||||
show-clock-on-header: "Uhr am oberen rechten Rand anzeigen"
|
||||
show-reply-target: "Zeige bei einer Antwort die ursprüngliche Nachricht"
|
||||
timeline: "Timeline"
|
||||
show-my-renotes: "Zeige eigene Renotes in der Timeline"
|
||||
show-renoted-my-notes: "Zeige Renotes deiner Posts in der Timeline"
|
||||
show-local-renotes: "Zeige Renotes lokaler Posts in der Timeline"
|
||||
remain-deleted-note: "Gelöschte Beiträge weiterhin anzeigen"
|
||||
sound: "Töne"
|
||||
enable-sounds: "Töne aktivieren"
|
||||
enable-sounds-desc: "Spiel einen Ton ab beim Erhalten eines Beitrags bzw. einer Nachricht. Diese Einstellung wird im Browser gespeichert."
|
||||
volume: "Lautstärke"
|
||||
test: "Test"
|
||||
update: "Misskey-Update"
|
||||
version: "Version:"
|
||||
latest-version: "Neuste Version:"
|
||||
update-checking: "Suche nach Updates"
|
||||
do-update: "Nach Updates suchen"
|
||||
update-settings: "Erweiterte Einstellungen"
|
||||
no-updates: "Kein Update verfügbar"
|
||||
no-updates-desc: "Misskey ist aktuell."
|
||||
update-available: "Eine neue Version ist verfügbar!"
|
||||
update-available-desc: "Änderungen werden beim Neuladen der Seite angewendet."
|
||||
advanced-settings: "Erweiterte Einstellungen"
|
||||
debug-mode: "Debug-Modus einschalten"
|
||||
debug-mode-desc: "Diese Einstellung wird im Browser gespeichert."
|
||||
navbar-position: "Postion der Navigationsleiste"
|
||||
navbar-position-top: "Oben"
|
||||
navbar-position-left: "Links"
|
||||
navbar-position-right: "Rechts"
|
||||
i-am-under-limited-internet: "Ich möchte Datenvolumen sparen"
|
||||
post-style: "Beitrags-Anzeigestil"
|
||||
post-style-standard: "Standard"
|
||||
post-style-smart: "Smart"
|
||||
notification-position: "Benachrichtigungen anzeigen"
|
||||
notification-position-bottom: "Unten"
|
||||
notification-position-top: "Oben"
|
||||
disable-via-mobile: "Beitrag nicht als „vom Handy“ markieren"
|
||||
load-raw-images: "Anhänge in voller Größe laden"
|
||||
load-remote-media: "Zeige Inhalte von fremden Servern"
|
||||
search: "Suche"
|
||||
delete: "Löschen"
|
||||
loading: "Laden"
|
||||
ok: "Okay"
|
||||
cancel: "Abbrechen"
|
||||
update-available-title: "Aktualisierung verfügbar"
|
||||
update-available: "Eine neue Version von Misskey ist verfügbar ({newer}, aktuell ist {current}). Lade die Seite neu um die aktuelle Version zu laden"
|
||||
my-token-regenerated: "Dein Token wurde generiert. Du wirst jetzt abgemeldet."
|
||||
verified-user: "Verifizierter Benutzer"
|
||||
hide-password: "Passwort verbergen"
|
||||
show-password: "Passwort zeigen"
|
||||
enter-username: "Kontonamen eingeben"
|
||||
do-not-use-in-production: "Dies ist eine Entwicklungsversion. Nicht in einer Produktivumgebung verwenden."
|
||||
user-suspended: "Dieser Nutzer wurde gesperrt."
|
||||
is-remote-user: "Diese Nutzerinformationen können unvollständig sein."
|
||||
is-remote-post: "Dies ist ein entfernter Post."
|
||||
view-on-remote: "Vollständige Infos auf Ursprungsserver anzeigen"
|
||||
renoted-by: "Renote von {user}"
|
||||
no-notes: "Keine Beiträge"
|
||||
turn-on-darkmode: "Dunkles Design"
|
||||
turn-off-darkmode: "Helles Design"
|
||||
error:
|
||||
title: "Allgemeiner Fehler"
|
||||
retry: "Erneut versuchen"
|
||||
reversi:
|
||||
drawn: "Unentschieden"
|
||||
my-turn: "Du bist am Zug"
|
||||
opponent-turn: "Dein Gegner ist an der Reihe"
|
||||
turn-of: "{name}s Zug"
|
||||
past-turn-of: "Zug von {name}"
|
||||
won: "{name} hat gewonnen"
|
||||
black: "Schwarz"
|
||||
white: "Weiß"
|
||||
total: "Gesamt"
|
||||
this-turn: "{count}. Zug"
|
||||
widgets:
|
||||
analog-clock: "Analoge Uhr"
|
||||
profile: "Profil"
|
||||
@ -174,8 +287,10 @@ common:
|
||||
memo: "Notizen"
|
||||
trends: "Trends"
|
||||
photo-stream: "Bilder"
|
||||
posts-monitor: "Beitrags-Aktivität"
|
||||
slideshow: "Diashow"
|
||||
version: "Version"
|
||||
broadcast: "Ankündigungen"
|
||||
notifications: "Benachrichtigungen"
|
||||
users: "Empfohlene Benutzer"
|
||||
polls: "Umfrage"
|
||||
@ -184,7 +299,9 @@ common:
|
||||
nav: "Navigation"
|
||||
tips: "Tipps"
|
||||
hashtags: "Hashtags"
|
||||
queue: "Warteschlange"
|
||||
dev: "Fehler beim Erstellen der Applikation. Bitte versuche es erneut."
|
||||
ai-chan-kawaii: "Ai-chan kawaii!"
|
||||
you: "Du"
|
||||
auth/views/form.vue:
|
||||
share-access: "Erlaubst Du <i>{name}</i> auf deinen Account zuzugreifen?"
|
||||
@ -194,19 +311,37 @@ auth/views/form.vue:
|
||||
auth/views/index.vue:
|
||||
loading: "Lädt"
|
||||
denied: "Autorisierung der Anwendung wurde verweigert."
|
||||
denied-paragraph: "Diese App kann nicht auf deinen Account zugreifen."
|
||||
already-authorized: "Diese Anwendung ist bereits autorisiert."
|
||||
allowed: "Autorisierung der Anwendung wurde erlaubt."
|
||||
callback-url: "Zur App zurückkehren"
|
||||
please-go-back: "Bitte gehe zurück zur Anwendung."
|
||||
error: "Sitzung ist nicht vorhanden."
|
||||
sign-in: "Bitte melde dich an."
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Verifizierter Benutzer"
|
||||
pinned-users: "Vorgeschlagen"
|
||||
popular-users: "Beliebt"
|
||||
recently-updated-users: "Kürzlich aktiv"
|
||||
recently-registered-users: "Neue Benutzer"
|
||||
popular-tags: "Beliebte Tags"
|
||||
federated: "Aus dem Fediverse"
|
||||
explore: "{host} erkunden"
|
||||
users-info: "Momentan sind {users} Nutzer hier registriert"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "Player öffnen"
|
||||
disable-player: "Player schließen"
|
||||
common/views/components/user-list.vue:
|
||||
no-users: "Keine Benutzer"
|
||||
common/views/components/games/reversi/reversi.vue:
|
||||
matching:
|
||||
waiting-for: "Warten auf {}"
|
||||
cancel: "Abbrechen"
|
||||
common/views/components/games/reversi/reversi.game.vue:
|
||||
surrender: "Aufgeben"
|
||||
surrendered: "durch Aufgabe"
|
||||
is-llotheo: "Der niedrigere gewinnt (Llotheo)"
|
||||
looped-map: "Spielbrettenden verbinden"
|
||||
can-put-everywhere: "Setzen ist überall erlaubt"
|
||||
common/views/components/games/reversi/reversi.index.vue:
|
||||
title: "Misskey Reversi"
|
||||
sub-title: "Spiele Reversi mit deinen Freunden!"
|
||||
@ -224,7 +359,14 @@ common/views/components/games/reversi/reversi.room.vue:
|
||||
black-or-white: "Schwarz/Weiß"
|
||||
black-is: "Schwarz ist {}"
|
||||
rules: "Regeln"
|
||||
is-llotheo: "Der niedrigere gewinnt (Llotheo)"
|
||||
looped-map: "Spielbrettenden verbinden"
|
||||
can-put-everywhere: "Setzen ist überall erlaubt"
|
||||
settings-of-the-bot: "Bot-Einstellungen"
|
||||
this-game-is-started-soon: "Spiel beginnt gleich"
|
||||
waiting-for-other: "Warte auf den Gegner"
|
||||
waiting-for-me: "Warten, bis du bereit bist"
|
||||
waiting-for-both: "Vorbereiten…"
|
||||
cancel: "Abbrechen"
|
||||
ready: "Bereit"
|
||||
common/views/components/connect-failed.vue:
|
||||
@ -255,10 +397,13 @@ common/views/components/media-banner.vue:
|
||||
sensitive: "Dieser Inhalt ist NSFW"
|
||||
click-to-show: "Klicke zum den Inhalt anzusehen"
|
||||
common/views/components/theme.vue:
|
||||
theme: "Design"
|
||||
light-theme: "Thema"
|
||||
dark-theme: "Thema während des Nachtmodus"
|
||||
light-themes: "Helles Thema"
|
||||
dark-themes: "Dunkles Thema"
|
||||
install-a-theme: "Design wird installiert"
|
||||
theme-code: "Design-Quelltext"
|
||||
install: "Anwenden"
|
||||
installed: "\"{}\" wurde installiert"
|
||||
create-a-theme: "Thema erstellen"
|
||||
@ -269,24 +414,39 @@ common/views/components/theme.vue:
|
||||
base-theme: "Basisthema"
|
||||
base-theme-light: "Hell"
|
||||
base-theme-dark: "Dunkel"
|
||||
find-more-theme: "Mehr Designs finden"
|
||||
theme-name: "Name des Themas"
|
||||
preview-created-theme: "Vorschau"
|
||||
invalid-theme: "Thema ist ungültig"
|
||||
already-installed: "Thema ist bereits installiert"
|
||||
saved: "Gespeichert"
|
||||
manage-themes: "Designs verwalten"
|
||||
builtin-themes: "Standard-Designs"
|
||||
my-themes: "Meine Designs"
|
||||
installed-themes: "Installierte Designs"
|
||||
select-theme: "Design wählen"
|
||||
uninstall: "Deinstallieren"
|
||||
uninstalled: "„{}“ wurde deinstalliert"
|
||||
author: "Autor"
|
||||
desc: "Beschreibung"
|
||||
export: "Exportieren"
|
||||
import: "Importieren"
|
||||
import-by-code: "oder Quelltext einfügen"
|
||||
theme-name-required: "Design-Name ist erforderlich"
|
||||
common/views/components/cw-button.vue:
|
||||
hide: "Ausblenden"
|
||||
show: "Mehr"
|
||||
chars: "{count} Zeichen"
|
||||
files: "{count} Dateien"
|
||||
poll: "Umfrage"
|
||||
common/views/components/messaging.vue:
|
||||
search-user: "Einen Nutzer suchen"
|
||||
you: "Du"
|
||||
no-history: "Keine Chronik"
|
||||
user: "Benutzer"
|
||||
group: "Gruppen"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "Keine Unterhaltungen"
|
||||
no-history: "Keine weitere Chronik vorhanden"
|
||||
resize-form: "Ziehen um die Größe zu verändern"
|
||||
new-message: "Neue Nachricht"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Nachricht hier eingeben"
|
||||
@ -306,12 +466,28 @@ common/views/components/nav.vue:
|
||||
develop: "Entwickler"
|
||||
feedback: "Feedback"
|
||||
common/views/components/note-menu.vue:
|
||||
mention: "Erwähnungen"
|
||||
detail: "Details"
|
||||
copy-content: "Inhalt kopieren"
|
||||
copy-link: "Link kopieren"
|
||||
favorite: "Diesen Beitrag favorisieren"
|
||||
unfavorite: "Aus Favoriten entfernen"
|
||||
watch: "Beobachten"
|
||||
unwatch: "Nicht mehr beobachten"
|
||||
pin: "An die Profilseite pinnen"
|
||||
unpin: "Lösen"
|
||||
delete: "Löschen"
|
||||
delete-confirm: "Diesen Beitrag löschen?"
|
||||
remote: "Auf Quelle anzeigen"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "Erwähnungen"
|
||||
mute: "Stummschalten"
|
||||
unmute: "Stummschaltung aufheben"
|
||||
mute-confirm: "Bist du sicher, dass du diesen Nutzer stummschalten möchtest?"
|
||||
unmute-confirm: "Stummschaltung für diesen Nutzer aufheben?"
|
||||
block: "Sperren"
|
||||
unblock: "Sperrung aufheben"
|
||||
block-confirm: "Diesen Nutzer wirklich sperren?"
|
||||
common/views/components/poll.vue:
|
||||
vote-to: "Stimme für '{}'"
|
||||
vote-count: "{} Stimmen"
|
||||
@ -389,6 +565,14 @@ common/views/components/profile-editor.vue:
|
||||
export-targets:
|
||||
user-lists: "Listen"
|
||||
enter-password: "Bitte Passwort eingeben"
|
||||
common/views/components/user-group-editor.vue:
|
||||
invite: "Einladen"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Listen"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "Gruppen"
|
||||
owned-groups: "Meine Gruppen"
|
||||
invites: "Einladen"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Laden"
|
||||
no-broadcasts: "Keine Broadcasts"
|
||||
@ -398,6 +582,7 @@ common/views/widgets/photo-stream.vue:
|
||||
title: "Fotostream"
|
||||
no-photos: "Keine Fotos"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "Beitrags-Aktivität"
|
||||
toggle: "Sicht umschalten"
|
||||
common/views/widgets/server.vue:
|
||||
title: "Serverinformationen"
|
||||
@ -485,7 +670,7 @@ desktop/views/components/followers.vue:
|
||||
desktop/views/components/following.vue:
|
||||
empty: "Du folgst niemanden"
|
||||
desktop/views/components/home.vue:
|
||||
done: "Verbunden"
|
||||
done: "Beenden"
|
||||
add-widget: "Widget hinzufügen:"
|
||||
add: "Hinzufügen"
|
||||
desktop/views/input-dialog.vue:
|
||||
@ -500,6 +685,7 @@ desktop/views/components/note-detail.vue:
|
||||
desktop/views/components/note.vue:
|
||||
reply: "Antworten"
|
||||
renote: "Anmerken"
|
||||
detail: "Details"
|
||||
private: "Dieser Beitrag ist privat"
|
||||
deleted: "Dieser Beitrag wurde entfernt"
|
||||
desktop/views/components/notes.vue:
|
||||
@ -578,6 +764,7 @@ desktop/views/components/timeline.vue:
|
||||
desktop/views/components/ui.header.account.vue:
|
||||
profile: "Dein Profil"
|
||||
lists: "Listen"
|
||||
groups: "Gruppen"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "Spielen"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@ -586,17 +773,18 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Einen neuen Post erstellen"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Suchen"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Listen"
|
||||
desktop/views/components/users-list.vue:
|
||||
fetching: "Lade…"
|
||||
admin/views/dashboard.vue:
|
||||
drive: "Drive"
|
||||
admin/views/abuse.vue:
|
||||
details: "Details"
|
||||
remove-report: "Löschen"
|
||||
admin/views/instance.vue:
|
||||
recaptcha-preview: "Vorschau"
|
||||
invite: "Einladen"
|
||||
save: "Speichern"
|
||||
saved: "Gespeichert"
|
||||
admin/views/charts.vue:
|
||||
drive: "Drive"
|
||||
admin/views/drive.vue:
|
||||
@ -605,8 +793,6 @@ admin/views/drive.vue:
|
||||
delete: "Löschen"
|
||||
admin/views/users.vue:
|
||||
users:
|
||||
state:
|
||||
verified: "Verifizierter Benutzer"
|
||||
origin:
|
||||
local: "Lokal"
|
||||
admin/views/emoji.vue:
|
||||
@ -618,6 +804,7 @@ admin/views/announcements.vue:
|
||||
save: "Speichern"
|
||||
remove: "Löschen"
|
||||
add: "Hinzufügen"
|
||||
saved: "Gespeichert"
|
||||
admin/views/federation.vue:
|
||||
status: "Status"
|
||||
desktop/views/pages/note.vue:
|
||||
@ -676,14 +863,13 @@ mobile/views/components/ui.nav.vue:
|
||||
notifications: "Benachrichtigungen"
|
||||
search: "Suchen"
|
||||
user-lists: "Listen"
|
||||
user-groups: "Gruppen"
|
||||
game: "Spielen"
|
||||
about: "Über"
|
||||
mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Eine Datei hochladen"
|
||||
create-folder: "Ein Verzeichnis erstellen"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Listen"
|
||||
mobile/views/pages/home.vue:
|
||||
home: "Home"
|
||||
local: "Lokal"
|
||||
@ -737,6 +923,7 @@ dev/views/new-app.vue:
|
||||
authority-desc: "Nur die hier eingetragenen Berechtigungen, werden per API zur Verfügung stehen."
|
||||
authority-warning: "Dies kann auch nach dem erstellen der Anwendung geändert werden, allerdings werden dann alle bisher generierten Token ungültig."
|
||||
pages:
|
||||
like: "Gefällt mir"
|
||||
blocks:
|
||||
post: "\"Neuer Beitrag\"-Formular"
|
||||
script:
|
||||
|
@ -251,9 +251,9 @@ common:
|
||||
update-available-title: "Update available"
|
||||
update-available: "A new version of Misskey is now available({newer}, the current version is {current}). Reload the page to apply updates."
|
||||
my-token-regenerated: "Your token has been regenerated, so you will be signed out."
|
||||
verified-user: "Verified account"
|
||||
hide-password: "Hide Password"
|
||||
show-password: "Show Password"
|
||||
enter-username: "Enter user name"
|
||||
do-not-use-in-production: "This is a development build. Do not use in production."
|
||||
user-suspended: "This user has been suspended."
|
||||
is-remote-user: "The information about this user may not be entirely complete."
|
||||
@ -319,7 +319,7 @@ auth/views/index.vue:
|
||||
error: "Session does not exist."
|
||||
sign-in: "Please sign in."
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Official accounts"
|
||||
pinned-users: "Higlighted users"
|
||||
popular-users: "Popular users"
|
||||
recently-updated-users: "Recently active users"
|
||||
recently-registered-users: "Users who joined recently"
|
||||
@ -449,18 +449,23 @@ common/views/components/messaging.vue:
|
||||
search-user: "Find a user"
|
||||
you: "You"
|
||||
no-history: "Without history"
|
||||
user: "User"
|
||||
group: "Group"
|
||||
start-with-user: "Start chatting with a user"
|
||||
start-with-group: "Start a group and chat"
|
||||
select-group: "Select a group"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "No conversations"
|
||||
not-talked-user: "You have not talked to this user yet"
|
||||
not-talked-group: "There is no conversation in this group"
|
||||
no-history: "There is no further history"
|
||||
resize-form: "Drag to resize"
|
||||
new-message: "New message"
|
||||
only-one-file-attached: "Only ONE file can be attached to a message."
|
||||
only-one-file-attached: "You can only attach one file to a message"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Enter message here"
|
||||
send: "Send"
|
||||
attach-from-local: "Attach files from your device"
|
||||
attach-from-drive: "Attach files from your Drive"
|
||||
only-one-file-attached: "Only one file can be attached to the message."
|
||||
only-one-file-attached: "You can only attach one file to a message"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "Read"
|
||||
deleted: "This message has been deleted"
|
||||
@ -473,6 +478,7 @@ common/views/components/nav.vue:
|
||||
repository: "Repository"
|
||||
develop: "Developers"
|
||||
feedback: "Feedback"
|
||||
tos: "Terms Of Service"
|
||||
common/views/components/note-menu.vue:
|
||||
mention: "Mention"
|
||||
detail: "Details"
|
||||
@ -544,6 +550,7 @@ common/views/components/poll-editor.vue:
|
||||
day: "S"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Send a reaction"
|
||||
input-reaction-placeholder: "or input Emoji"
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "Custom Emoji"
|
||||
people: "People"
|
||||
@ -585,6 +592,8 @@ common/views/components/signup.vue:
|
||||
password-matched: "OK"
|
||||
password-not-matched: "Doesn't match"
|
||||
recaptcha: "Verification"
|
||||
agree-to: "Accept {0}."
|
||||
tos: "Terms Of Service"
|
||||
create: "Create an Account"
|
||||
some-error: "An attempt at account creation has failed for some reason. Please try again."
|
||||
common/views/components/special-message.vue:
|
||||
@ -692,9 +701,30 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "Remove from this list"
|
||||
delete-are-you-sure: "Delete list \"$1\"?"
|
||||
deleted: "Deleted successfully"
|
||||
add-user: "Add a user"
|
||||
common/views/components/user-group-editor.vue:
|
||||
users: "Members"
|
||||
rename: "Rename group"
|
||||
delete: "Delete group"
|
||||
transfer: "transfer group"
|
||||
transfer-are-you-sure: "Are you sure you want to add @$2 to the group $1?"
|
||||
remove-user: "Remove a user from this group"
|
||||
deleted: "Deleted"
|
||||
invite: "Invite"
|
||||
invited: "The invitation was successfully sent"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Lists"
|
||||
create-list: "Create a list"
|
||||
list-name: "List name"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "Group"
|
||||
create-group: "Create a group"
|
||||
group-name: "Group name"
|
||||
owned-groups: "My groups"
|
||||
joined-groups: "Membership in groups"
|
||||
invites: "Invite"
|
||||
accept-invite: "Join"
|
||||
reject-invite: "Decline"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Checking"
|
||||
no-broadcasts: "No announcements"
|
||||
@ -756,6 +786,10 @@ common/views/pages/follow.vue:
|
||||
request-pending: "Pending follow request"
|
||||
follow-processing: "Processing follow"
|
||||
follow-request: "Follow request"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "Follow requests"
|
||||
accept: "Accept"
|
||||
reject: "Reject"
|
||||
desktop:
|
||||
banner-crop-title: "Crop the part that appears as a banner"
|
||||
banner: "Banner"
|
||||
@ -772,7 +806,7 @@ desktop/views/components/activity.chart.vue:
|
||||
total: "Black ... Total"
|
||||
notes: "Blue ... Notes"
|
||||
replies: "Red ... Replies"
|
||||
renotes: "Green ... Repost"
|
||||
renotes: "Green ... Renotes"
|
||||
desktop/views/components/activity.vue:
|
||||
title: "Activity"
|
||||
toggle: "Toggle views"
|
||||
@ -869,7 +903,7 @@ desktop/views/components/note-detail.vue:
|
||||
private: "Post is private"
|
||||
deleted: "Post has been removed"
|
||||
location: "Location"
|
||||
renote: "Repost"
|
||||
renote: "Renote"
|
||||
add-reaction: "Add a reaction"
|
||||
undo-reaction: "Reverse reaction"
|
||||
desktop/views/components/note.vue:
|
||||
@ -893,10 +927,10 @@ desktop/views/components/post-form.vue:
|
||||
quote-placeholder: "Quote this Post..."
|
||||
submit: "Post"
|
||||
reply: "Reply"
|
||||
renote: "Repost"
|
||||
renote: "Renote"
|
||||
posted: "Posted!"
|
||||
replied: "Replied!"
|
||||
reposted: "Reposted!"
|
||||
reposted: "Renoted!"
|
||||
note-failed: "Failed to post"
|
||||
reply-failed: "Failed to reply"
|
||||
renote-failed: "Failed to Renote"
|
||||
@ -924,13 +958,13 @@ desktop/views/components/progress-dialog.vue:
|
||||
desktop/views/components/renote-form.vue:
|
||||
quote: "Quote..."
|
||||
cancel: "Cancel"
|
||||
renote: "Repost"
|
||||
renote: "Renote"
|
||||
renote-home: "Renote (Home)"
|
||||
reposting: "Renoting..."
|
||||
success: "Reposted!"
|
||||
failure: "Failed to Repost"
|
||||
success: "Renoted!"
|
||||
failure: "Failed to Renote"
|
||||
desktop/views/components/renote-form-window.vue:
|
||||
title: "Do you want to Renote it?"
|
||||
title: "Do you want to renote it?"
|
||||
desktop/views/pages/user-following-or-followers.vue:
|
||||
following: "{user}'s following"
|
||||
followers: "{user}'s follower"
|
||||
@ -1027,6 +1061,7 @@ desktop/views/components/ui.header.vue:
|
||||
desktop/views/components/ui.header.account.vue:
|
||||
profile: "Your profile"
|
||||
lists: "Lists"
|
||||
groups: "Group"
|
||||
follow-requests: "Follow requests"
|
||||
admin: "Admin"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
@ -1037,12 +1072,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Compose new Post"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Search"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "Follow requests"
|
||||
accept: "Accept"
|
||||
reject: "Reject"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "User lists"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Posts"
|
||||
following: "Following"
|
||||
@ -1064,7 +1093,6 @@ admin/views/index.vue:
|
||||
users: "Users"
|
||||
federation: "Federation"
|
||||
announcements: "Announcements"
|
||||
hashtags: "Hashtags"
|
||||
abuse: "Abuse"
|
||||
queue: "Job Queue"
|
||||
logs: "Logs"
|
||||
@ -1091,14 +1119,26 @@ admin/views/instance.vue:
|
||||
instance-name: "Instance name"
|
||||
instance-description: "Instance description"
|
||||
host: "Host"
|
||||
icon-url: "URL of the icon"
|
||||
logo-url: "URL of the logo"
|
||||
banner-url: "Banner image URL"
|
||||
error-image-url: "Error image URL"
|
||||
languages: "Language of this instance"
|
||||
languages-desc: "You can add more than one, separated by spaces."
|
||||
tos-url: "Terms of Service URL"
|
||||
repository-url: "Repository URL"
|
||||
feedback-url: "URL for feedback"
|
||||
maintainer-config: "Administrator information"
|
||||
maintainer-name: "Administrator name"
|
||||
maintainer-email: "Contact Administrator"
|
||||
advanced-config: "Other settings"
|
||||
drive-config: "Drive settings"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-endpoint: "Endpoint"
|
||||
object-storage-port: "Port"
|
||||
object-storage-access-key: "Access Key"
|
||||
object-storage-secret-key: "Secret Key"
|
||||
object-storage-use-ssl: "Use SSL"
|
||||
cache-remote-files: "Cache remote files"
|
||||
cache-remote-files-desc: "Without this parameter, all remote files are linked to their host server directly. This will be an effective solution to save your server storage, however make remote files invisible to users who set direct-link disabled, since no thumbnail will be generated, increase traffic. It is recommended that this parameter set enabled."
|
||||
local-drive-capacity-mb: "Volume of Drive per user"
|
||||
@ -1107,8 +1147,8 @@ admin/views/instance.vue:
|
||||
recaptcha-config: "the reCAPTCHA settings"
|
||||
recaptcha-info: "reCAPTCHA token is required. Please get it on https://www.google.com/recaptcha/intro/"
|
||||
enable-recaptcha: "enable reCAPTCHA"
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
recaptcha-secret-key: "Secret Key"
|
||||
recaptcha-preview: "Preview"
|
||||
twitter-integration-config: "Settings of connecting to Twitter"
|
||||
twitter-integration-info: "The callback URL is set on {url}."
|
||||
enable-twitter-integration: "Enable connection to Twitter"
|
||||
@ -1139,7 +1179,7 @@ admin/views/instance.vue:
|
||||
invite: "Invite"
|
||||
save: "Save"
|
||||
saved: "Saved"
|
||||
user-recommendation-config: "Recommended users"
|
||||
pinned-users: "Higlighted user"
|
||||
email-config: "Email server settings"
|
||||
email-config-info: "Used to confirm email and password reset etc."
|
||||
enable-email: "Enable email delivery"
|
||||
@ -1223,14 +1263,10 @@ admin/views/users.vue:
|
||||
silence-confirm: "Silence user?"
|
||||
unmake-silence: "Unsilence"
|
||||
unsilence-confirm: "Are you certain that you want to stop silencing this user?"
|
||||
verify: "Verify account"
|
||||
verify-confirm: "Do you want this to be a verified account?"
|
||||
verified: "The account is now being verified"
|
||||
unverify: "Unverify account"
|
||||
unverify-confirm: "Do you want to remove the 'verified account' designation?"
|
||||
unverified: "The account is now being unverified"
|
||||
update-remote-user: "Update information about remote user"
|
||||
remote-user-updated: "The information regarding the remote user has been updated."
|
||||
delete-all-files: "Delete all files"
|
||||
delete-all-files-confirm: "Are you sure that you want to delete all files?"
|
||||
users:
|
||||
title: "Users"
|
||||
sort:
|
||||
@ -1245,7 +1281,6 @@ admin/views/users.vue:
|
||||
admin: "Administrator"
|
||||
moderator: "Moderator"
|
||||
adminOrModerator: "Admin/Moderator"
|
||||
verified: "Verified account"
|
||||
silenced: "Already silenced"
|
||||
suspended: "Suspended"
|
||||
origin:
|
||||
@ -1307,6 +1342,7 @@ admin/views/federation.vue:
|
||||
latest-request-received-at: "Last request received at"
|
||||
remove-all-following: "Withold all followers"
|
||||
remove-all-following-info: "Unfollow all accounts from {host}. Please run this if the instance no longer exists."
|
||||
delete-all-files: "Remove all files"
|
||||
block: "Block"
|
||||
marked-as-closed: "Marked as closed"
|
||||
lookup: "Look up"
|
||||
@ -1353,6 +1389,7 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "Hourly"
|
||||
day: "Daily"
|
||||
blocked-hosts: "Blocking"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "More details..."
|
||||
timeline: "Timeline"
|
||||
@ -1509,6 +1546,7 @@ mobile/views/components/ui.nav.vue:
|
||||
follow-requests: "Follow requests"
|
||||
search: "Search"
|
||||
user-lists: "Lists"
|
||||
user-groups: "Group"
|
||||
widgets: "Widgets"
|
||||
game: "Games"
|
||||
admin: "Admin"
|
||||
@ -1521,8 +1559,6 @@ mobile/views/pages/drive.vue:
|
||||
rename-folder: "Rename folder"
|
||||
move-folder: "Move this folder"
|
||||
delete-folder: "Delete this folder"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Lists"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "Your account is now ready! 📦"
|
||||
mobile/views/pages/followers.vue:
|
||||
@ -1547,10 +1583,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
activity: "Activity"
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "Share on {name}"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "Follow requests"
|
||||
accept: "Accept"
|
||||
reject: "Reject"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "Post"
|
||||
prev: "Previous note"
|
||||
@ -1656,6 +1688,10 @@ pages:
|
||||
edit-this-page: "Edit this page"
|
||||
view-source: "View Source"
|
||||
view-page: "View page"
|
||||
like: "Like"
|
||||
unlike: "Unlike"
|
||||
liked-pages: "Favorite pages"
|
||||
my-pages: "My pages"
|
||||
inspector: "Inspector"
|
||||
content: "Page block"
|
||||
variables: "Variables"
|
||||
|
@ -12,7 +12,9 @@ common:
|
||||
rich-contents: "Posts"
|
||||
rich-contents-desc: "Escribe sobre tus pensamientos, eventos, todo lo que quieras compartir. Si es necesario, puedes usar varias sintaxis, decorar tus posts y añadir tus imágenes favoritas, archivos de viddeo y encuestas."
|
||||
reaction: "Reacciones"
|
||||
reaction-desc: "La forma mas facil de expresar tus emociones. Misskey te permite añadir varios tipos de reacciones a los posts de otros usuarios. La emperiencia emocional en Misskey nunca será igual que en otra red social, donde solo puedes poner \"likes\"."
|
||||
ui: "Interfaz"
|
||||
ui-desc: "No hay ninguna interfaz que le vaya bien a todos. Por eso, Misskey tiene una interfaz altamente personalizable para tus gustos. Puedes hacer tu página principal única editando la interfaz de tu timeline y moviendo varios widgets para conseguir hacer de este lugar uno propio."
|
||||
drive: "Drive"
|
||||
adblock:
|
||||
detected: "Por favor, desactive el bloqueador de publicidad."
|
||||
@ -55,6 +57,7 @@ common:
|
||||
month-and-day: "{day} de {month}"
|
||||
trash: "Papelera"
|
||||
drive: "Drive"
|
||||
pages: "Páginas"
|
||||
messaging: "Conversación"
|
||||
home: "Inicio"
|
||||
deck: "Deck"
|
||||
@ -70,8 +73,12 @@ common:
|
||||
"write:blocks": "Editar bloques"
|
||||
"read:favorites": "Ver favoritos"
|
||||
"write:favorites": "Editar favoritos"
|
||||
"read:following": "Ver información de seguidor"
|
||||
"read:messaging": "Ver conversación"
|
||||
"read:mutes": "Ver silenciados"
|
||||
"write:notes": "Crear y eliminar articulos"
|
||||
"read:notifications": "Ver notificaciones"
|
||||
"read:reactions": "Ver reacciones"
|
||||
"write:votes": "Vota"
|
||||
weekday-short:
|
||||
sunday: "domingo"
|
||||
@ -136,8 +143,11 @@ common:
|
||||
default-note-visibility: "Rango de publicación predeterminado"
|
||||
web-search-engine: "Buscador web"
|
||||
web-search-engine-desc: "Ejemplo: https://www.google.com/?#q={{query}}"
|
||||
this-setting-is-this-device-only: "Solo para este dispositivo"
|
||||
use-os-default-emojis: "Usar los emoticonos estándar del sistema operativo"
|
||||
line-width: "Grosor de línea"
|
||||
line-width-thin: "Fino"
|
||||
line-width-normal: "Normal"
|
||||
line-width-thick: "Grosor"
|
||||
font-size: "Tamaño del texto"
|
||||
font-size-x-small: "Muy pequeño"
|
||||
@ -162,13 +172,20 @@ common:
|
||||
wallpaper: "Fondo de pantalla"
|
||||
choose-wallpaper: "Escoge un fondo de pantalla"
|
||||
delete-wallpaper: "Quitar fondo de pantalla"
|
||||
post-form-on-timeline: "Mostrar el formulario de las entradas encima de la línea de tiempo"
|
||||
show-clock-on-header: "Muestra el reloj en la parte superior derecha"
|
||||
show-reply-target: "Mostrar destinatario de la mención"
|
||||
timeline: "Timeline"
|
||||
show-my-renotes: "Mostrar mis renotes en la timeline"
|
||||
show-renoted-my-notes: "Mostrar renotes de mis posts en la timeline"
|
||||
sound: "Sonido"
|
||||
enable-sounds: "Habilitar sonido"
|
||||
volume: "Volúmen"
|
||||
test: "Prueba"
|
||||
update: "Actualizar Misskey"
|
||||
version: "Versión"
|
||||
latest-version: "Última versión"
|
||||
update-checking: "Buscando actualizaciones"
|
||||
no-updates: "No hay actualizaciones disponibles"
|
||||
no-updates-desc: "Tu Misskey está actualizado"
|
||||
update-available: "¡Una nueva versión está disponible!"
|
||||
@ -178,18 +195,36 @@ common:
|
||||
search: "Buscar"
|
||||
delete: "eliminar"
|
||||
loading: "cargando"
|
||||
ok: "Confirmar"
|
||||
cancel: "Cancelar"
|
||||
update-available-title: "Actualización disponible"
|
||||
update-available: "Hay disponible una nueva versión de Misskey ({newer}, la versión actual es {current}). Refresca la página para aplicar las actualizaciones."
|
||||
my-token-regenerated: "Tu token se ha regenerado vas a ser desconectado."
|
||||
verified-user: "Cuenta verificada"
|
||||
hide-password: "Ocultar contraseña"
|
||||
show-password: "Mostrar contraseña"
|
||||
do-not-use-in-production: "Esto está en desarrollo, no usarlo para producción."
|
||||
user-suspended: "Este usuario ha sido suspendido"
|
||||
is-remote-user: "La información sobre este usuario puede no estar completa"
|
||||
is-remote-post: "Es una publicación remota"
|
||||
view-on-remote: "Consultar el perfil completo"
|
||||
renoted-by: "Renotado por {user}"
|
||||
no-notes: "No hay publicaciones"
|
||||
turn-on-darkmode: "Cambiar a modo oscuro"
|
||||
turn-off-darkmode: "Modo claro"
|
||||
error:
|
||||
title: "Se ha producido un problema :("
|
||||
retry: "Inténtalo otra vez"
|
||||
reversi:
|
||||
drawn: "Empatado"
|
||||
my-turn: "Mi turno"
|
||||
opponent-turn: "Turno del oponente"
|
||||
turn-of: "Turno de {name}"
|
||||
past-turn-of: "Turno de {name}"
|
||||
won: "{name} ha ganado"
|
||||
black: "Negro"
|
||||
white: "Blanco"
|
||||
total: "Total"
|
||||
this-turn: "Turno {count}"
|
||||
widgets:
|
||||
analog-clock: "Reloj analógico"
|
||||
profile: "Perfil"
|
||||
@ -212,8 +247,12 @@ common:
|
||||
nav: "Navegación"
|
||||
tips: "Consejos"
|
||||
hashtags: "Etiquetas"
|
||||
queue: "En cola"
|
||||
dev: "Se ha producido un error creando la aplicación. Intentelo de nuevo."
|
||||
ai-chan-kawaii: "Ai-chan es muy mona!"
|
||||
you: "Tú"
|
||||
auth/views/form.vue:
|
||||
share-access: "¿Deseas permitir a <i>{name}</i> acceder a tu cuenta?"
|
||||
permission-ask: "La aplicación requiere los siguientes permisos:"
|
||||
cancel: "Cancelar"
|
||||
accept: "Garantizar acceso."
|
||||
@ -228,7 +267,18 @@ auth/views/index.vue:
|
||||
error: "Esta sesión no existe."
|
||||
sign-in: "Por favor inicia sesión."
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Cuenta verificada"
|
||||
popular-users: "Usuarios populares"
|
||||
recently-updated-users: "Usuarios activos recientemente"
|
||||
recently-registered-users: "Usuarios que se han unido recientemente"
|
||||
popular-tags: "Etiquetas populares"
|
||||
federated: "Desde el fediverso"
|
||||
explore: "Explorar {host}"
|
||||
users-info: "Actualmente hay {users} registrados aquí"
|
||||
common/views/components/url-preview.vue:
|
||||
enable-player: "Activar reproducción"
|
||||
disable-player: "Cerrar el reproductor"
|
||||
common/views/components/user-list.vue:
|
||||
no-users: "No hay usuarios."
|
||||
common/views/components/games/reversi/reversi.vue:
|
||||
matching:
|
||||
waiting-for: "Esperando por {}"
|
||||
@ -244,6 +294,7 @@ common/views/components/games/reversi/reversi.index.vue:
|
||||
sub-title: "¡Juega Reversi con tus amigos!"
|
||||
invite: "Invitar"
|
||||
rule: "Cómo jugar"
|
||||
rule-desc: "Reversi es un juego de estrategia para dos jugadores, el cual se juega en un tablero de 8x8. Hay 64 fichas llamadas discos, las cuales son claras de un lado y oscuras del otro. Los jugadores toman turnos colocando fichas en el tablero con su color asignado mirando hacia arriba. Durante una jugada, cualquier disco del color del oponente que esté en fila entre un disco del oponente y otro del mismo color, será volteado para tener el color del jugador que haya hecho la movida. El objetivo del juego es tener la mayoría de los discos de tu color cuando el último cuadro es llenado."
|
||||
mode-invite: "Invitar"
|
||||
mode-invite-desc: "Invitar un usuario al juego."
|
||||
invitations: "¡Has recibido una invitación!"
|
||||
@ -300,23 +351,40 @@ common/views/components/media-banner.vue:
|
||||
click-to-show: "Click para mostrar"
|
||||
common/views/components/theme.vue:
|
||||
theme: "Tema"
|
||||
light-theme: "Tema a usar en Light mode"
|
||||
dark-theme: "Tema a usar en dark mode"
|
||||
light-themes: "Tema claro"
|
||||
dark-themes: "Tema oscuro"
|
||||
install-a-theme: "Instalar tema"
|
||||
theme-code: "Código del tema"
|
||||
install: "Instalación"
|
||||
installed: "\"{}\" se ha instalado"
|
||||
create-a-theme: "Crear tema"
|
||||
save-created-theme: "Guardar tema"
|
||||
primary-color: "Color primario"
|
||||
secondary-color: "Color secundario"
|
||||
text-color: "Color del texto"
|
||||
base-theme: "Tema base"
|
||||
base-theme-light: "Claro"
|
||||
base-theme-dark: "Oscuro"
|
||||
find-more-theme: "Obtener más temas"
|
||||
theme-name: "Nombre del tema"
|
||||
preview-created-theme: "Vista previa"
|
||||
invalid-theme: "No es un tema válido"
|
||||
already-installed: "Este tema ya está instalado."
|
||||
saved: "Guardado"
|
||||
manage-themes: "Gestor de temas"
|
||||
builtin-themes: "Temas estandar"
|
||||
my-themes: "Mis temas"
|
||||
installed-themes: "Temas instalados"
|
||||
select-theme: "Elegir tema"
|
||||
uninstall: "Desinstalar"
|
||||
uninstalled: "\"{}\" ha sido desinstalado"
|
||||
author: "Autor"
|
||||
desc: "Descripción"
|
||||
export: "Exportar"
|
||||
import: "Importar"
|
||||
import-by-code: "o pega el código"
|
||||
common/views/components/cw-button.vue:
|
||||
show: "Mostrar"
|
||||
chars: "{count} letras"
|
||||
@ -327,17 +395,13 @@ common/views/components/messaging.vue:
|
||||
you: "Tu"
|
||||
no-history: "Sin historial"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "Sin conversaciones"
|
||||
no-history: "El historial se ha acabado"
|
||||
resize-form: "Arrastra para redimensionar"
|
||||
new-message: "Nuevo mensaje"
|
||||
only-one-file-attached: "Un único archivo se puede conectar al mensaje"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Escribe el mensaje aquí"
|
||||
send: "Enviar"
|
||||
attach-from-local: "Adjunta ficheros desde tu PC"
|
||||
attach-from-drive: "Adjunta ficheros desde tu disco"
|
||||
only-one-file-attached: "Un único archivo se puede conectar al mensaje"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "Leer"
|
||||
deleted: "El mensaje se ha borrado"
|
||||
@ -430,10 +494,25 @@ common/views/components/stream-indicator.vue:
|
||||
connected: "Conectado"
|
||||
common/views/components/notification-settings.vue:
|
||||
title: "Notificaciones"
|
||||
common/views/components/integration-settings.vue:
|
||||
title: "Integraciones"
|
||||
connect: "Conectar"
|
||||
disconnect: "Desconectarse"
|
||||
connected-to: "Estas conectado a la siguiente cuenta"
|
||||
common/views/components/github-setting.vue:
|
||||
description: "Una vez conectada tu cuenta de GitHub a Misskey podrás ver la información sobre tu perfil de GitHub y además podrás registrarte mediante tu cuenta de GitHub."
|
||||
connected-to: "Estas conectado a esta cuenta de GitHub"
|
||||
detail: "Ver detalles..."
|
||||
reconnect: "Reconectar"
|
||||
connect: "Vincular tu cuenta de GitHub"
|
||||
disconnect: "Desconectarse"
|
||||
common/views/components/discord-setting.vue:
|
||||
description: "Una vez conectada tu cuenta de Discord a Misskey podrás ver la información sobre tu perfil de Discord y además podrás registrarte mediante tu cuenta de Discord."
|
||||
connected-to: "Estas conectado a esta cuenta de Discord"
|
||||
detail: "Ver detalles..."
|
||||
reconnect: "Reconectar"
|
||||
connect: "Vincular tu cuenta de Discord"
|
||||
disconnect: "Desconectarse"
|
||||
common/views/components/uploader.vue:
|
||||
waiting: "Un momento"
|
||||
common/views/components/visibility-chooser.vue:
|
||||
@ -445,29 +524,72 @@ common/views/components/visibility-chooser.vue:
|
||||
specified: "Directo"
|
||||
specified-desc: "Publica solo para los seguidores que quieras"
|
||||
local-public: "Público (sólo local)"
|
||||
local-public-desc: "No publicar para remoto"
|
||||
local-home: "Inicio (sólo local)"
|
||||
local-followers: "Seguidores (sólo local)"
|
||||
common/views/components/trends.vue:
|
||||
count: "{} usuarios mencionados"
|
||||
empty: "Ninguna tendencia popular ahora"
|
||||
common/views/components/language-settings.vue:
|
||||
title: "Mostrar idioma"
|
||||
pick-language: "Selecciona un idioma"
|
||||
recommended: "Recomendado"
|
||||
auto: "Automático"
|
||||
specify-language: "Especifica el idioma"
|
||||
info: "Necesitas recargar la página para que los cambios tengan efecto."
|
||||
common/views/components/profile-editor.vue:
|
||||
title: "Perfil"
|
||||
name: "Nombre"
|
||||
account: "Cuenta"
|
||||
location: "Localización"
|
||||
description: "Acerca de mí"
|
||||
you-can-include-hashtags: "También puedes incluir hashtags en la descripción de tu perfil."
|
||||
language: "Idioma"
|
||||
birthday: "Fecha de nacimiento"
|
||||
avatar: "Avatar"
|
||||
banner: "Banner"
|
||||
is-cat: "Esta cuenta es un gato"
|
||||
is-bot: "Esta cuenta es un bot"
|
||||
is-locked: "Las peticiones de seguimiento necesitan aprobación"
|
||||
careful-bot: "Las peticiones de seguimiento de bots necesitan aprobación"
|
||||
auto-accept-followed: "Aprobar automaticamente las peticiones de follow de gente a la que sigues"
|
||||
advanced: "Otros"
|
||||
privacy: "Privacidad"
|
||||
save: "Guardar"
|
||||
saved: "Perfil actualizado con exito"
|
||||
uploading: "Subiendo"
|
||||
upload-failed: "Error al subir"
|
||||
email: "Preferencias de correo"
|
||||
email-address: "Correo electrónico"
|
||||
email-verified: "Tu cuenta de correo ha sido verificada."
|
||||
email-not-verified: "Tu cuenta de correo no está verificada. Por favor comprueba tu bandeja de entrada."
|
||||
export: "Exportar"
|
||||
import: "Importar"
|
||||
export-and-import: "Exportar/Importar"
|
||||
export-targets:
|
||||
all-notes: "Todas las notas publicadas"
|
||||
following-list: "Seguidores"
|
||||
mute-list: "Silenciar"
|
||||
blocking-list: "Bloquear"
|
||||
user-lists: "Listas"
|
||||
export-requested: "Has solicitado una exportación. Esto puede tardar un rato. Después de que termine la exportación el archivo se añadirá al drive."
|
||||
import-requested: "Has empezado una importación. Esto puede tardar un rato."
|
||||
enter-password: "Escribe una contraseña"
|
||||
danger-zone: "Zona de peligro"
|
||||
delete-account: "Eliminar cuenta"
|
||||
account-deleted: "Esta cuenta ha sido eliminada. Puede tardar un rato hasta que toda la información desaparazca."
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Usuarios"
|
||||
rename: "Cambiar el nombre de la lista"
|
||||
delete: "Eliminar lista"
|
||||
remove-user: "Eliminar de la lista"
|
||||
common/views/components/user-group-editor.vue:
|
||||
invite: "Invitar"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Listas"
|
||||
list-name: "Nombre de lista"
|
||||
common/views/components/user-groups.vue:
|
||||
invites: "Invitar"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Recuperando"
|
||||
no-broadcasts: "Sin emisión"
|
||||
@ -526,6 +648,8 @@ common/views/pages/follow.vue:
|
||||
request-pending: "Solicitud pendiente"
|
||||
follow-processing: "Solicitud en proceso"
|
||||
follow-request: "Solicitar suscripción"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "Solicitudes de seguimiento"
|
||||
desktop:
|
||||
banner-crop-title: "Corta la parte que aparece como un banner"
|
||||
banner: "Banner"
|
||||
@ -769,12 +893,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Crear una publicación"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Buscar"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "Solicitudes de seguidores"
|
||||
accept: "Aceptar"
|
||||
reject: "Rechazar"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Listas de usuario"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Publicaciones"
|
||||
following: "Sigue"
|
||||
@ -788,24 +906,45 @@ admin/views/index.vue:
|
||||
instance: "Instancia"
|
||||
moderators: "Moderadores"
|
||||
users: "Usuarios"
|
||||
hashtags: "Hashtags"
|
||||
federation: "Federado"
|
||||
queue: "Cola de trabajos"
|
||||
logs: "Registros"
|
||||
back-to-misskey: "Volver a Misskey"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Panel de Control"
|
||||
accounts: "Cuenta"
|
||||
notes: "Publicaciones"
|
||||
drive: "Drive"
|
||||
instances: "Instancias"
|
||||
this-instance: "Esta instancia"
|
||||
federated: "Federado"
|
||||
admin/views/queue.vue:
|
||||
title: "Cola"
|
||||
remove-all-jobs: "Limpiar todos los trabajos pendientes"
|
||||
admin/views/abuse.vue:
|
||||
title: "Abuso"
|
||||
target: "Destinatario"
|
||||
reporter: "Informador"
|
||||
details: "Detalles"
|
||||
remove-report: "eliminar"
|
||||
admin/views/instance.vue:
|
||||
instance: "Instancia"
|
||||
instance-name: "Nombre de la instancia"
|
||||
instance-description: "Descripción de la instancia"
|
||||
host: "Host"
|
||||
recaptcha-secret-key: "clave secreta reCAPTCHA"
|
||||
banner-url: "URL de la imagen de banner"
|
||||
error-image-url: "Error en la URL de la imagen"
|
||||
languages: "Idioma de esta instancia"
|
||||
languages-desc: "Puedes añadir mas de uno, separado por espacios."
|
||||
maintainer-config: "Información del administrador"
|
||||
maintainer-name: "Nombre del administrador"
|
||||
maintainer-email: "Contactar con el administrador"
|
||||
drive-config: "Ajustes del Drive"
|
||||
cache-remote-files: "Mantener en cache los archivos remotos"
|
||||
recaptcha-preview: "Vista previa"
|
||||
invite: "Invitar"
|
||||
save: "Guardar"
|
||||
saved: "Guardado"
|
||||
email: "Correo electrónico"
|
||||
smtp-host: "Host SMTP"
|
||||
smtp-port: "Puerto SMTP"
|
||||
@ -834,7 +973,6 @@ admin/views/users.vue:
|
||||
state:
|
||||
all: "Todo"
|
||||
moderator: "Moderadores"
|
||||
verified: "Cuenta verificada"
|
||||
origin:
|
||||
local: "Local"
|
||||
admin/views/emoji.vue:
|
||||
@ -846,12 +984,14 @@ admin/views/announcements.vue:
|
||||
save: "Guardar"
|
||||
remove: "eliminar"
|
||||
add: "Agregar"
|
||||
saved: "Guardado"
|
||||
admin/views/federation.vue:
|
||||
instance: "Instancia"
|
||||
host: "Host"
|
||||
following: "Siguiendo"
|
||||
status: "Estado"
|
||||
block: "Bloquear"
|
||||
instances: "Federado"
|
||||
states:
|
||||
all: "Todo"
|
||||
blocked: "Bloquear"
|
||||
@ -859,6 +999,7 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "Por hora"
|
||||
day: "Por día"
|
||||
blocked-hosts: "Bloquear"
|
||||
desktop/views/pages/selectdrive.vue:
|
||||
cancel: "Cancelar"
|
||||
desktop/views/pages/user-list.users.vue:
|
||||
@ -935,8 +1076,6 @@ mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Subir fichero"
|
||||
create-folder: "Crear una carpeta"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Listas"
|
||||
mobile/views/pages/home.vue:
|
||||
home: "Inicio"
|
||||
local: "Local"
|
||||
@ -947,10 +1086,6 @@ mobile/views/pages/widgets.vue:
|
||||
customization-tips: "Consejos de personalización"
|
||||
mobile/views/pages/widgets/activity.vue:
|
||||
activity: "Actividad"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "Solicitudes de seguimiento"
|
||||
accept: "Aceptar"
|
||||
reject: "Rechazar"
|
||||
mobile/views/pages/games/reversi.vue:
|
||||
reversi: "Reversi"
|
||||
mobile/views/pages/search.vue:
|
||||
@ -970,6 +1105,7 @@ deck:
|
||||
deck/deck.user-column.vue:
|
||||
activity: "Actividad"
|
||||
pages:
|
||||
like: "Me gusta"
|
||||
blocks:
|
||||
post: "Formulario"
|
||||
script:
|
||||
|
@ -68,16 +68,23 @@ common:
|
||||
explore: "Découvrir"
|
||||
following: "Suit"
|
||||
followers: "Abonné·e·s"
|
||||
favorites: "Mettre cette note en favoris"
|
||||
favorites: "Favorites"
|
||||
permissions:
|
||||
"read:account": "Afficher les informations du compte"
|
||||
"write:account": "Mettre à jour les informations de votre compte"
|
||||
"read:blocks": "Voir les blocs"
|
||||
"write:blocks": "Écrire des blocs"
|
||||
"read:drive": "Parcourir le Drive"
|
||||
"write:drive": "Écrire sur le Drive"
|
||||
"read:favorites": "Afficher les favoris"
|
||||
"write:favorites": "Écrire des favoris"
|
||||
"read:messaging": "Lire les conversations"
|
||||
"write:messaging": "Utiliser la messagerie"
|
||||
"write:notes": "Créer ou supprimer des publications"
|
||||
"read:notifications": "Afficher les notifications"
|
||||
"write:notifications": "Gérer vos notifications"
|
||||
"read:reactions": "Lire les réactions"
|
||||
"write:reactions": "Gérer vos réactions"
|
||||
"write:votes": "Vote"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "Les utilisateurs suivants afficheront leurs publications sur votre fil."
|
||||
@ -133,7 +140,7 @@ common:
|
||||
notification: "Notifications"
|
||||
apps: "Applications"
|
||||
tags: "Hashtags"
|
||||
mute-and-block: "Silencer / Bloquer"
|
||||
mute-and-block: "Silencés / Bloqués"
|
||||
blocking: "En cours blocage"
|
||||
security: "Sécurité"
|
||||
signin: "Historique des connexions"
|
||||
@ -193,6 +200,7 @@ common:
|
||||
show-clock-on-header: "Afficher l'horloge sur le coté supérieur droit"
|
||||
timeline: "Fil d’actualité"
|
||||
show-my-renotes: "Afficher mes republications dans le fil"
|
||||
show-renoted-my-notes: "Afficher les partages de mes propres notes sur le fil"
|
||||
remain-deleted-note: "Continuer à afficher les notes supprimées"
|
||||
sound: "Son"
|
||||
enable-sounds: "Activer les sons"
|
||||
@ -234,9 +242,9 @@ common:
|
||||
update-available-title: "Mise à jour disponible"
|
||||
update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour."
|
||||
my-token-regenerated: "Votre jeton vient d’être généré, vous allez maintenant être déconnecté."
|
||||
verified-user: "Compte vérifié"
|
||||
hide-password: "Masquer le mot de passe"
|
||||
show-password: "Afficher le mot de passe"
|
||||
enter-username: "Saisir un nom d'utilisateur"
|
||||
do-not-use-in-production: "Il s’agit d’une version de développement. Ne pas utiliser dans un environnement de production."
|
||||
user-suspended: "Cet·te utilisateur·trice a été suspendu·e"
|
||||
is-remote-user: "Les informations à propos de ce compte peuvent être incomplètes."
|
||||
@ -302,7 +310,6 @@ auth/views/index.vue:
|
||||
error: "La session n’existe pas."
|
||||
sign-in: "Veuillez vous connecter"
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Comptes vérifiés"
|
||||
popular-users: "Utilisateurs populaires"
|
||||
recently-updated-users: "Utilisateurs actifs récemment"
|
||||
recently-registered-users: "Les nouveaux inscrits"
|
||||
@ -431,18 +438,22 @@ common/views/components/messaging.vue:
|
||||
search-user: "Trouver un utilisateur"
|
||||
you: "Vous"
|
||||
no-history: "Pas d'historique"
|
||||
user: "Utilisateur·rice·s"
|
||||
group: "Groupe"
|
||||
start-with-user: "Initier une discussion avec un·e utilisateur·rice"
|
||||
select-group: "Sélectionner un groupe"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "Pas de conversations"
|
||||
not-talked-user: "Vous n'avez pas encore discuté avec cet·te utilisateur·rice"
|
||||
not-talked-group: "Il n y a aucune conversation dans ce groupe"
|
||||
no-history: "Aucun historique"
|
||||
resize-form: "Faites glisser pour redimensionner"
|
||||
new-message: "Nouveau message"
|
||||
only-one-file-attached: "Un seul fichier peut être joint au message"
|
||||
only-one-file-attached: "Vous ne pouvez joindre qu'un seul fichier au message"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Tapez ici votre message"
|
||||
send: "Envoyer"
|
||||
attach-from-local: "Joindre un fichier depuis votre ordinateur"
|
||||
attach-from-drive: "Joindre un fichier depuis votre Drive"
|
||||
only-one-file-attached: "Un seul fichier uniquement peut être joint au message"
|
||||
only-one-file-attached: "Vous ne pouvez joindre qu'un seul fichier au message"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "Lu"
|
||||
deleted: "Ce message a été supprimé"
|
||||
@ -455,6 +466,7 @@ common/views/components/nav.vue:
|
||||
repository: "Dépôt"
|
||||
develop: "Développeurs"
|
||||
feedback: "Suggestions"
|
||||
tos: "Conditions d'utilisation"
|
||||
common/views/components/note-menu.vue:
|
||||
mention: "Mention"
|
||||
detail: "Détails"
|
||||
@ -473,8 +485,12 @@ common/views/components/user-menu.vue:
|
||||
mention: "Mention"
|
||||
mute: "Silencier"
|
||||
unmute: "Enlever la sourdine"
|
||||
mute-confirm: "Rendre muet cet utilisateur ?"
|
||||
unmute-confirm: "Ne plus masquer cet utilisateur ?"
|
||||
block: "Bloquer"
|
||||
unblock: "Débloquer"
|
||||
block-confirm: "Bloquer cet utilisateur ?"
|
||||
unblock-confirm: "Débloquer cet utilisateur ?"
|
||||
push-to-list: "Ajouter à une liste"
|
||||
select-list: "Sélectionnez une liste"
|
||||
report-abuse: "Signaler un abus"
|
||||
@ -557,6 +573,7 @@ common/views/components/signup.vue:
|
||||
password-matched: "OK"
|
||||
password-not-matched: "Les mots de passe ne correspondent pas."
|
||||
recaptcha: "Vérifier"
|
||||
tos: "Conditions d'utilisation"
|
||||
create: "Créer un compte"
|
||||
some-error: "La création du compte a échoué. Veuillez réessayer."
|
||||
common/views/components/special-message.vue:
|
||||
@ -664,9 +681,26 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "Retirer de cette liste"
|
||||
delete-are-you-sure: "Voulez-vous vraiment supprimer la liste « $1 » ?"
|
||||
deleted: "Supprimé"
|
||||
add-user: "Ajouter un utilisateur"
|
||||
common/views/components/user-group-editor.vue:
|
||||
rename: "Renommer le groupe"
|
||||
delete: "Supprimer le groupe"
|
||||
deleted: "Supprimé"
|
||||
invite: "Inviter"
|
||||
invited: "Invitation envoyée avec succès"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Listes"
|
||||
create-list: "Créer une liste"
|
||||
list-name: "Nom de la liste"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "Groupe"
|
||||
create-group: "Créer un groupe"
|
||||
group-name: "Nom du groupe"
|
||||
owned-groups: "Mes groupes"
|
||||
joined-groups: "Membre dans les groupes"
|
||||
invites: "Inviter"
|
||||
accept-invite: "Participer"
|
||||
reject-invite: "Refuser"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Récupération"
|
||||
no-broadcasts: "Aucune annonce"
|
||||
@ -727,6 +761,10 @@ common/views/pages/follow.vue:
|
||||
request-pending: "Demande d’abonnement en attente"
|
||||
follow-processing: "Demande en attente"
|
||||
follow-request: "Demande d’abonnement"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "Demandes d’abonnement"
|
||||
accept: "Accepter"
|
||||
reject: "Refuser"
|
||||
desktop:
|
||||
banner-crop-title: "Découpez la partie qui apparaîtra comme bannière"
|
||||
banner: "Bannière"
|
||||
@ -998,6 +1036,7 @@ desktop/views/components/ui.header.vue:
|
||||
desktop/views/components/ui.header.account.vue:
|
||||
profile: "Votre profil"
|
||||
lists: "Listes"
|
||||
groups: "Groupes"
|
||||
follow-requests: "Demandes d’abonnement"
|
||||
admin: "Admin"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
@ -1008,12 +1047,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Rédiger une nouvelle publication"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Chercher"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "Demandes d’abonnement"
|
||||
accept: "Accepter"
|
||||
reject: "Refuser"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Listes de l'utilisateur"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Publications"
|
||||
following: "Abonné à"
|
||||
@ -1032,10 +1065,9 @@ admin/views/index.vue:
|
||||
instance: "Instance"
|
||||
emoji: "Émoji"
|
||||
moderators: "Modérateurs"
|
||||
users: "Utilisateurs"
|
||||
users: "Utilisateur·rice·s"
|
||||
federation: "Fédération"
|
||||
announcements: "Annonces"
|
||||
hashtags: "Hashtags"
|
||||
abuse: "Abus"
|
||||
queue: "File d’attente"
|
||||
logs: "Journaux"
|
||||
@ -1062,14 +1094,26 @@ admin/views/instance.vue:
|
||||
instance-name: "Nom de l’instance"
|
||||
instance-description: "Description de l’instance"
|
||||
host: "Hôte"
|
||||
icon-url: "URL de l'icône"
|
||||
logo-url: "URL do logo"
|
||||
banner-url: "URL de l’image de la bannière"
|
||||
error-image-url: "URL de l’image d’erreur"
|
||||
languages: "Langue de l’instance"
|
||||
languages-desc: "Vous pouvez en définir plus d’une, séparées par des espaces."
|
||||
tos-url: "URL des conditions d'utilisation"
|
||||
repository-url: "URL du dépôt"
|
||||
maintainer-config: "Informations de l’administrateur"
|
||||
maintainer-name: "Nom de l’administrateur"
|
||||
maintainer-email: "Contact administratif"
|
||||
advanced-config: "Autres réglages"
|
||||
drive-config: "Paramètres du lecteur"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-endpoint: "Point de terminaison"
|
||||
object-storage-port: "Port"
|
||||
object-storage-access-key: "Clé d'accès"
|
||||
object-storage-secret-key: "Clé secrète"
|
||||
object-storage-use-ssl: "Utiliser SSL"
|
||||
object-storage-s3-info-here: "ici"
|
||||
cache-remote-files: "Mettre en cache des fichiers distants"
|
||||
local-drive-capacity-mb: "Volume du lecteur par utilisateur"
|
||||
remote-drive-capacity-mb: "Volume du lecteur par utilisateur distant"
|
||||
@ -1077,8 +1121,9 @@ admin/views/instance.vue:
|
||||
recaptcha-config: "Paramètres de reCAPTCHA"
|
||||
recaptcha-info: "Si activé, un jeton reCAPTCHA est requis. Vous pouvez en obtenir un sur https://www.google.com/recaptcha/intro/"
|
||||
enable-recaptcha: "Activation de reCAPTCHA"
|
||||
recaptcha-site-key: "Clé reCAPTCHA du site"
|
||||
recaptcha-secret-key: "Clé secrète reCAPTCHA"
|
||||
recaptcha-secret-key: "Clé secrète"
|
||||
recaptcha-preview: "Prévisualisation"
|
||||
hidden-tags: "Tags cachés"
|
||||
twitter-integration-config: "Paramètres de connexion à Twitter"
|
||||
twitter-integration-info: "L'URL de callback est {url}."
|
||||
enable-twitter-integration: "Activer la connexion à Twitter"
|
||||
@ -1106,7 +1151,6 @@ admin/views/instance.vue:
|
||||
invite: "Inviter"
|
||||
save: "Sauvegarder"
|
||||
saved: "Enregistré"
|
||||
user-recommendation-config: "Utilisateurs"
|
||||
email-config: "Paramètres du serveur de messagerie"
|
||||
email-config-info: "Utilisé pour confirmer votre adresse de courrier électronique et la réinitialisation de votre mot de passe."
|
||||
enable-email: "Activation de la distribution du courrier"
|
||||
@ -1187,17 +1231,12 @@ admin/views/users.vue:
|
||||
unsuspend-confirm: "Souhaiteriez-vous ne plus suspendre ce compte ?"
|
||||
unsuspended: "La suspension de l’utilisateur a été levée avec succès"
|
||||
make-silence: "Mettre en sourdine"
|
||||
silence-confirm: "Mettre l'utilisateur sous silence ?"
|
||||
unmake-silence: "Enlever la sourdine"
|
||||
verify: "Vérification du compte"
|
||||
verify-confirm: "Souhaiteriez-vous rendre votre compte comme étant un compte vérifié ?"
|
||||
verified: "Le compte a été vérifié"
|
||||
unverify: "Enlever la vérification du compte"
|
||||
unverify-confirm: "Désirez-vous considérer ce compte comme étant non-vérifié ?"
|
||||
unverified: "Ce compte n'est plus vérifié"
|
||||
update-remote-user: "Mettre à jour les informations de l’utilisateur·rice distant·e"
|
||||
remote-user-updated: "Les informations de l’utilisateur·rice distant·e ont étés mis à jour"
|
||||
users:
|
||||
title: "Utilisateurs"
|
||||
title: "Utilisateur·rice·s"
|
||||
sort:
|
||||
title: "Trier par"
|
||||
createdAtAsc: "Date d’inscription (Ascendant)"
|
||||
@ -1210,7 +1249,6 @@ admin/views/users.vue:
|
||||
admin: "Admin"
|
||||
moderator: "Modérateur"
|
||||
adminOrModerator: "Administrateur/Modérateur"
|
||||
verified: "Compte vérifié"
|
||||
silenced: "Déjà mis en sourdine"
|
||||
suspended: "Suspendu"
|
||||
origin:
|
||||
@ -1309,6 +1347,7 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "Par heure"
|
||||
day: "Par jour"
|
||||
blocked-hosts: "En cours blocage"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "à propos"
|
||||
timeline: "Fil d’actualité"
|
||||
@ -1412,7 +1451,7 @@ mobile/views/components/media-video.vue:
|
||||
sensitive: "Le contenu est NSFW"
|
||||
click-to-show: "Cliquer pour afficher"
|
||||
common/views/components/follow-button.vue:
|
||||
following: "Abonné"
|
||||
following: "Abonné·e"
|
||||
follow: " Suivre"
|
||||
request-pending: "Demande en attente"
|
||||
follow-processing: "En cours d’abonnement"
|
||||
@ -1462,6 +1501,7 @@ mobile/views/components/ui.nav.vue:
|
||||
follow-requests: "Demandes d’abonnement"
|
||||
search: "Rechercher"
|
||||
user-lists: "Listes"
|
||||
user-groups: "Groupe"
|
||||
widgets: "Modules"
|
||||
game: "Jeux"
|
||||
admin: "Admin"
|
||||
@ -1469,16 +1509,17 @@ mobile/views/components/ui.nav.vue:
|
||||
mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Téléverser un fichier"
|
||||
url-upload: "Transférer un fichier depuis une URL"
|
||||
create-folder: "Créer un dossier"
|
||||
rename-folder: "Renommer le dossier"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Listes"
|
||||
move-folder: "Déplacer ce dossier"
|
||||
delete-folder: "Supprimer ce dossier"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "Votre compte est prêt ! 📦"
|
||||
mobile/views/pages/followers.vue:
|
||||
followers-of: "Abonnés de {name}"
|
||||
followers-of: "Abonné·e·s de {name}"
|
||||
mobile/views/pages/following.vue:
|
||||
following-of: "Abonnés de {name}"
|
||||
following-of: "Abonné·e·s de {name}"
|
||||
mobile/views/pages/home.vue:
|
||||
home: "Accueil"
|
||||
local: "Local"
|
||||
@ -1496,10 +1537,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
activity: "Activité"
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "Partager avec {name}"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "Demandes d’abonnement"
|
||||
accept: "Accepter"
|
||||
reject: "Refuser"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "Publication"
|
||||
prev: "Note précédente"
|
||||
@ -1579,6 +1616,7 @@ dev/views/apps.vue:
|
||||
create-app: "Créer une app"
|
||||
app-missing: "Aucune application"
|
||||
dev/views/new-app.vue:
|
||||
new-app: "Nouvelle application"
|
||||
create-app: "Création d’une application"
|
||||
app-name: "Nom de l’application"
|
||||
app-name-desc: "Le nom de votre application"
|
||||
@ -1589,7 +1627,33 @@ dev/views/new-app.vue:
|
||||
authority-desc: "Sont accessibles via l’API, uniquement les fonctionnalités demandées ici."
|
||||
authority-warning: "Vous pouvez le changer même après avoir créé l'application, mais si vous attribuez une nouvelle permission, toutes les clés utilisateur associées seront dès lors invalides."
|
||||
pages:
|
||||
page-created: "Page a été créée !"
|
||||
are-you-sure-delete: "Confirmez-vous la suppression de cette page ?"
|
||||
page-deleted: "La page a bien été supprimée."
|
||||
edit-this-page: "Éditer cette page"
|
||||
view-source: "Afficher la source"
|
||||
view-page: "Afficher la page"
|
||||
like: "Bien"
|
||||
inspector: "Inspecteur"
|
||||
content: "Bloc de page"
|
||||
variables: "Variables"
|
||||
more-details: "Description"
|
||||
title: "Titre"
|
||||
url: "URL de page"
|
||||
summary: "Résumé de page"
|
||||
align-center: "Centrée"
|
||||
font: "Police de caractères"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
choose-block: "Ajouter un bloc"
|
||||
select-type: "Choisir un type"
|
||||
enter-variable-name: "Veuillez choisir un nom de variable"
|
||||
the-variable-name-is-already-used: "Cette variable est déjà utilisée"
|
||||
content-blocks: "Contenu du cadre"
|
||||
input-blocks: "Entrée"
|
||||
special-blocks: "Spécial"
|
||||
post-from-post-form: "Publier ce contenu"
|
||||
posted-from-post-form: "Publié !"
|
||||
blocks:
|
||||
text: "Texte"
|
||||
textarea: "Zone de texte"
|
||||
@ -1602,6 +1666,7 @@ pages:
|
||||
post: "Champs de publication"
|
||||
_post:
|
||||
text: "Contenu"
|
||||
textInput: "Entrée textuelle"
|
||||
_textInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
@ -1610,6 +1675,7 @@ pages:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
numberInput: "Entrée numérique"
|
||||
_numberInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
@ -1623,11 +1689,15 @@ pages:
|
||||
_counter:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
inc: "Augmenter le chiffre"
|
||||
_button:
|
||||
text: "Titre"
|
||||
action: "L'opération lorsque le bouton sera pressé"
|
||||
_action:
|
||||
dialog: "Afficher une fenêtre de dialogue"
|
||||
_dialog:
|
||||
content: "Contenu"
|
||||
resetRandom: "Réinitialiser le nombre aléatoire"
|
||||
script:
|
||||
categories:
|
||||
flow: "Contrôle"
|
||||
@ -1701,29 +1771,57 @@ pages:
|
||||
_gtEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_if:
|
||||
arg1: "Si"
|
||||
arg2: "donc"
|
||||
arg3: "sinon"
|
||||
not: "négation"
|
||||
_not:
|
||||
arg1: "négation"
|
||||
random: "Aléatoire"
|
||||
_randomPick:
|
||||
arg1: "Listes"
|
||||
_dailyRandom:
|
||||
arg1: "Probabilité"
|
||||
_dailyRannum:
|
||||
arg1: "Minimum"
|
||||
arg2: "Maximum"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listes"
|
||||
_seedRandom:
|
||||
arg2: "Probabilité"
|
||||
_seedRannum:
|
||||
arg2: "Min"
|
||||
arg3: "Max"
|
||||
_seedRandomPick:
|
||||
arg2: "Listes"
|
||||
_DRPWPM:
|
||||
arg1: "Liste de texte"
|
||||
pick: "Sélectionner dans la liste"
|
||||
_pick:
|
||||
arg1: "Listes"
|
||||
arg2: "Position"
|
||||
number: "Numérique"
|
||||
stringToNumber: "Chaîne en chiffres"
|
||||
_stringToNumber:
|
||||
arg1: "Texte"
|
||||
numberToString: "Chiffres en chaîne"
|
||||
_numberToString:
|
||||
arg1: "Numérique"
|
||||
_splitStrByLine:
|
||||
arg1: "Texte"
|
||||
ref: "Variables"
|
||||
fn: "Fonction"
|
||||
_fn:
|
||||
slots: "Emplacement"
|
||||
arg1: "Sortie"
|
||||
for: "Répéter"
|
||||
thereIsEmptySlot: "Slot {slot} est vide !"
|
||||
types:
|
||||
string: "Texte"
|
||||
number: "Numérique"
|
||||
array: "Listes"
|
||||
stringArray: "Liste de texte"
|
||||
emptySlot: "Slot vide"
|
||||
enviromentVariables: "Variables d'environnement"
|
||||
pageVariables: "Élément de page"
|
||||
|
@ -15,6 +15,7 @@ const merge = (...args) => args.reduce((a, c) => ({
|
||||
|
||||
const languages = [
|
||||
'cs-CZ',
|
||||
'da-DK',
|
||||
'de-DE',
|
||||
'en-US',
|
||||
'es-ES',
|
||||
|
@ -101,6 +101,34 @@ common:
|
||||
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
|
||||
explore: "ユーザーを探索する"
|
||||
|
||||
post-form:
|
||||
attach-location-information: "位置情報を添付する"
|
||||
hide-contents: "内容を隠す"
|
||||
reply-placeholder: "この投稿への返信..."
|
||||
quote-placeholder: "この投稿を引用..."
|
||||
option-quote-placeholder: "この投稿を引用... (オプション)"
|
||||
quote-attached: "引用付き"
|
||||
quote-question: "引用として添付しますか?"
|
||||
submit: "投稿"
|
||||
reply: "返信"
|
||||
renote: "Renote"
|
||||
posting: "投稿中"
|
||||
attach-media-from-local: "PCからメディアを添付"
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
insert-a-kao: "v('ω')v"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
recent-tags: "最近"
|
||||
local-only-message: "この投稿はローカルにのみ公開されます"
|
||||
click-to-tagging: "クリックでタグ付け"
|
||||
visibility: "公開範囲"
|
||||
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
||||
error: "エラー"
|
||||
enter-username: "ユーザー名を入力してください"
|
||||
add-visible-user: "ユーザーを追加"
|
||||
cw-placeholder: "内容への注釈 (オプション)"
|
||||
username-prompt: "ユーザー名を入力してください"
|
||||
|
||||
weekday-short:
|
||||
sunday: "日"
|
||||
monday: "月"
|
||||
@ -265,6 +293,7 @@ common:
|
||||
my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
|
||||
hide-password: "パスワードを隠す"
|
||||
show-password: "パスワードを表示する"
|
||||
enter-username: "ユーザー名を入力してください"
|
||||
|
||||
do-not-use-in-production: "これは開発ビルドです。本番環境で使用しないでください。"
|
||||
user-suspended: "このユーザーは凍結されています。"
|
||||
@ -480,20 +509,25 @@ common/views/components/messaging.vue:
|
||||
search-user: "ユーザーを探す"
|
||||
you: "あなた"
|
||||
no-history: "履歴はありません"
|
||||
user: "ユーザー"
|
||||
group: "グループ"
|
||||
start-with-user: "ユーザーとトークを開始"
|
||||
start-with-group: "グループとトークを開始"
|
||||
select-group: "グループを選択してください"
|
||||
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "このユーザーと話したことはありません"
|
||||
not-talked-user: "このユーザーとの会話はありません"
|
||||
not-talked-group: "このグループでの会話はありません"
|
||||
no-history: "これより過去の履歴はありません"
|
||||
resize-form: "ドラッグしてフォームの広さを調整"
|
||||
new-message: "新しいメッセージがあります"
|
||||
only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
|
||||
only-one-file-attached: "メッセージに添付できるファイルはひとつです"
|
||||
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "ここにメッセージを入力"
|
||||
send: "送信"
|
||||
attach-from-local: "PCからファイルを添付する"
|
||||
attach-from-drive: "ドライブからファイルを添付する"
|
||||
only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
|
||||
only-one-file-attached: "メッセージに添付できるファイルはひとつです"
|
||||
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "既読"
|
||||
@ -508,6 +542,7 @@ common/views/components/nav.vue:
|
||||
repository: "リポジトリ"
|
||||
develop: "開発者"
|
||||
feedback: "フィードバック"
|
||||
tos: "利用規約"
|
||||
|
||||
common/views/components/note-menu.vue:
|
||||
mention: "メンション"
|
||||
@ -584,6 +619,7 @@ common/views/components/poll-editor.vue:
|
||||
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "リアクションを選択"
|
||||
input-reaction-placeholder: "または絵文字を入力"
|
||||
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "カスタム絵文字"
|
||||
@ -628,6 +664,8 @@ common/views/components/signup.vue:
|
||||
password-matched: "確認されました"
|
||||
password-not-matched: "一致していません"
|
||||
recaptcha: "認証"
|
||||
agree-to: "{0}に同意します。"
|
||||
tos: "利用規約"
|
||||
create: "アカウント作成"
|
||||
some-error: "何らかの原因によりアカウントの作成に失敗しました。再度お試しください。"
|
||||
|
||||
@ -747,11 +785,36 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "このリストから削除"
|
||||
delete-are-you-sure: "リスト「$1」を削除しますか?"
|
||||
deleted: "削除しました"
|
||||
add-user: "ユーザーを追加"
|
||||
|
||||
common/views/components/user-group-editor.vue:
|
||||
users: "メンバー"
|
||||
rename: "グループ名を変更"
|
||||
delete: "グループを削除"
|
||||
transfer: "グループを譲渡"
|
||||
transfer-are-you-sure: "グループ「$1」を「@$2」さんに譲渡しますか?"
|
||||
transferred: "グループを譲渡しました"
|
||||
remove-user: "このグループから削除"
|
||||
delete-are-you-sure: "グループ「$1」を削除しますか?"
|
||||
deleted: "削除しました"
|
||||
invite: "招待"
|
||||
invited: "招待を送信しました"
|
||||
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "リスト"
|
||||
create-list: "リストを作成"
|
||||
list-name: "リスト名"
|
||||
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "グループ"
|
||||
create-group: "グループを作成"
|
||||
group-name: "グループ名"
|
||||
owned-groups: "自分のグループ"
|
||||
joined-groups: "参加しているグループ"
|
||||
invites: "招待"
|
||||
accept-invite: "参加"
|
||||
reject-invite: "拒否"
|
||||
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
@ -824,6 +887,11 @@ common/views/pages/follow.vue:
|
||||
follow-processing: "フォロー処理中"
|
||||
follow-request: "フォロー申請"
|
||||
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "フォロー申請"
|
||||
accept: "承認"
|
||||
reject: "拒否"
|
||||
|
||||
desktop:
|
||||
banner-crop-title: "バナーとして表示する部分を選択"
|
||||
banner: "バナー"
|
||||
@ -977,34 +1045,12 @@ desktop/views/components/notifications.vue:
|
||||
empty: "ありません!"
|
||||
|
||||
desktop/views/components/post-form.vue:
|
||||
add-visible-user: "+ユーザーを追加"
|
||||
attach-location-information: "位置情報を添付する"
|
||||
hide-contents: "内容を隠す"
|
||||
reply-placeholder: "この投稿への返信..."
|
||||
quote-placeholder: "この投稿を引用..."
|
||||
submit: "投稿"
|
||||
reply: "返信"
|
||||
renote: "Renote"
|
||||
posted: "投稿しました!"
|
||||
replied: "返信しました!"
|
||||
reposted: "Renoteしました!"
|
||||
note-failed: "投稿に失敗しました"
|
||||
reply-failed: "返信に失敗しました"
|
||||
renote-failed: "Renoteに失敗しました"
|
||||
posting: "投稿中"
|
||||
attach-media-from-local: "PCからメディアを添付"
|
||||
attach-media-from-drive: "ドライブからメディアを添付"
|
||||
insert-a-kao: "v('ω')v"
|
||||
create-poll: "アンケートを作成"
|
||||
text-remain: "残り{}文字"
|
||||
recent-tags: "最近"
|
||||
local-only-message: "この投稿はローカルにのみ公開されます"
|
||||
click-to-tagging: "クリックでタグ付け"
|
||||
visibility: "公開範囲"
|
||||
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
||||
error: "エラー"
|
||||
enter-username: "ユーザー名を入力してください"
|
||||
annotations: "内容への注釈 (オプション)"
|
||||
|
||||
desktop/views/components/post-form-window.vue:
|
||||
note: "新規投稿"
|
||||
@ -1136,6 +1182,7 @@ desktop/views/components/ui.header.vue:
|
||||
desktop/views/components/ui.header.account.vue:
|
||||
profile: "プロフィール"
|
||||
lists: "リスト"
|
||||
groups: "グループ"
|
||||
follow-requests: "フォロー申請"
|
||||
admin: "管理"
|
||||
|
||||
@ -1151,14 +1198,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "検索"
|
||||
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "フォロー申請"
|
||||
accept: "承認"
|
||||
reject: "拒否"
|
||||
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "リスト"
|
||||
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "投稿"
|
||||
following: "フォロー"
|
||||
@ -1184,7 +1223,6 @@ admin/views/index.vue:
|
||||
users: "ユーザー"
|
||||
federation: "連合"
|
||||
announcements: "お知らせ"
|
||||
hashtags: "ハッシュタグ"
|
||||
abuse: "スパム報告"
|
||||
queue: "ジョブキュー"
|
||||
logs: "ログ"
|
||||
@ -1202,6 +1240,33 @@ admin/views/dashboard.vue:
|
||||
admin/views/queue.vue:
|
||||
title: "キュー"
|
||||
remove-all-jobs: "すべてのジョブをクリア"
|
||||
jobs: "ジョブ"
|
||||
queue: "キュー"
|
||||
domains:
|
||||
deliver: "配送"
|
||||
inbox: "受信"
|
||||
db: "データベース"
|
||||
objectStorage: "オブジェクトストレージ"
|
||||
state: "状態"
|
||||
states:
|
||||
active: "処理中"
|
||||
delayed: "予約済み"
|
||||
waiting: "順番待ち"
|
||||
result-is-truncated: "結果は省略されています"
|
||||
other-queues: "その他のキュー"
|
||||
|
||||
admin/views/logs.vue:
|
||||
logs: "ログ"
|
||||
domain: "ドメイン"
|
||||
level: "レベル"
|
||||
levels:
|
||||
all: "全て"
|
||||
info: "情報"
|
||||
success: "成功"
|
||||
warning: "警告"
|
||||
error: "エラー"
|
||||
debug: "デバッグ"
|
||||
delete-all: "全て削除"
|
||||
|
||||
admin/views/abuse.vue:
|
||||
title: "スパム報告"
|
||||
@ -1215,14 +1280,34 @@ admin/views/instance.vue:
|
||||
instance-name: "インスタンス名"
|
||||
instance-description: "インスタンスの紹介"
|
||||
host: "ホスト"
|
||||
icon-url: "アイコンURL"
|
||||
logo-url: "ロゴURL"
|
||||
banner-url: "バナー画像URL"
|
||||
error-image-url: "エラー画像URL"
|
||||
languages: "インスタンスの対象言語"
|
||||
languages-desc: "スペースで区切って複数設定できます。"
|
||||
tos-url: "利用規約URL"
|
||||
repository-url: "リポジトリURL"
|
||||
feedback-url: "フィードバックURL"
|
||||
maintainer-config: "管理者情報"
|
||||
maintainer-name: "管理者名"
|
||||
maintainer-email: "管理者の連絡先"
|
||||
advanced-config: "その他の設定"
|
||||
note-and-tl: "投稿とタイムライン"
|
||||
drive-config: "ドライブの設定"
|
||||
use-object-storage: "オブジェクトストレージを使用する"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-bucket: "バケット名"
|
||||
object-storage-prefix: "プレフィックス"
|
||||
object-storage-endpoint: "エンドポイント"
|
||||
object-storage-region: "リージョン"
|
||||
object-storage-port: "ポート"
|
||||
object-storage-access-key: "アクセスキー"
|
||||
object-storage-secret-key: "シークレットキー"
|
||||
object-storage-use-ssl: "SSLを使用"
|
||||
object-storage-s3-info: "Amazon S3をオブジェクトストレージとして使用する場合の「エンドポイント」と「リージョン」の設定については{0}をご確認ください。"
|
||||
object-storage-s3-info-here: "こちら"
|
||||
object-storage-gcs-info: "Google Cloud Storageをオブジェクトストレージとして使用する場合、「エンドポイント」は storage.googleapis.com に設定し、「リージョン」は空欄にします。"
|
||||
cache-remote-files: "リモートのファイルをキャッシュする"
|
||||
cache-remote-files-desc: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。そのためサーバーのストレージを節約できますが、プライバシー設定で直リンクを無効にしているユーザーにはファイルが見えなくなったり、サムネイルが生成されないので通信量が増加します。通常はこの設定をオンにしておくことをおすすめします。"
|
||||
local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量"
|
||||
@ -1230,9 +1315,14 @@ admin/views/instance.vue:
|
||||
mb: "メガバイト単位"
|
||||
recaptcha-config: "reCAPTCHAの設定"
|
||||
recaptcha-info: "reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。"
|
||||
recaptcha-info2: "v3は非対応です。v2を使用してください。"
|
||||
enable-recaptcha: "reCAPTCHAを有効にする"
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
recaptcha-site-key: "サイトキー"
|
||||
recaptcha-secret-key: "シークレットキー"
|
||||
recaptcha-preview: "プレビュー"
|
||||
hidden-tags: "非表示ハッシュタグ"
|
||||
hidden-tags-info: "集計から除外するハッシュタグを改行で区切って記述します。"
|
||||
external-service-integration-config: "外部サービス連携"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
@ -1264,6 +1354,7 @@ admin/views/instance.vue:
|
||||
save: "保存"
|
||||
saved: "保存しました"
|
||||
pinned-users: "ピン留めユーザー"
|
||||
pinned-users-info: "ピン留めしたいユーザーを改行で区切って記述します。"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
@ -1331,6 +1422,9 @@ admin/views/drive.vue:
|
||||
unmark-as-sensitive: "閲覧注意を解除"
|
||||
marked-as-sensitive: "閲覧注意に設定しました"
|
||||
unmarked-as-sensitive: "閲覧注意を解除しました"
|
||||
clean-remote-files: "リモートファイルのキャッシュを削除"
|
||||
clean-remote-files-are-you-sure: "すべてのリモートファイルのキャッシュを削除してもよろしいですか?"
|
||||
clean-up: "クリーンアップ"
|
||||
|
||||
admin/views/users.vue:
|
||||
operation: "操作"
|
||||
@ -1352,6 +1446,8 @@ admin/views/users.vue:
|
||||
unsilence-confirm: "サイレンスを解除しますか?"
|
||||
update-remote-user: "リモートユーザー情報の更新"
|
||||
remote-user-updated: "リモートユーザー情報を更新しました"
|
||||
delete-all-files: "すべてのファイルを削除"
|
||||
delete-all-files-confirm: "すべてのファイルを削除しますか?"
|
||||
users:
|
||||
title: "ユーザー"
|
||||
sort:
|
||||
@ -1432,6 +1528,7 @@ admin/views/federation.vue:
|
||||
latest-request-received-at: "直近のリクエスト受信"
|
||||
remove-all-following: "フォローを全解除"
|
||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||
delete-all-files: "ファイルをすべて削除"
|
||||
block: "ブロック"
|
||||
marked-as-closed: "閉鎖されているとマーク"
|
||||
lookup: "照会"
|
||||
@ -1478,6 +1575,9 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "1時間ごと"
|
||||
day: "1日ごと"
|
||||
blocked-hosts: "ブロック"
|
||||
blocked-hosts-info: "ブロックしたいホストを改行で区切って記述します。"
|
||||
save: "保存"
|
||||
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
@ -1640,18 +1740,6 @@ mobile/views/components/note-sub.vue:
|
||||
mobile/views/components/notifications.vue:
|
||||
empty: "ありません!"
|
||||
|
||||
mobile/views/components/post-form.vue:
|
||||
add-visible-user: "ユーザーを追加"
|
||||
submit: "投稿"
|
||||
reply: "返信"
|
||||
renote: "Renote"
|
||||
quote-placeholder: "この投稿を引用... (オプション)"
|
||||
reply-placeholder: "この投稿への返信..."
|
||||
cw-placeholder: "内容への注釈 (オプション)"
|
||||
geolocation-alert: "お使いの端末は位置情報に対応していません"
|
||||
error: "エラー"
|
||||
username-prompt: "ユーザー名を入力してください"
|
||||
|
||||
mobile/views/components/sub-note-content.vue:
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
@ -1668,6 +1756,7 @@ mobile/views/components/ui.nav.vue:
|
||||
follow-requests: "フォロー申請"
|
||||
search: "検索"
|
||||
user-lists: "リスト"
|
||||
user-groups: "グループ"
|
||||
widgets: "ウィジェット"
|
||||
game: "ゲーム"
|
||||
admin: "管理"
|
||||
@ -1682,9 +1771,6 @@ mobile/views/pages/drive.vue:
|
||||
move-folder: "このフォルダを移動"
|
||||
delete-folder: "このフォルダを削除"
|
||||
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "リスト"
|
||||
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "📦 始めましょう"
|
||||
|
||||
@ -1717,11 +1803,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "{name}で共有"
|
||||
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "フォロー申請"
|
||||
accept: "承認"
|
||||
reject: "拒否"
|
||||
|
||||
mobile/views/pages/note.vue:
|
||||
title: "投稿"
|
||||
prev: "前の投稿"
|
||||
@ -1842,6 +1923,10 @@ pages:
|
||||
edit-this-page: "このページを編集"
|
||||
view-source: "ソースを表示"
|
||||
view-page: "ページを見る"
|
||||
like: "いいね"
|
||||
unlike: "いいね解除"
|
||||
liked-pages: "いいねしたページ"
|
||||
my-pages: "自分のページ"
|
||||
inspector: "インスペクター"
|
||||
content: "ページブロック"
|
||||
variables: "変数"
|
||||
|
@ -121,7 +121,7 @@ common:
|
||||
update-available-title: "更新があんで"
|
||||
update-available: "Misskeyの新しいバージョンがあんで({newer}。現在{current}をつこてるわ)。ページを再度読み込みしたると更新が適用されるわ。"
|
||||
my-token-regenerated: "あんさんのトークンが更新されたらしいわ。すまんがとりあえずサインアウトすんで。"
|
||||
verified-user: "アメちゃん付きアカウント"
|
||||
enter-username: "ユーザー名を入力してや"
|
||||
do-not-use-in-production: "開発ビルドや。本番環境で使わんといて!知らんで!"
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "ちゃんとした情報見せてや!"
|
||||
@ -181,7 +181,6 @@ auth/views/index.vue:
|
||||
error: "セッションが存在してへん。"
|
||||
sign-in: "サインインしてや"
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "アメちゃん付きアカウント"
|
||||
federated: "連合"
|
||||
common/views/components/games/reversi/reversi.vue:
|
||||
matching:
|
||||
@ -296,18 +295,15 @@ common/views/components/messaging.vue:
|
||||
search-user: "ユーザーを探す"
|
||||
you: "あんさん"
|
||||
no-history: "履歴はあらへんで"
|
||||
user: "ユーザー"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "このユーザーと話したことはあらへんで"
|
||||
no-history: "これより過去の履歴はあらへんで"
|
||||
resize-form: "ドラッグしてフォームの広さを調整"
|
||||
new-message: "新しいメッセージがあるで"
|
||||
only-one-file-attached: "メッセージに添付できんのはひとつのファイルのみやで"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "ここにメッセージ書いてや"
|
||||
send: "送信"
|
||||
attach-from-local: "PCからファイルを添付する"
|
||||
attach-from-drive: "ドライブからファイルを添付する"
|
||||
only-one-file-attached: "メッセージに添付できんのはひとつのファイルのみやで"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "既読"
|
||||
deleted: "このメッセージは削除されたわ"
|
||||
@ -478,8 +474,14 @@ common/views/components/profile-editor.vue:
|
||||
enter-password: "パスワードを入れてや"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
add-user: "ユーザー増やす"
|
||||
common/views/components/user-group-editor.vue:
|
||||
invite: "招待"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "リスト"
|
||||
list-name: "リスト名"
|
||||
common/views/components/user-groups.vue:
|
||||
invites: "招待"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "見てみるわ…"
|
||||
no-broadcasts: "お知らせはあらへんで"
|
||||
@ -538,6 +540,8 @@ common/views/pages/follow.vue:
|
||||
request-pending: "フォローの許し待っとる"
|
||||
follow-processing: "今フォロー処理やっとる‥"
|
||||
follow-request: "フォロー許してくれや!言うてみる"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "フォロー許してくれや!言うてみる"
|
||||
desktop:
|
||||
banner-crop-title: "どこバナーとして出す?"
|
||||
banner: "バナー"
|
||||
@ -811,12 +815,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "新規投稿"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "検索"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "フォロー許してくれや!言うてみる"
|
||||
accept: "許す"
|
||||
reject: "許さん"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "リスト"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "投稿"
|
||||
following: "フォロー"
|
||||
@ -838,7 +836,6 @@ admin/views/index.vue:
|
||||
users: "ユーザー"
|
||||
federation: "連合"
|
||||
announcements: "知っといてや"
|
||||
hashtags: "ハッシュタグ"
|
||||
back-to-misskey: "Misskeyに戻る"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "ダッシュボード"
|
||||
@ -863,6 +860,7 @@ admin/views/instance.vue:
|
||||
maintainer-name: "管理者名"
|
||||
maintainer-email: "管理者の連絡先"
|
||||
drive-config: "ドライブの設定"
|
||||
object-storage-endpoint: "エンドポイント"
|
||||
cache-remote-files: "リモートのファイルをキャッシュする"
|
||||
cache-remote-files-desc: "この設定を無効にすると、リモートファイルをこっちで保管せずに直接リンク張るようになるで。サーバーのストレージは軽くやろうけど、プライバシー設定で直リンクを向こうにしとるユーザーはファイルが見れへんし、サムネイルが無いから通信量が増えたりするから、普通はオンにしといてな。"
|
||||
local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量"
|
||||
@ -871,8 +869,7 @@ admin/views/instance.vue:
|
||||
recaptcha-config: "reCAPTCHAの設定"
|
||||
recaptcha-info: "reCAPTCHAを有効にするにはreCAPTCHAトークンが要るで。https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してな。"
|
||||
enable-recaptcha: "reCAPTCHAを有効にする"
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
recaptcha-preview: "試してみる"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定してや。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
@ -899,7 +896,6 @@ admin/views/instance.vue:
|
||||
invite: "来てや"
|
||||
save: "保存"
|
||||
saved: "保存したで!"
|
||||
user-recommendation-config: "このユーザーええで"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使うで。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
@ -956,7 +952,6 @@ admin/views/users.vue:
|
||||
state:
|
||||
all: "すべて"
|
||||
moderator: "モデレーター"
|
||||
verified: "アメちゃん付きアカウント"
|
||||
origin:
|
||||
local: "ローカル"
|
||||
admin/views/emoji.vue:
|
||||
@ -995,6 +990,7 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "1時間ごと"
|
||||
day: "1日ごと"
|
||||
blocked-hosts: "ブロック"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "もうちょい……"
|
||||
timeline: "タイムライン"
|
||||
@ -1151,8 +1147,6 @@ mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "ファイル上げる"
|
||||
create-folder: "フォルダー作る"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "リスト"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "📦 始めようや"
|
||||
mobile/views/pages/followers.vue:
|
||||
@ -1175,10 +1169,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
activity: "やっとること"
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "{name}で共有"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "フォロー許してくれや!"
|
||||
accept: "許す"
|
||||
reject: "許さん"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "投稿"
|
||||
prev: "前のやつ"
|
||||
@ -1263,6 +1253,7 @@ dev/views/new-app.vue:
|
||||
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
|
||||
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
|
||||
pages:
|
||||
like: "ええやん"
|
||||
blocks:
|
||||
image: "画像"
|
||||
post: "投稿フォーム"
|
||||
|
@ -20,7 +20,7 @@ common:
|
||||
outro: "이외에도 Misskey에만 있는 기능이 아직도 더 있으니 부디 여러분 자신의 눈으로 확인해보시기 바랍니다. Misskey는 분산형 SNS라서 이 인스턴스가 마음에 들지 않으신다면 다른 인스턴스를 시도해보실 수도 있습니다. 그럼, GLHF!"
|
||||
adblock:
|
||||
detected: "광고 차단기를 해제하십시오"
|
||||
warning: "<strong>Misskey는 광고를 게재하지 않습니다</strong>. 그러나 광고 차단 기능을 사용할 경우 일부 기능을 사용할 수 없게 될 가능성이나 결함이 발생하는 경우가 있습니다."
|
||||
warning: "<strong>Misskey는 광고를 게재하지 않습니다</strong>. 하지만 광고 차단 기능을 사용할 경우 일부 기능을 사용할 수 없거나 문제가 발생할 수 있습니다."
|
||||
application-authorization: "앱 연계"
|
||||
close: "닫기"
|
||||
do-not-copy-paste: "여기에 코드를 입력하거나 붙여넣지 마십시오. 계정이 무단으로 사용될 수 있습니다."
|
||||
@ -42,7 +42,7 @@ common:
|
||||
gotit: "Got it!"
|
||||
notification:
|
||||
file-uploaded: "파일이 업로드되었습니다"
|
||||
message-from: "{}님으로부터 메시지:"
|
||||
message-from: "{}님의 메시지:"
|
||||
reversi-invited: "게임 초대가 있습니다"
|
||||
reversi-invited-by: "{}님으로부터"
|
||||
notified-by: "{}님으로부터"
|
||||
@ -51,7 +51,7 @@ common:
|
||||
time:
|
||||
unknown: "알 수 없는 시간"
|
||||
future: "미래"
|
||||
just_now: "방금"
|
||||
just_now: "방금 전"
|
||||
seconds_ago: "{}초 전"
|
||||
minutes_ago: "{}분 전"
|
||||
hours_ago: "{}시간 전"
|
||||
@ -67,19 +67,19 @@ common:
|
||||
home: "홈"
|
||||
deck: "덱"
|
||||
timeline: "타임라인"
|
||||
explore: "발견"
|
||||
explore: "발견하기"
|
||||
following: "팔로우 중"
|
||||
followers: "팔로워"
|
||||
favorites: "즐겨찾기"
|
||||
permissions:
|
||||
"read:account": "계정 정보 보기"
|
||||
"write:account": "계정 정보 변경"
|
||||
"read:blocks": "차단 보기"
|
||||
"write:blocks": "차단 수정"
|
||||
"read:drive": "드라이브 보기"
|
||||
"write:drive": "드라이브 수정"
|
||||
"read:favorites": "즐겨찾기 보기"
|
||||
"write:favorites": "즐겨찾기 수정"
|
||||
"read:account": "계정의 정보를 볼 수 있습니다."
|
||||
"write:account": "계정의 정보를 변경할 수 있습니다."
|
||||
"read:blocks": "차단 목록을 볼 수 있습니다."
|
||||
"write:blocks": "차단 목록을 조작할 수 있습니다."
|
||||
"read:drive": "드라이브의 파일들을 볼 수 있습니다."
|
||||
"write:drive": "드라이브의 파일들을 조작할 수 있습니다."
|
||||
"read:favorites": "즐겨찾기 목록을 볼 수 있습니다."
|
||||
"write:favorites": "즐겨찾기 목록을 조작할 수 있습니다."
|
||||
"read:following": "팔로우 정보 보기"
|
||||
"write:following": "팔로잉, 팔로우 수정"
|
||||
"read:messaging": "대화 보기"
|
||||
@ -251,9 +251,9 @@ common:
|
||||
update-available-title: "업데이트가 있습니다"
|
||||
update-available: "Misskey의 새로운 버전이 있습니다 ({newer}. 현재 {current}을 사용 중). 페이지를 다시 로드하면 업데이트가 적용됩니다."
|
||||
my-token-regenerated: "당신의 토큰이 업데이트되었으므로 로그아웃합니다."
|
||||
verified-user: "공식 계정"
|
||||
hide-password: "비밀번호 숨기기"
|
||||
show-password: "비밀번호 표시"
|
||||
enter-username: "사용자명을 입력하여 주십시오"
|
||||
do-not-use-in-production: "이것은 개발 빌드입니다. 프로덕션 환경에서 사용하지 마십시오."
|
||||
user-suspended: "이 사용자는 정지된 상태입니다."
|
||||
is-remote-user: "이 사용자 정보는 정확하지 않을 수 있습니다."
|
||||
@ -294,7 +294,7 @@ common:
|
||||
notifications: "알림"
|
||||
users: "추천 사용자"
|
||||
polls: "투표"
|
||||
post-form: "게시 양식"
|
||||
post-form: "글 입력란"
|
||||
server: "서버 정보"
|
||||
nav: "내비게이션"
|
||||
tips: "팁"
|
||||
@ -319,7 +319,7 @@ auth/views/index.vue:
|
||||
error: "세션이 존재하지 않습니다."
|
||||
sign-in: "로그인 해주시기 바랍니다"
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "공식 계정"
|
||||
pinned-users: "고정된 사용자"
|
||||
popular-users: "인기 사용자"
|
||||
recently-updated-users: "최근 게시한 사용자"
|
||||
recently-registered-users: "신규 사용자"
|
||||
@ -449,18 +449,23 @@ common/views/components/messaging.vue:
|
||||
search-user: "사용자 찾기"
|
||||
you: "당신"
|
||||
no-history: "기록이 없습니다"
|
||||
user: "사용자"
|
||||
group: "그룹"
|
||||
start-with-user: "사용자와 대화 시작"
|
||||
start-with-group: "그룹과 대화 시작"
|
||||
select-group: "그룹을 선택하여 주십시오"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "이 사용자와 대화한 적이 없습니다"
|
||||
not-talked-user: "이 사용자와의 대화가 없습니다"
|
||||
not-talked-group: "이 그룹과의 대화가 없습니다"
|
||||
no-history: "이것보다 과거의 기록이 없습니다"
|
||||
resize-form: "드래그하여 폼의 크기 조절"
|
||||
new-message: "새 메시지가 있습니다"
|
||||
only-one-file-attached: "메시지에는 하나의 파일만 첨부할 수 있습니다"
|
||||
only-one-file-attached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "여기에 메시지를 입력하세요"
|
||||
send: "전송"
|
||||
attach-from-local: "PC에서 파일 첨부"
|
||||
attach-from-drive: "드라이브에서 파일 첨부"
|
||||
only-one-file-attached: "메시지에는 하나의 파일만 첨부할 수 있습니다"
|
||||
only-one-file-attached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "읽음"
|
||||
deleted: "이 메시지는 삭제되었습니다"
|
||||
@ -473,6 +478,7 @@ common/views/components/nav.vue:
|
||||
repository: "저장소"
|
||||
develop: "개발자"
|
||||
feedback: "피드백"
|
||||
tos: "이용 약관"
|
||||
common/views/components/note-menu.vue:
|
||||
mention: "멘션"
|
||||
detail: "상세"
|
||||
@ -544,6 +550,7 @@ common/views/components/poll-editor.vue:
|
||||
day: "일"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "리액션 선택"
|
||||
input-reaction-placeholder: "또는 이모지 입력"
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "커스텀 이모지"
|
||||
people: "사람들"
|
||||
@ -585,6 +592,8 @@ common/views/components/signup.vue:
|
||||
password-matched: "확인되었습니다"
|
||||
password-not-matched: "일치하지 않습니다"
|
||||
recaptcha: "자동 가입 방지"
|
||||
agree-to: "{0}에 동의합니다."
|
||||
tos: "이용 약관"
|
||||
create: "계정 만들기"
|
||||
some-error: "알 수 없는 이유로 계정 만들기에 실패했습니다. 다시 한번 시도해 주세요."
|
||||
common/views/components/special-message.vue:
|
||||
@ -692,9 +701,32 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "이 리스트에서 제거"
|
||||
delete-are-you-sure: "리스트 \"$1\"을 삭제하시겠습니까?"
|
||||
deleted: "삭제하였습니다"
|
||||
add-user: "사용자 추가"
|
||||
common/views/components/user-group-editor.vue:
|
||||
users: "멤버"
|
||||
rename: "그룹명을 변경"
|
||||
delete: "그룹을 삭제"
|
||||
transfer: "그룹을 양도"
|
||||
transfer-are-you-sure: "그룹 「$1」을 「@$2」 님에게 양도하시겠습니까?"
|
||||
transferred: "그룹을 양도하였습니다"
|
||||
remove-user: "이 그룹에서 삭제"
|
||||
delete-are-you-sure: "그룹 「$1」을 삭제하시겠습니까?"
|
||||
deleted: "삭제하였습니다"
|
||||
invite: "초대"
|
||||
invited: "초대를 보냈습니다"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "리스트"
|
||||
create-list: "리스트 만들기"
|
||||
list-name: "리스트 이름"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "그룹"
|
||||
create-group: "그룹 만들기"
|
||||
group-name: "그룹명"
|
||||
owned-groups: "자신의 그룹"
|
||||
joined-groups: "참여중인 그룹"
|
||||
invites: "초대"
|
||||
accept-invite: "참여"
|
||||
reject-invite: "거부"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "확인중"
|
||||
no-broadcasts: "공지사항이 없습니다"
|
||||
@ -756,6 +788,10 @@ common/views/pages/follow.vue:
|
||||
request-pending: "팔로우 허가 대기중"
|
||||
follow-processing: "팔로우 처리중"
|
||||
follow-request: "팔로우 요청"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "팔로우 요청"
|
||||
accept: "승인"
|
||||
reject: "거부"
|
||||
desktop:
|
||||
banner-crop-title: "배너로 표시할 부분을 선택"
|
||||
banner: "배너"
|
||||
@ -1027,6 +1063,7 @@ desktop/views/components/ui.header.vue:
|
||||
desktop/views/components/ui.header.account.vue:
|
||||
profile: "프로필"
|
||||
lists: "리스트"
|
||||
groups: "그룹"
|
||||
follow-requests: "팔로우 요청"
|
||||
admin: "관리"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
@ -1037,12 +1074,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "새 글"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "검색"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "팔로우 요청"
|
||||
accept: "승인"
|
||||
reject: "거부"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "리스트"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "글"
|
||||
following: "팔로잉"
|
||||
@ -1064,7 +1095,6 @@ admin/views/index.vue:
|
||||
users: "사용자"
|
||||
federation: "연합"
|
||||
announcements: "공지사항"
|
||||
hashtags: "해시태그"
|
||||
abuse: "스팸 신고"
|
||||
queue: "작업 대기열"
|
||||
logs: "로그"
|
||||
@ -1091,14 +1121,34 @@ admin/views/instance.vue:
|
||||
instance-name: "인스턴스 이름"
|
||||
instance-description: "인스턴스의 소개"
|
||||
host: "관리자"
|
||||
icon-url: "아이콘 URL"
|
||||
logo-url: "로고 URL"
|
||||
banner-url: "배너 이미지 URL"
|
||||
error-image-url: "오류 이미지 URL"
|
||||
languages: "인스턴스의 대상 언어"
|
||||
languages-desc: "공백으로 구분하여 여러 개 설정할 수 있습니다."
|
||||
tos-url: "이용약관 URL"
|
||||
repository-url: "저장소 URL"
|
||||
feedback-url: "피드백 URL"
|
||||
maintainer-config: "관리자 정보"
|
||||
maintainer-name: "관리자 이름"
|
||||
maintainer-email: "관리자 연락처"
|
||||
advanced-config: "그 외 설정"
|
||||
note-and-tl: "글과 타임라인"
|
||||
drive-config: "드라이브 설정"
|
||||
use-object-storage: "오브젝트 스토리지를 사용"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-bucket: "버킷 이름"
|
||||
object-storage-prefix: "프리픽스"
|
||||
object-storage-endpoint: "엔드포인트"
|
||||
object-storage-region: "리전"
|
||||
object-storage-port: "포트"
|
||||
object-storage-access-key: "액세스 키"
|
||||
object-storage-secret-key: "시크릿 키"
|
||||
object-storage-use-ssl: "SSL 사용"
|
||||
object-storage-s3-info: "Amazon S3를 오브젝트 스토리지로 사용하는 경우의 「엔드포인트」와 「리전」의 설정값에 대해서는 {0}을 확인하여 주십시오."
|
||||
object-storage-s3-info-here: "이곳"
|
||||
object-storage-gcs-info: "Google Cloud Storage를 오브젝트 스토리지로 사용하는 경우, 「엔드포인트」는 storage.googleapis.com 으로 설정하고, 「리전」 란은 비웁니다."
|
||||
cache-remote-files: "원격 파일을 캐시"
|
||||
cache-remote-files-desc: "이 설정을 해지하면 원격 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 프라이버시 설정에서 직접 링크를 무효로 설정한 사용자에게는 파일이 보이지 않거나, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다. 일반적으로 이 설정을 ON으로 두는 것을 추천합니다."
|
||||
local-drive-capacity-mb: "로컬 사용자 한 명당 드라이브 용량"
|
||||
@ -1106,9 +1156,14 @@ admin/views/instance.vue:
|
||||
mb: "메가바이트 단위"
|
||||
recaptcha-config: "reCAPCHA 설정"
|
||||
recaptcha-info: "reCAPCHA를 사용하도록 설정하는 경우 reCAPCHA 토큰을 확보해야 합니다. https://www.google.com/recaptcha/intro/ 에 접속하여 토큰을 가져와주십시오."
|
||||
recaptcha-info2: "v3는지원하지 않습니다. v2를 사용하여 주십시오."
|
||||
enable-recaptcha: "reCAPCHA 활성화"
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
recaptcha-site-key: "사이트 키"
|
||||
recaptcha-secret-key: "시크릿 키"
|
||||
recaptcha-preview: "미리보기"
|
||||
hidden-tags: "숨긴 해시태그"
|
||||
hidden-tags-info: "집계에서 제외할 해시태그를 줄 바꿈으로 구분하여 기술합니다."
|
||||
external-service-integration-config: "외부 서비스 연계"
|
||||
twitter-integration-config: "Twitter 연동 설정"
|
||||
twitter-integration-info: "콜백 URL은 {url} 로 설정됩니다."
|
||||
enable-twitter-integration: "트위터 연동 활성화"
|
||||
@ -1139,7 +1194,8 @@ admin/views/instance.vue:
|
||||
invite: "초대"
|
||||
save: "저장"
|
||||
saved: "저장하였습니다"
|
||||
user-recommendation-config: "추천 사용자"
|
||||
pinned-users: "고정된 사용자"
|
||||
pinned-users-info: "고정해두고 싶은 사용자를 줄바꿈으로 구분하여 기술합니다."
|
||||
email-config: "메일 서버 설정"
|
||||
email-config-info: "메일 주소 확인 혹은 비밀번호 재설정에 사용 됩니다."
|
||||
enable-email: "메일 발신 활성화"
|
||||
@ -1223,14 +1279,10 @@ admin/views/users.vue:
|
||||
silence-confirm: "침묵으로 설정합니까?"
|
||||
unmake-silence: "침묵 해제"
|
||||
unsilence-confirm: "침묵 해제하시겠습니까?"
|
||||
verify: "공식 계정으로 설정"
|
||||
verify-confirm: "공식 계정으로 설정하시겠습니까?"
|
||||
verified: "공식 계정으로 설정하였습니다"
|
||||
unverify: "공식 계정 해제"
|
||||
unverify-confirm: "공식 계정을 해제하시겠습니까?"
|
||||
unverified: "공식 계정을 해제하였습니다"
|
||||
update-remote-user: "원격 사용자 정보 갱신"
|
||||
remote-user-updated: "원격 사용자 정보를 갱신하였습니다"
|
||||
delete-all-files: "모든 파일 삭제"
|
||||
delete-all-files-confirm: "모든 파일을 삭제하시겠습니까?"
|
||||
users:
|
||||
title: "사용자"
|
||||
sort:
|
||||
@ -1245,7 +1297,6 @@ admin/views/users.vue:
|
||||
admin: "관리자"
|
||||
moderator: "모더레이터"
|
||||
adminOrModerator: "관리자+모더레이터"
|
||||
verified: "공식 계정"
|
||||
silenced: "침묵됨"
|
||||
suspended: "정지됨"
|
||||
origin:
|
||||
@ -1307,6 +1358,7 @@ admin/views/federation.vue:
|
||||
latest-request-received-at: "마지막으로 요청을 받은 시간"
|
||||
remove-all-following: "모든 팔로잉 해제"
|
||||
remove-all-following-info: "{host}(으)로부터 모든 팔로잉을 해제합니다. 해당 인스턴스가 더 이상 존재하지 않게 된 경우 등에 실행하십시오."
|
||||
delete-all-files: "파일을 모두 삭제"
|
||||
block: "차단"
|
||||
marked-as-closed: "폐쇄된 것으로 표시"
|
||||
lookup: "조회"
|
||||
@ -1353,6 +1405,8 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "1시간마다"
|
||||
day: "1일마다"
|
||||
blocked-hosts: "차단"
|
||||
blocked-hosts-info: "차단할 호스트를 줄바꿈으로 구분하여 기술합니다."
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "자세히..."
|
||||
timeline: "타임라인"
|
||||
@ -1509,6 +1563,7 @@ mobile/views/components/ui.nav.vue:
|
||||
follow-requests: "팔로우 요청"
|
||||
search: "검색"
|
||||
user-lists: "리스트"
|
||||
user-groups: "그룹"
|
||||
widgets: "위젯"
|
||||
game: "게임"
|
||||
admin: "관리"
|
||||
@ -1521,8 +1576,6 @@ mobile/views/pages/drive.vue:
|
||||
rename-folder: "폴더 이름 바꾸기"
|
||||
move-folder: "이 폴더를 이동"
|
||||
delete-folder: "이 폴더를 삭제"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "리스트"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "📦 이제 시작해도 됩니다"
|
||||
mobile/views/pages/followers.vue:
|
||||
@ -1547,10 +1600,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
activity: "활동"
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "{name}(으)로 공유"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "팔로우 요청"
|
||||
accept: "승인"
|
||||
reject: "거부"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "글"
|
||||
prev: "이전 글"
|
||||
@ -1652,42 +1701,251 @@ pages:
|
||||
page-created: "페이지를 만들었습니다"
|
||||
page-updated: "페이지를 수정했습니다"
|
||||
are-you-sure-delete: "이 페이지를 삭제하시겠습니까?"
|
||||
page-deleted: "페이지가 삭제되었습니다"
|
||||
edit-this-page: "이 페이지를 편집"
|
||||
view-source: "소스 보기"
|
||||
view-page: "페이지 보기"
|
||||
like: "좋아요"
|
||||
unlike: "좋아요 해제"
|
||||
liked-pages: "좋아요한 페이지"
|
||||
my-pages: "내 페이지"
|
||||
inspector: "인스펙터"
|
||||
content: "페이지 블록"
|
||||
variables: "변수"
|
||||
variables-info: "변수를 사용하면 동적인 페이지를 만들 수 있습니다. 텍스트에 <b>{ 변수명 }</b>을 적으면 그 위치에 변수의 값을 집어넣습니다. 예를 들자면 <b>Hello { thing } world!</b> 라는 텍스트가 있을 때, 변수(thing)의 값이 <b>ai</b>인 경우 텍스트는 <b>Hello ai world!</b>가 됩니다."
|
||||
variables-info2: "변수의 평가(값을 산출해내는 것)는 위에서부터 아래로 진행되므로 어떤 변수의 내부에서 자신보다 아래에 있는 변수를 참조할 수는 없습니다. 예를 들자면 위에서부터 <b>A, B, C</b>의 3개의 변수가 정의되어 있을 때, <b>C</b>의 내부에 <b>A</b>나 <b>B</b>를 참조할 수는 있지만, <b>A</b>의 내부에서 <b>B</b>나 <b>C</b>를 참조할 수는 없습니다."
|
||||
variables-info3: "사용자로부터 입력을 받으려면, 페이지에 「사용자 입력」 블록을 삽입하고 「변수명」에 입력받은 값을 저장하고 싶은 변수명을 설정합니다 (변수는 자동으로 생성됩니다). 그 변수를 사용하여 사용자 입력에 따라 동작할 수 있습니다."
|
||||
variables-info4: "함수를 사용하면 반복되는 작업을 손쉽게 처리할 수 있습니다. 함수를 만드시려면 「함수」 타입의 변수를 만듭니다. 함수에서 슬롯(인수)를 받도록 설정하면, 함수를 사용할 때 슬롯에 입력된 값을 함수 안에서 변수로써 이용할 수 있게 됩니다. 또한, AiScript 표준에는 함수를 인수로 받는 함수(고차함수)도 존재합니다. 함수를 미리 정의하는 것 외에, 이와 같은 고차함수를 즉석으로 설정할 수 있습니다."
|
||||
more-details: "자세한 설명"
|
||||
title: "제목"
|
||||
url: "페이지 URL"
|
||||
summary: "페이지 요약"
|
||||
align-center: "가운데 정렬"
|
||||
font: "글꼴"
|
||||
fontSerif: "세리프"
|
||||
fontSansSerif: "산 세리프"
|
||||
set-eye-catching-image: "아이캐치 이미지를 설정"
|
||||
remove-eye-catching-image: "아이캐치 이미지를 삭제"
|
||||
choose-block: "블록 추가"
|
||||
select-type: "종류 선택"
|
||||
enter-variable-name: "변수명을 설정해주십시오"
|
||||
the-variable-name-is-already-used: "그 변수명은 이미 사용중입니다"
|
||||
content-blocks: "콘텐츠"
|
||||
input-blocks: "입력"
|
||||
special-blocks: "특수"
|
||||
post-from-post-form: "이 내용을 올리기"
|
||||
posted-from-post-form: "게시하였습니다"
|
||||
blocks:
|
||||
text: "텍스트"
|
||||
textarea: "텍스트 영역"
|
||||
section: "섹션"
|
||||
image: "이미지"
|
||||
post: "게시 양식"
|
||||
button: "버튼"
|
||||
if: "만약"
|
||||
_if:
|
||||
variable: "변수"
|
||||
post: "글 입력란"
|
||||
_post:
|
||||
text: "내용"
|
||||
textInput: "텍스트 입력"
|
||||
_textInput:
|
||||
name: "변수명"
|
||||
text: "제목"
|
||||
default: "기본값"
|
||||
textareaInput: "여러 줄 텍스트 입력"
|
||||
_textareaInput:
|
||||
name: "변수명"
|
||||
text: "제목"
|
||||
default: "기본값"
|
||||
numberInput: "수치 입력"
|
||||
_numberInput:
|
||||
name: "변수명"
|
||||
text: "제목"
|
||||
default: "기본값"
|
||||
switch: "스위치"
|
||||
_switch:
|
||||
name: "변수명"
|
||||
text: "제목"
|
||||
default: "기본값"
|
||||
counter: "카운터"
|
||||
_counter:
|
||||
name: "변수명"
|
||||
text: "제목"
|
||||
inc: "증가치"
|
||||
_button:
|
||||
text: "제목"
|
||||
action: "버튼을 눌렀을 때의 동작"
|
||||
_action:
|
||||
dialog: "대화상자를 표시"
|
||||
_dialog:
|
||||
content: "내용"
|
||||
resetRandom: "난수를 초기화"
|
||||
script:
|
||||
categories:
|
||||
flow: "흐름 제어"
|
||||
logical: "논리 연산"
|
||||
operation: "계산"
|
||||
comparison: "비교"
|
||||
random: "랜덤"
|
||||
value: "값"
|
||||
fn: "함수"
|
||||
text: "텍스트 조작"
|
||||
convert: "변환"
|
||||
list: "리스트"
|
||||
blocks:
|
||||
text: "텍스트"
|
||||
multiLineText: "텍스트 (여러줄)"
|
||||
textList: "텍스트 목록"
|
||||
_textList:
|
||||
info: "각각을 줄 바꿈으로 구분해주십시오"
|
||||
strLen: "텍스트의 길이"
|
||||
_strLen:
|
||||
arg1: "텍스트"
|
||||
strPick: "문자 추출"
|
||||
_strPick:
|
||||
arg1: "텍스트"
|
||||
arg2: "문자 위치"
|
||||
strReplace: "텍스트 치환"
|
||||
_strReplace:
|
||||
arg1: "텍스트"
|
||||
arg2: "치환 전"
|
||||
arg3: "치환 후"
|
||||
strReverse: "텍스트 뒤집기"
|
||||
_strReverse:
|
||||
arg1: "텍스트"
|
||||
join: "텍스트 접합"
|
||||
_join:
|
||||
arg1: "리스트"
|
||||
arg2: "구분자"
|
||||
add: "+ 더하기"
|
||||
_add:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
subtract: "- 빼기"
|
||||
_subtract:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
multiply: "× 곱하기"
|
||||
_multiply:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
divide: "÷ 나누기"
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 나눈 나머지"
|
||||
_remind:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A와 B가 동일"
|
||||
_eq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
notEq: "A와 B가 다름"
|
||||
_notEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
and: "A 그리고 B"
|
||||
_and:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
or: "A 혹은 B"
|
||||
_or:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
lt: "< A가 B보다 작음"
|
||||
_lt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gt: "> A가 B보다 큼"
|
||||
_gt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
ltEq: "<= A가 B보다 작거나 같음"
|
||||
_ltEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gtEq: ">= A가 B보다 크거나 같음"
|
||||
_gtEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
if: "분기"
|
||||
_if:
|
||||
arg1: "만약"
|
||||
arg2: "그러면"
|
||||
arg3: "그렇지 않으면"
|
||||
not: "부정"
|
||||
_not:
|
||||
arg1: "부정"
|
||||
random: "랜덤"
|
||||
_random:
|
||||
arg1: "확률"
|
||||
rannum: "난수"
|
||||
_rannum:
|
||||
arg1: "최소"
|
||||
arg2: "최대"
|
||||
randomPick: "목록에서 임의로 선택"
|
||||
_randomPick:
|
||||
arg1: "리스트"
|
||||
dailyRandom: "랜덤 (하루동안 결과 유지)"
|
||||
_dailyRandom:
|
||||
arg1: "확률"
|
||||
dailyRannum: "난수 (하루동안 결과 유지)"
|
||||
_dailyRannum:
|
||||
arg1: "최소"
|
||||
arg2: "최대"
|
||||
dailyRandomPick: "목록에서 임의로 선택 (하루동안 결과 유지)"
|
||||
_dailyRandomPick:
|
||||
arg1: "리스트"
|
||||
seedRandom: "무작위 (시드)"
|
||||
_seedRandom:
|
||||
arg1: "시드"
|
||||
arg2: "확률"
|
||||
seedRannum: "난수 (시드)"
|
||||
_seedRannum:
|
||||
arg1: "시드"
|
||||
arg2: "최소"
|
||||
arg3: "최대"
|
||||
seedRandomPick: "목록에서 무작위로 선택 (시드)"
|
||||
_seedRandomPick:
|
||||
arg1: "시드"
|
||||
arg2: "리스트"
|
||||
DRPWPM: "확률형 목록에서 임의로 선택 (하루동안 결과 유지)"
|
||||
_DRPWPM:
|
||||
arg1: "텍스트 목록"
|
||||
pick: "목록에서 선택"
|
||||
_pick:
|
||||
arg1: "리스트"
|
||||
arg2: "위치"
|
||||
number: "수치"
|
||||
stringToNumber: "텍스트를 수치로"
|
||||
_stringToNumber:
|
||||
arg1: "텍스트"
|
||||
numberToString: "수치를 텍스트로"
|
||||
_numberToString:
|
||||
arg1: "수치"
|
||||
splitStrByLine: "텍스트를 행 단위로 분할"
|
||||
_splitStrByLine:
|
||||
arg1: "텍스트"
|
||||
ref: "변수"
|
||||
fn: "함수"
|
||||
_fn:
|
||||
slots: "슬롯"
|
||||
slots-info: "각 슬롯을 줄 바꿈으로 구분하여 주십시오"
|
||||
arg1: "출력"
|
||||
for: "반복"
|
||||
_for:
|
||||
arg1: "횟수"
|
||||
arg2: "처리"
|
||||
typeError: "슬롯 {slot}은 \"{expect}\"를 사용할 수 있지만 \"{actual}이 들어있습니다!"
|
||||
thereIsEmptySlot: "슬롯 {slot}이(가) 비었습니다!"
|
||||
types:
|
||||
string: "텍스트"
|
||||
number: "수치"
|
||||
boolean: "플래그"
|
||||
array: "리스트"
|
||||
stringArray: "텍스트 목록"
|
||||
emptySlot: "빈 슬롯"
|
||||
enviromentVariables: "환경 변수"
|
||||
pageVariables: "페이지 요소"
|
||||
argVariables: "입력 슬롯"
|
||||
|
@ -104,10 +104,9 @@ common/views/components/messaging.vue:
|
||||
search-user: "Gebruiker zoeken"
|
||||
you: "Jij"
|
||||
no-history: "Geen geschiedenis"
|
||||
user: "Gebruiker"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "Geen gesprekken"
|
||||
no-history: "Er is geen verdere geschiedenis"
|
||||
resize-form: "Versleep om grootte te wijzigen"
|
||||
new-message: "Nieuw bericht"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Voer hier je bericht in"
|
||||
@ -202,6 +201,9 @@ common/views/components/profile-editor.vue:
|
||||
enter-password: "Voer het wachtwoord in"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Gebruiker"
|
||||
add-user: "Gebruiker toevoegen"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Lijsten"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Bezig met ophalen"
|
||||
no-broadcasts: "Geen uitzendingen"
|
||||
@ -401,8 +403,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Nieuw bericht opstellen"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Zoeken"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Lijsten"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Berichten"
|
||||
following: "Volgend"
|
||||
@ -422,8 +422,6 @@ admin/views/dashboard.vue:
|
||||
notes: "Bericht"
|
||||
admin/views/abuse.vue:
|
||||
remove-report: "Verwijderen"
|
||||
admin/views/instance.vue:
|
||||
user-recommendation-config: "Aanbevolen gebruikers"
|
||||
admin/views/charts.vue:
|
||||
notes: "Bericht"
|
||||
users: "Gebruiker"
|
||||
@ -560,8 +558,6 @@ mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Bestand uploaden"
|
||||
create-folder: "Map creëren"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Lijsten"
|
||||
mobile/views/pages/home.vue:
|
||||
home: "Startpagina"
|
||||
local: "Lokaal"
|
||||
@ -620,6 +616,7 @@ docs:
|
||||
edit-this-page-on-github: "Heb je een fout ontdekt of wil je bijdragen aan de documentatie? "
|
||||
edit-this-page-on-github-link: "Bewerk deze pagina op GitHub!"
|
||||
pages:
|
||||
like: "Leuk"
|
||||
blocks:
|
||||
image: "Afbeeldingen"
|
||||
script:
|
||||
|
@ -130,6 +130,7 @@ common/views/components/cw-button.vue:
|
||||
hide: "Skjul"
|
||||
common/views/components/messaging.vue:
|
||||
you: "Du"
|
||||
user: "Bruker"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
send: "Send"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
@ -192,8 +193,13 @@ common/views/components/profile-editor.vue:
|
||||
user-lists: "Lister"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Bruker"
|
||||
common/views/components/user-group-editor.vue:
|
||||
invite: "Inviter"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Lister"
|
||||
list-name: "Liste navn"
|
||||
common/views/components/user-groups.vue:
|
||||
invites: "Inviter"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Henter"
|
||||
next: "Neste"
|
||||
@ -307,11 +313,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Skriv nytt innlegg"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Søk"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
accept: "Godta"
|
||||
reject: "Avslå"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Brukerlister"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Innlegg"
|
||||
following: "Følger"
|
||||
@ -448,17 +449,12 @@ mobile/views/components/ui.nav.vue:
|
||||
user-lists: "Lister"
|
||||
game: "Spill"
|
||||
admin: "Admin"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Lister"
|
||||
mobile/views/pages/home.vue:
|
||||
home: "Hjem"
|
||||
local: "Lokalt"
|
||||
global: "Globalt"
|
||||
mobile/views/pages/widgets.vue:
|
||||
add-widget: "Legg til"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
accept: "Godta"
|
||||
reject: "Avslå"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "Innlegg"
|
||||
prev: "Forrige innlegg"
|
||||
@ -490,6 +486,7 @@ deck/deck.user-column.vue:
|
||||
followers: "Følgere"
|
||||
images: "Bilder"
|
||||
pages:
|
||||
like: "Lik"
|
||||
blocks:
|
||||
image: "Bilder"
|
||||
script:
|
||||
|
@ -166,9 +166,9 @@ common:
|
||||
update-available-title: "Aktualizacja jest dostępna"
|
||||
update-available: "Nowa wersja Misskey jest dostępna ({newer}, obecna to {current}). Odśwież stronę, aby zastosować aktualizację."
|
||||
my-token-regenerated: "Twój token został wygenerowany. Zostaniesz wylogowany."
|
||||
verified-user: "Zweryfikowane konto"
|
||||
hide-password: "Ukryj hasło"
|
||||
show-password: "Pokaż hasło"
|
||||
enter-username: "Wprowadź nazwę użytkownika"
|
||||
view-on-remote: "Dla dopełnienia, zobacz to zdalnie."
|
||||
renoted-by: "{user} udostępnił(a)"
|
||||
error:
|
||||
@ -218,8 +218,6 @@ auth/views/index.vue:
|
||||
please-go-back: "Wróć do aplikacji."
|
||||
error: "Sesja nie istnieje."
|
||||
sign-in: "Proszę zalogować się."
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Zweryfikowane konto"
|
||||
common/views/components/games/reversi/reversi.vue:
|
||||
matching:
|
||||
waiting-for: "Oczekiwanie na {}"
|
||||
@ -326,18 +324,15 @@ common/views/components/messaging.vue:
|
||||
search-user: "Znajdź użytkownika"
|
||||
you: "Ty"
|
||||
no-history: "Brak historii"
|
||||
user: "Użytkownicy"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "Brak konwersacji"
|
||||
no-history: "Brak dalszej historii"
|
||||
resize-form: "Przeciągnij aby zmienić rozmiar"
|
||||
new-message: "Nowa wiadomość"
|
||||
only-one-file-attached: "Tylko JEDEN plik może zostać dołączony do wiadomości"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Wprowadź wiadomość tutaj"
|
||||
send: "Wyślij"
|
||||
attach-from-local: "Załącz pliki z komputera"
|
||||
attach-from-drive: "Załącz pliki z dysku"
|
||||
only-one-file-attached: "Tylko jeden plik może zostać dołączony do wiadomości"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "Przeczytano"
|
||||
deleted: "Wiadomość została usunięta"
|
||||
@ -514,8 +509,15 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "Usuń z tej listy"
|
||||
delete-are-you-sure: "Usunąć listę \"$1\"?"
|
||||
deleted: "Usunięto"
|
||||
add-user: "Dodaj użytkownika"
|
||||
common/views/components/user-group-editor.vue:
|
||||
deleted: "Usunięto"
|
||||
invite: "Zaproś"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "Listy"
|
||||
list-name: "Nazwa listy"
|
||||
common/views/components/user-groups.vue:
|
||||
invites: "Zaproś"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Sprawdzanie"
|
||||
no-broadcasts: "Brak transmisji"
|
||||
@ -571,6 +573,8 @@ common/views/pages/follow.vue:
|
||||
request-pending: "Oczekiwanie na pozwolenie"
|
||||
follow-processing: "Przetwarzanie śledzenia"
|
||||
follow-request: "Poproś o śledzenie"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "Prośby o śledzenie"
|
||||
desktop:
|
||||
banner: "Baner"
|
||||
uploading-banner: "Wysyłanie baneru"
|
||||
@ -832,12 +836,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "Utwórz nowy wpis"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "Szukaj"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "Prośby o śledzenie"
|
||||
accept: "Zatwierdź"
|
||||
reject: "Odmów"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "Listy"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "Wpisy"
|
||||
following: "Śledzeni"
|
||||
@ -858,7 +856,6 @@ admin/views/index.vue:
|
||||
moderators: "Moderatorzy"
|
||||
users: "Użytkownicy"
|
||||
announcements: "Ogłoszenia"
|
||||
hashtags: "Hashtagi"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Kokpit"
|
||||
accounts: "Konta"
|
||||
@ -870,6 +867,7 @@ admin/views/abuse.vue:
|
||||
remove-report: "Usuń"
|
||||
admin/views/instance.vue:
|
||||
instance: "Instancja"
|
||||
recaptcha-preview: "Pokaż podgląd"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-client-id: "Client ID"
|
||||
@ -877,7 +875,6 @@ admin/views/instance.vue:
|
||||
invite: "Zaproś"
|
||||
save: "Zapisz"
|
||||
saved: "Zapisano"
|
||||
user-recommendation-config: "Polecani użytkownicy"
|
||||
email: "Adres e-mail"
|
||||
admin/views/charts.vue:
|
||||
notes: "Wpisy"
|
||||
@ -907,7 +904,6 @@ admin/views/users.vue:
|
||||
state:
|
||||
all: "Wszyscy"
|
||||
moderator: "Moderatorzy"
|
||||
verified: "Zweryfikowane konto"
|
||||
origin:
|
||||
title: "Źródło"
|
||||
local: "Lokalny"
|
||||
@ -953,6 +949,7 @@ admin/views/federation.vue:
|
||||
blocked: "Zablokuj"
|
||||
chart-srcs:
|
||||
requests: "Żądania"
|
||||
blocked-hosts: "Zablokuj"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "O Misskey"
|
||||
timeline: "Oś czasu"
|
||||
@ -1107,8 +1104,6 @@ mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Wyślij plik"
|
||||
create-folder: "Utwórz katalog"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Listy"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "Rozpocznijmy! 📦"
|
||||
mobile/views/pages/home.vue:
|
||||
@ -1123,10 +1118,6 @@ mobile/views/pages/widgets.vue:
|
||||
customization-tips: "Wskazówki o dostosowywaniu"
|
||||
mobile/views/pages/widgets/activity.vue:
|
||||
activity: "Aktywność"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "Prośby o śledzenie"
|
||||
accept: "Zatwierdź"
|
||||
reject: "Odmów"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "Wpis"
|
||||
prev: "Poprzedni wpis"
|
||||
@ -1201,6 +1192,7 @@ dev/views/new-app.vue:
|
||||
app-name: "Nazwa Aplikacji"
|
||||
authority: "Uprawnienia"
|
||||
pages:
|
||||
like: "Lubię"
|
||||
title: "Tytuł"
|
||||
blocks:
|
||||
image: "Zdjęcia"
|
||||
|
@ -89,7 +89,6 @@ common:
|
||||
update-available-title: "Atualização disponível"
|
||||
update-available: "Uma nova versão de Misskey está disponível ({newer}). A versão atual é {current}. Recarregue a página para atualizar."
|
||||
my-token-regenerated: "Seu token foi recriado, portanto você foi deslogado."
|
||||
verified-user: "Conta verificada"
|
||||
reversi:
|
||||
drawn: "Empatado"
|
||||
my-turn: "Seu turno"
|
||||
@ -129,8 +128,6 @@ auth/views/index.vue:
|
||||
please-go-back: "Por favor, volte ao aplicativo."
|
||||
error: "A sessão não existe."
|
||||
sign-in: "Por favor, entre."
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "Conta verificada"
|
||||
common/views/components/games/reversi/reversi.index.vue:
|
||||
invite: "Convidar"
|
||||
rule: "Como jogar"
|
||||
@ -163,6 +160,10 @@ common/views/components/profile-editor.vue:
|
||||
name: "Nome"
|
||||
export-targets:
|
||||
following-list: "Seguindo"
|
||||
common/views/components/user-group-editor.vue:
|
||||
invite: "Convidar"
|
||||
common/views/components/user-groups.vue:
|
||||
invites: "Convidar"
|
||||
common/views/widgets/posts-monitor.vue:
|
||||
title: "Gráfico de publicações"
|
||||
common/views/widgets/memo.vue:
|
||||
@ -194,10 +195,6 @@ admin/views/instance.vue:
|
||||
invite: "Convidar"
|
||||
admin/views/drive.vue:
|
||||
delete: "Apagar"
|
||||
admin/views/users.vue:
|
||||
users:
|
||||
state:
|
||||
verified: "Conta verificada"
|
||||
admin/views/emoji.vue:
|
||||
emojis:
|
||||
remove: "Apagar"
|
||||
@ -277,6 +274,7 @@ docs:
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Gerenciar aplicativos"
|
||||
pages:
|
||||
like: "Curtir"
|
||||
blocks:
|
||||
image: "Imagens"
|
||||
post: "Formulário de publicação"
|
||||
|
@ -147,6 +147,7 @@ mobile/views/components/sub-note-content.vue:
|
||||
mobile/views/pages/widgets.vue:
|
||||
customization-tips: "Советы по настройке"
|
||||
pages:
|
||||
like: "Нравится"
|
||||
script:
|
||||
categories:
|
||||
random: "Случайно"
|
||||
|
@ -145,7 +145,7 @@ common:
|
||||
profile: "个人资料"
|
||||
notification: "通知"
|
||||
apps: "应用程序"
|
||||
tags: "标签"
|
||||
tags: "哈希标签"
|
||||
mute-and-block: "屏蔽/拉黑"
|
||||
blocking: "拉黑"
|
||||
security: "安全性"
|
||||
@ -168,7 +168,7 @@ common:
|
||||
use-avatar-reversi-stones: "用头像作为黑白棋的棋子"
|
||||
disable-animated-mfm: "在帖子中禁用动画文本"
|
||||
disable-showing-animated-images: "不播放动画"
|
||||
suggest-recent-hashtags: "在帖子表单上显示最近流行的主题标签"
|
||||
suggest-recent-hashtags: "在帖子表单上显示最近流行的哈希标签"
|
||||
always-show-nsfw: "总是显示 NSFW 的内容"
|
||||
always-mark-nsfw: "总是用 NSFW 来标记附件"
|
||||
show-full-acct: "不要从用户名中忽略主机名"
|
||||
@ -251,9 +251,9 @@ common:
|
||||
update-available-title: "有可用更新"
|
||||
update-available: "新的 Misskey 版本现已发布({newer}。目前版本{current}). 刷新页面以应用更新。"
|
||||
my-token-regenerated: "您的 Token 已被重置, 您将自动登出。"
|
||||
verified-user: "认证用户"
|
||||
hide-password: "隐藏密码"
|
||||
show-password: "显示密码"
|
||||
enter-username: "请输入用户名"
|
||||
do-not-use-in-production: "这是一个开发者测试版. 请勿在生产环境中使用."
|
||||
user-suspended: "该用户已被冻结。"
|
||||
is-remote-user: "此用户信息可能不准确。"
|
||||
@ -298,7 +298,7 @@ common:
|
||||
server: "服务器信息"
|
||||
nav: "导航"
|
||||
tips: "提示"
|
||||
hashtags: "标签"
|
||||
hashtags: "哈希标签"
|
||||
queue: "队列"
|
||||
dev: "构建应用程序失败,请再试一次。"
|
||||
ai-chan-kawaii: "小蓝真可爱"
|
||||
@ -319,7 +319,7 @@ auth/views/index.vue:
|
||||
error: "会话不存在。"
|
||||
sign-in: "请登录。"
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "官方账户"
|
||||
pinned-users: "已置顶用户"
|
||||
popular-users: "热门用户"
|
||||
recently-updated-users: "活跃用户"
|
||||
recently-registered-users: "新用户"
|
||||
@ -449,18 +449,23 @@ common/views/components/messaging.vue:
|
||||
search-user: "查找用户"
|
||||
you: "您"
|
||||
no-history: "没有历史记录"
|
||||
user: "用户"
|
||||
group: "群组"
|
||||
start-with-user: "开始用户聊天"
|
||||
start-with-group: "开始群组聊天"
|
||||
select-group: "请选择群组"
|
||||
common/views/components/messaging-room.vue:
|
||||
empty: "没有对话"
|
||||
not-talked-user: "没有用户的会话记录"
|
||||
not-talked-group: "没有群组的会话记录"
|
||||
no-history: "没有更多的历史记录"
|
||||
resize-form: "拖动以调整大小"
|
||||
new-message: "新信息"
|
||||
only-one-file-attached: "只有一个附件可以附加到消息中。"
|
||||
only-one-file-attached: "只能添加一个附件"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "在此键入信息"
|
||||
send: "发送"
|
||||
attach-from-local: "从电脑中添加文件"
|
||||
attach-from-drive: "从网盘中添加文件"
|
||||
only-one-file-attached: "在信息中只允许添加一个附件"
|
||||
only-one-file-attached: "只能添加一个附件"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
is-read: "已读"
|
||||
deleted: "此消息已被删除"
|
||||
@ -470,9 +475,10 @@ common/views/components/nav.vue:
|
||||
status: "状态"
|
||||
wiki: "维基百科"
|
||||
donors: "捐赠者"
|
||||
repository: "代码库"
|
||||
repository: "源码库"
|
||||
develop: "开发人员"
|
||||
feedback: "反馈"
|
||||
tos: "服务条款"
|
||||
common/views/components/note-menu.vue:
|
||||
mention: "提到"
|
||||
detail: "详细信息"
|
||||
@ -585,6 +591,8 @@ common/views/components/signup.vue:
|
||||
password-matched: "确认"
|
||||
password-not-matched: "密码不一致"
|
||||
recaptcha: "验证"
|
||||
agree-to: "同意{0}"
|
||||
tos: "服务条款"
|
||||
create: "创建一个账户"
|
||||
some-error: "由于某种原因,创建帐户失败。请再试一次。"
|
||||
common/views/components/special-message.vue:
|
||||
@ -692,9 +700,32 @@ common/views/components/user-list-editor.vue:
|
||||
remove-user: "从此列表中删除"
|
||||
delete-are-you-sure: "删除列表“$1”?"
|
||||
deleted: "已删除"
|
||||
add-user: "添加用户"
|
||||
common/views/components/user-group-editor.vue:
|
||||
users: "成员"
|
||||
rename: "更改群组名"
|
||||
delete: "删除群组"
|
||||
transfer: "群组转让"
|
||||
transfer-are-you-sure: "将群组「$1」转让给「@$2」吗?"
|
||||
transferred: "群组已转让"
|
||||
remove-user: "从本群组中删除"
|
||||
delete-are-you-sure: "确定要删除「$1」组?"
|
||||
deleted: "已删除"
|
||||
invite: "邀请"
|
||||
invited: "邀请已发送"
|
||||
common/views/components/user-lists.vue:
|
||||
user-lists: "列表"
|
||||
create-list: "创建列表"
|
||||
list-name: "列表名称"
|
||||
common/views/components/user-groups.vue:
|
||||
user-groups: "群组"
|
||||
create-group: "创建群组"
|
||||
group-name: "群组名"
|
||||
owned-groups: "我的群组"
|
||||
joined-groups: "加入群组"
|
||||
invites: "邀请"
|
||||
accept-invite: "加入"
|
||||
reject-invite: "拒绝"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "确认中"
|
||||
no-broadcasts: "没有公告"
|
||||
@ -714,7 +745,7 @@ common/views/widgets/posts-monitor.vue:
|
||||
title: "投稿表格"
|
||||
toggle: "切换视图"
|
||||
common/views/widgets/hashtags.vue:
|
||||
title: "标签"
|
||||
title: "哈希标签"
|
||||
common/views/widgets/server.vue:
|
||||
title: "服务器信息"
|
||||
toggle: "切换显示"
|
||||
@ -756,6 +787,10 @@ common/views/pages/follow.vue:
|
||||
request-pending: "发送关注申请"
|
||||
follow-processing: "申请处理中"
|
||||
follow-request: "关注请求"
|
||||
common/views/pages/follow-requests.vue:
|
||||
received-follow-requests: "关注申请"
|
||||
accept: "接受"
|
||||
reject: "拒绝"
|
||||
desktop:
|
||||
banner-crop-title: "裁剪显示为背景的部分"
|
||||
banner: "背景"
|
||||
@ -1017,8 +1052,8 @@ desktop/views/components/timeline.vue:
|
||||
mentions: "提到的"
|
||||
messages: "直接发布"
|
||||
list: "列表"
|
||||
hashtag: "标签"
|
||||
add-tag-timeline: "添加标签"
|
||||
hashtag: "哈希标签"
|
||||
add-tag-timeline: "添加哈希标签"
|
||||
add-list: "添加列表"
|
||||
list-name: "列表名称"
|
||||
desktop/views/components/ui.header.vue:
|
||||
@ -1027,6 +1062,7 @@ desktop/views/components/ui.header.vue:
|
||||
desktop/views/components/ui.header.account.vue:
|
||||
profile: "个人资料"
|
||||
lists: "列表"
|
||||
groups: "群组"
|
||||
follow-requests: "关注申请"
|
||||
admin: "管理"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
@ -1037,12 +1073,6 @@ desktop/views/components/ui.header.post.vue:
|
||||
post: "撰写新帖子"
|
||||
desktop/views/components/ui.header.search.vue:
|
||||
placeholder: "搜索"
|
||||
desktop/views/components/received-follow-requests-window.vue:
|
||||
title: "关注申请"
|
||||
accept: "接收"
|
||||
reject: "拒绝"
|
||||
desktop/views/components/user-lists-window.vue:
|
||||
title: "用户列表"
|
||||
desktop/views/components/user-preview.vue:
|
||||
notes: "帖子"
|
||||
following: "关注中"
|
||||
@ -1064,7 +1094,6 @@ admin/views/index.vue:
|
||||
users: "用户"
|
||||
federation: "联合"
|
||||
announcements: "公告"
|
||||
hashtags: "标签"
|
||||
abuse: "举报垃圾信息"
|
||||
queue: "作业队列"
|
||||
logs: "登录"
|
||||
@ -1091,14 +1120,34 @@ admin/views/instance.vue:
|
||||
instance-name: "实例名称"
|
||||
instance-description: "实例介绍"
|
||||
host: "主机名"
|
||||
icon-url: "图标URL"
|
||||
logo-url: "Logo URL"
|
||||
banner-url: "背景图片地址"
|
||||
error-image-url: "无效的图像URL"
|
||||
languages: "实例语言"
|
||||
languages-desc: "您可以添加多个,以空格分隔。"
|
||||
tos-url: "服务条款URL"
|
||||
repository-url: "源码库URL"
|
||||
feedback-url: "反馈URL"
|
||||
maintainer-config: "管理员信息"
|
||||
maintainer-name: "管理员名称"
|
||||
maintainer-email: "联系管理员"
|
||||
advanced-config: "其他设置"
|
||||
note-and-tl: "帖子和时间线"
|
||||
drive-config: "网盘设置"
|
||||
use-object-storage: "使用对象存储"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-bucket: "存储空间名"
|
||||
object-storage-prefix: "前缀"
|
||||
object-storage-endpoint: "端点"
|
||||
object-storage-region: "区域"
|
||||
object-storage-port: "端口"
|
||||
object-storage-access-key: "访问密钥"
|
||||
object-storage-secret-key: "密钥"
|
||||
object-storage-use-ssl: "使用 SSL"
|
||||
object-storage-s3-info: "使用Amazon S3作为对象存储时,请确认{0}相关“终端”和“区域”的设置。"
|
||||
object-storage-s3-info-here: "这里"
|
||||
object-storage-gcs-info: "将Google Cloud Storage用作对象存储时,请将“终端”设置为storage.googleapis.com,并将“区域”留空。"
|
||||
cache-remote-files: "远程文件缓存"
|
||||
cache-remote-files-desc: "如果没有此参数,则所有远程文件都将直接链接到其主机服务器。 这将是保存服务器存储的有效解决方案,但是对于设置禁用直接链接的用户而言,远程文件不可见,因为不会生成缩略图,从而增加流量。 建议启用此参数集。"
|
||||
local-drive-capacity-mb: "每个用户的网盘空间"
|
||||
@ -1106,9 +1155,14 @@ admin/views/instance.vue:
|
||||
mb: "以兆字节(Mbps)为单位"
|
||||
recaptcha-config: "reCAPTCHA设置"
|
||||
recaptcha-info: "reCAPTCHA token是必要的. 请从 https://www.google.com/recaptcha/intro/ 获取。\n请注意, 该功能在中国大陆不可用。"
|
||||
recaptcha-info2: "不支持v3。请使用v2。"
|
||||
enable-recaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)"
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
recaptcha-site-key: "网站密钥"
|
||||
recaptcha-secret-key: "密钥"
|
||||
recaptcha-preview: "预览"
|
||||
hidden-tags: "隐藏哈希标签"
|
||||
hidden-tags-info: "使用换行符分隔要从集合中排除的哈希标签。"
|
||||
external-service-integration-config: "连接外部服务"
|
||||
twitter-integration-config: "连接到Twitter的设置"
|
||||
twitter-integration-info: "设置返回的URL{url}。"
|
||||
enable-twitter-integration: "启用连接到Twitter"
|
||||
@ -1139,7 +1193,8 @@ admin/views/instance.vue:
|
||||
invite: "邀请"
|
||||
save: "保存"
|
||||
saved: "保存完毕"
|
||||
user-recommendation-config: "推荐用户"
|
||||
pinned-users: "置顶用户"
|
||||
pinned-users-info: "描述您要置顶的用户,以换行符分隔。"
|
||||
email-config: "电子邮件服务器设置"
|
||||
email-config-info: "用于确认电子邮件和密码重置等。"
|
||||
enable-email: "启用电子邮件送递"
|
||||
@ -1223,14 +1278,10 @@ admin/views/users.vue:
|
||||
silence-confirm: "确认屏蔽?"
|
||||
unmake-silence: "解除禁言"
|
||||
unsilence-confirm: "解除屏蔽?"
|
||||
verify: "认证用户"
|
||||
verify-confirm: "是否官方账号?"
|
||||
verified: "此账户已被认证"
|
||||
unverify: "解除账户认证"
|
||||
unverify-confirm: "是否解除官方账号认证?"
|
||||
unverified: "该帐户未经认证"
|
||||
update-remote-user: "更新远程用户信息"
|
||||
remote-user-updated: "远程用户信息已更新"
|
||||
delete-all-files: "删除所有文件"
|
||||
delete-all-files-confirm: "删除所有文件吗?"
|
||||
users:
|
||||
title: "用户"
|
||||
sort:
|
||||
@ -1245,7 +1296,6 @@ admin/views/users.vue:
|
||||
admin: "管理员"
|
||||
moderator: "版主"
|
||||
adminOrModerator: "管理员+版主"
|
||||
verified: "官方认证账户"
|
||||
silenced: "已禁言"
|
||||
suspended: "已冻结"
|
||||
origin:
|
||||
@ -1307,6 +1357,7 @@ admin/views/federation.vue:
|
||||
latest-request-received-at: "上次收到的请求"
|
||||
remove-all-following: "取消所有关注"
|
||||
remove-all-following-info: "取消{host}的所有关注者。当实例不存在时执行。"
|
||||
delete-all-files: "删除所有文件"
|
||||
block: "拉黑"
|
||||
marked-as-closed: "标记为已关闭"
|
||||
lookup: "查询"
|
||||
@ -1353,6 +1404,8 @@ admin/views/federation.vue:
|
||||
chart-spans:
|
||||
hour: "每小时"
|
||||
day: "每天"
|
||||
blocked-hosts: "拉黑"
|
||||
blocked-hosts-info: "描述您要阻止的主机,以换行符分隔。"
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "更多信息..."
|
||||
timeline: "时间线"
|
||||
@ -1374,7 +1427,7 @@ desktop/views/pages/search.vue:
|
||||
not-available: "在此实例的设置中关闭搜索功能。"
|
||||
not-found: "没有找到“{q}”的帖子"
|
||||
desktop/views/pages/tag.vue:
|
||||
no-posts-found: "没有找到带有主题标签“{q}”的帖子"
|
||||
no-posts-found: "没有找到带有哈希标签“{q}”的帖子"
|
||||
desktop/views/pages/user-list.users.vue:
|
||||
users: "用户"
|
||||
add-user: "添加用户"
|
||||
@ -1450,7 +1503,7 @@ mobile/views/components/drive.file-detail.vue:
|
||||
download: "下载"
|
||||
rename: "重命名"
|
||||
move: "移动"
|
||||
hash: "Hash (md5)"
|
||||
hash: "哈希(md5)"
|
||||
exif: "EXIF"
|
||||
nsfw: "阅读注意"
|
||||
mark-as-sensitive: "标记为“敏感”"
|
||||
@ -1509,6 +1562,7 @@ mobile/views/components/ui.nav.vue:
|
||||
follow-requests: "关注申请"
|
||||
search: "搜索"
|
||||
user-lists: "列表"
|
||||
user-groups: "群组"
|
||||
widgets: "小部件"
|
||||
game: "游戏"
|
||||
admin: "管理"
|
||||
@ -1521,8 +1575,6 @@ mobile/views/pages/drive.vue:
|
||||
rename-folder: "重命名文件夹"
|
||||
move-folder: "移动此文件夹"
|
||||
delete-folder: "删除此文件夹"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "列表"
|
||||
mobile/views/pages/signup.vue:
|
||||
lets-start: "您的账户现已准备就绪! 📦"
|
||||
mobile/views/pages/followers.vue:
|
||||
@ -1537,7 +1589,7 @@ mobile/views/pages/home.vue:
|
||||
mentions: "Mentions"
|
||||
messages: "直接发布"
|
||||
mobile/views/pages/tag.vue:
|
||||
no-posts-found: "没有找到带有主题标签“{q}”的帖子"
|
||||
no-posts-found: "没有找到带有哈希标签“{q}”的帖子"
|
||||
mobile/views/pages/widgets.vue:
|
||||
dashboard: "仪表盘"
|
||||
widgets-hints: "您可以添加/删除/重新排列小部件。 要移动小部件,请拖动“三”。 点击“×”删除小部件。 某些小部件可以通过点击来更改显示。"
|
||||
@ -1547,10 +1599,6 @@ mobile/views/pages/widgets/activity.vue:
|
||||
activity: "活动"
|
||||
mobile/views/pages/share.vue:
|
||||
share-with: "共享{name}"
|
||||
mobile/views/pages/received-follow-requests.vue:
|
||||
title: "关注申请"
|
||||
accept: "批准"
|
||||
reject: "拒绝"
|
||||
mobile/views/pages/note.vue:
|
||||
title: "帖文"
|
||||
prev: "上一个帖子"
|
||||
@ -1589,7 +1637,7 @@ deck:
|
||||
home: "首页"
|
||||
local: "Local"
|
||||
hybrid: "社交"
|
||||
hashtag: "标签"
|
||||
hashtag: "哈希标签"
|
||||
global: "Global"
|
||||
mentions: "Mentions"
|
||||
direct: "直接发布"
|
||||
@ -1656,6 +1704,10 @@ pages:
|
||||
edit-this-page: "编辑此页面"
|
||||
view-source: "查看源代码"
|
||||
view-page: "查看页面"
|
||||
like: "赞"
|
||||
unlike: "取消赞"
|
||||
liked-pages: "喜欢的页面"
|
||||
my-pages: "个人页面"
|
||||
inspector: "检查器"
|
||||
content: "页面内容"
|
||||
variables: "变量"
|
||||
@ -1671,6 +1723,8 @@ pages:
|
||||
font: "字体"
|
||||
fontSerif: "衬线字体"
|
||||
fontSansSerif: "无衬线字体"
|
||||
set-eye-catching-image: "设置封面图片"
|
||||
remove-eye-catching-image: "删除封面图片"
|
||||
choose-block: "添加块"
|
||||
select-type: "类型选择"
|
||||
enter-variable-name: "请确定变量名"
|
||||
@ -1735,6 +1789,7 @@ pages:
|
||||
value: "值"
|
||||
fn: "函数"
|
||||
text: "文本操作"
|
||||
convert: "转换"
|
||||
list: "列表"
|
||||
blocks:
|
||||
text: "文本"
|
||||
@ -1818,6 +1873,9 @@ pages:
|
||||
arg1: "如果"
|
||||
arg2: "的话"
|
||||
arg3: "否则"
|
||||
not: "否定"
|
||||
_not:
|
||||
arg1: "否定"
|
||||
random: "随机"
|
||||
_random:
|
||||
arg1: "概率"
|
||||
@ -1851,6 +1909,7 @@ pages:
|
||||
_seedRandomPick:
|
||||
arg1: "种子"
|
||||
arg2: "列表"
|
||||
DRPWPM: "从概率列表中随机选择(每用户每天)"
|
||||
_DRPWPM:
|
||||
arg1: "文本列表"
|
||||
pick: "从列表中选择"
|
||||
|
@ -86,3 +86,5 @@ admin/views/dashboard.vue:
|
||||
drive: "雲端硬碟"
|
||||
admin/views/charts.vue:
|
||||
drive: "雲端硬碟"
|
||||
pages:
|
||||
like: "贊"
|
||||
|
@ -3,21 +3,14 @@ import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
export class UserProfile1556746559567 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`UPDATE "user_profile" SET github = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubId"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "githubId" VARCHAR(64)`);
|
||||
await queryRunner.query(`UPDATE "user_profile" SET discord = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordExpiresDate"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "discordExpiresDate" VARCHAR(64)`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "githubId" TYPE VARCHAR(64) USING "githubId"::VARCHAR(64)`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "discordExpiresDate" TYPE VARCHAR(64) USING "discordExpiresDate"::VARCHAR(64)`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`UPDATE "user_profile" SET github = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubId"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "githubId" INTEGER`);
|
||||
await queryRunner.query(`UPDATE "user_profile" SET discord = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordExpiresDate"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "discordExpiresDate" INTEGER`);
|
||||
await queryRunner.query(`UPDATE "user_profile" SET github = FALSE, discord = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "githubId" TYPE INTEGER USING NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "discordExpiresDate" TYPE INTEGER USING NULL`);
|
||||
}
|
||||
|
||||
}
|
||||
|
16
migration/1557761316509-AddSomeUrls.ts
Normal file
16
migration/1557761316509-AddSomeUrls.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class AddSomeUrls1557761316509 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "ToSUrl" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://github.com/syuilo/misskey'`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://github.com/syuilo/misskey/issues/new'`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "feedbackUrl"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "repositoryUrl"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "ToSUrl"`);
|
||||
}
|
||||
}
|
31
migration/1557932705754-ObjectStorageSetting.ts
Normal file
31
migration/1557932705754-ObjectStorageSetting.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class ObjectStorageSetting1557932705754 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "useObjectStorage" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageBucket" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStoragePrefix" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageBaseUrl" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageEndpoint" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageRegion" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageAccessKey" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageSecretKey" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStoragePort" integer`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageUseSSL" boolean NOT NULL DEFAULT true`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageUseSSL"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStoragePort"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageSecretKey"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageAccessKey"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageRegion"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageEndpoint"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageBaseUrl"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStoragePrefix"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageBucket"`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "useObjectStorage"`);
|
||||
}
|
||||
|
||||
}
|
23
migration/1558072954435-PageLike.ts
Normal file
23
migration/1558072954435-PageLike.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class PageLike1558072954435 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "page_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "pageId" character varying(32) NOT NULL, CONSTRAINT "PK_813f034843af992d3ae0f43c64c" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_0e61efab7f88dbb79c9166dbb4" ON "page_like" ("userId") `);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4ce6fb9c70529b4c8ac46c9bfa" ON "page_like" ("userId", "pageId") `);
|
||||
await queryRunner.query(`ALTER TABLE "page" ADD "likedCount" integer NOT NULL DEFAULT 0`);
|
||||
await queryRunner.query(`ALTER TABLE "page_like" ADD CONSTRAINT "FK_0e61efab7f88dbb79c9166dbb48" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "page_like" ADD CONSTRAINT "FK_cf8782626dced3176038176a847" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "page_like" DROP CONSTRAINT "FK_cf8782626dced3176038176a847"`);
|
||||
await queryRunner.query(`ALTER TABLE "page_like" DROP CONSTRAINT "FK_0e61efab7f88dbb79c9166dbb48"`);
|
||||
await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "likedCount"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_4ce6fb9c70529b4c8ac46c9bfa"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_0e61efab7f88dbb79c9166dbb4"`);
|
||||
await queryRunner.query(`DROP TABLE "page_like"`);
|
||||
}
|
||||
|
||||
}
|
41
migration/1558103093633-UserGroup.ts
Normal file
41
migration/1558103093633-UserGroup.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class UserGroup1558103093633 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "user_group" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(256) NOT NULL, "userId" character varying(32) NOT NULL, "isPrivate" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_3c29fba6fe013ec8724378ce7c9" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_20e30aa35180e317e133d75316" ON "user_group" ("createdAt") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_3d6b372788ab01be58853003c9" ON "user_group" ("userId") `);
|
||||
await queryRunner.query(`CREATE TABLE "user_group_joining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_15f2425885253c5507e1599cfe7" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_f3a1b4bd0c7cabba958a0c0b23" ON "user_group_joining" ("userId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_67dc758bc0566985d1b3d39986" ON "user_group_joining" ("userGroupId") `);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" ADD "groupId" character varying(32)`);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" ADD "reads" character varying(32) array NOT NULL DEFAULT '{}'::varchar[]`);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" ALTER COLUMN "recipientId" DROP NOT NULL`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."recipientId" IS 'The recipient user ID.'`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_2c4be03b446884f9e9c502135b" ON "messaging_message" ("groupId") `);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_2c4be03b446884f9e9c502135be" FOREIGN KEY ("groupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group" ADD CONSTRAINT "FK_3d6b372788ab01be58853003c93" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_joining" ADD CONSTRAINT "FK_f3a1b4bd0c7cabba958a0c0b231" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_joining" ADD CONSTRAINT "FK_67dc758bc0566985d1b3d399865" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_group_joining" DROP CONSTRAINT "FK_67dc758bc0566985d1b3d399865"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_joining" DROP CONSTRAINT "FK_f3a1b4bd0c7cabba958a0c0b231"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group" DROP CONSTRAINT "FK_3d6b372788ab01be58853003c93"`);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_2c4be03b446884f9e9c502135be"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_2c4be03b446884f9e9c502135b"`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."recipientId" IS ''`);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" ALTER COLUMN "recipientId" SET NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" DROP COLUMN "reads"`);
|
||||
await queryRunner.query(`ALTER TABLE "messaging_message" DROP COLUMN "groupId"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_67dc758bc0566985d1b3d39986"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_f3a1b4bd0c7cabba958a0c0b23"`);
|
||||
await queryRunner.query(`DROP TABLE "user_group_joining"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_3d6b372788ab01be58853003c9"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_20e30aa35180e317e133d75316"`);
|
||||
await queryRunner.query(`DROP TABLE "user_group"`);
|
||||
}
|
||||
|
||||
}
|
25
migration/1558257926829-UserGroupInvite.ts
Normal file
25
migration/1558257926829-UserGroupInvite.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class UserGroupInvite1558257926829 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "user_group_invite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_3893884af0d3a5f4d01e7921a97" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_1039988afa3bf991185b277fe0" ON "user_group_invite" ("userId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_e10924607d058004304611a436" ON "user_group_invite" ("userGroupId") `);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_78787741f9010886796f2320a4" ON "user_group_invite" ("userId", "userGroupId") `);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_1039988afa3bf991185b277fe03" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_e10924607d058004304611a436a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_e10924607d058004304611a436a"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_1039988afa3bf991185b277fe03"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_d9ecaed8c6dc43f3592c229282"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_78787741f9010886796f2320a4"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_e10924607d058004304611a436"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_1039988afa3bf991185b277fe0"`);
|
||||
await queryRunner.query(`DROP TABLE "user_group_invite"`);
|
||||
}
|
||||
|
||||
}
|
13
migration/1558266512381-UserListJoining.ts
Normal file
13
migration/1558266512381-UserListJoining.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class UserListJoining1558266512381 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_90f7da835e4c10aca6853621e1" ON "user_list_joining" ("userId", "userListId") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_90f7da835e4c10aca6853621e1"`);
|
||||
}
|
||||
|
||||
}
|
35
package.json
35
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.12.0",
|
||||
"version": "11.20.0",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -12,6 +12,9 @@
|
||||
"scripts": {
|
||||
"start": "node ./index.js",
|
||||
"init": "node ./built/init.js",
|
||||
"ormconfig": "node ./built/ormconfig.js",
|
||||
"migrate": "npm run ormconfig && ts-node ./node_modules/typeorm/cli.js migration:run",
|
||||
"migrateandstart": "npm run migrate && npm run start",
|
||||
"build": "webpack && gulp build",
|
||||
"webpack": "webpack",
|
||||
"watch": "webpack --watch",
|
||||
@ -22,6 +25,10 @@
|
||||
"test": "gulp test",
|
||||
"format": "gulp format"
|
||||
},
|
||||
"resolutions": {
|
||||
"gulp-cssnano/cssnano/postcss-svgo/svgo/js-yaml": "^3.13.1",
|
||||
"video-thumbnail-generator/lodash": "^4.17.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "7.0.0-rc.2",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.15",
|
||||
@ -30,13 +37,11 @@
|
||||
"@fortawesome/free-solid-svg-icons": "5.7.2",
|
||||
"@fortawesome/vue-fontawesome": "0.1.5",
|
||||
"@koa/cors": "2.2.3",
|
||||
"@prezzemolo/zip": "0.0.3",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.5.11",
|
||||
"@types/dateformat": "3.0.0",
|
||||
"@types/deep-equal": "1.0.1",
|
||||
"@types/double-ended-queue": "2.1.0",
|
||||
"@types/file-type": "10.9.1",
|
||||
"@types/gulp": "4.0.6",
|
||||
"@types/gulp-mocha": "0.0.32",
|
||||
"@types/gulp-rename": "0.0.33",
|
||||
@ -94,17 +99,17 @@
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "3.0.1",
|
||||
"apexcharts": "3.6.9",
|
||||
"apexcharts": "3.6.12",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bootstrap-vue": "2.0.0-rc.13",
|
||||
"bull": "3.8.1",
|
||||
"bull": "3.9.1",
|
||||
"cafy": "15.1.1",
|
||||
"chai": "4.2.0",
|
||||
"chalk": "2.4.2",
|
||||
"cli-highlight": "2.1.0",
|
||||
"cli-highlight": "2.1.1",
|
||||
"commander": "2.20.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"crc-32": "1.2.0",
|
||||
@ -121,7 +126,7 @@
|
||||
"feed": "2.0.4",
|
||||
"file-type": "10.11.0",
|
||||
"fuckadblock": "3.2.1",
|
||||
"gulp": "4.0.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
"gulp-imagemin": "5.0.3",
|
||||
"gulp-mocha": "6.0.0",
|
||||
@ -140,7 +145,7 @@
|
||||
"is-root": "2.1.0",
|
||||
"is-svg": "4.1.0",
|
||||
"js-yaml": "3.13.1",
|
||||
"jsdom": "15.0.0",
|
||||
"jsdom": "15.1.0",
|
||||
"json5": "2.1.0",
|
||||
"json5-loader": "2.0.0",
|
||||
"katex": "0.10.1",
|
||||
@ -173,7 +178,7 @@
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "5.1.0",
|
||||
"parsimmon": "1.12.0",
|
||||
"pg": "7.10.0",
|
||||
"pg": "7.11.0",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"prismjs": "1.16.0",
|
||||
@ -212,7 +217,7 @@
|
||||
"summaly": "2.2.0",
|
||||
"systeminformation": "4.1.6",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.2.3",
|
||||
"terser-webpack-plugin": "1.2.4",
|
||||
"textarea-caret": "3.1.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.1.0",
|
||||
@ -245,13 +250,13 @@
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.2.15",
|
||||
"vue-template-compiler": "2.6.10",
|
||||
"vuedraggable": "2.20.0",
|
||||
"vuedraggable": "2.21.0",
|
||||
"vuewordcloud": "18.7.11",
|
||||
"vuex": "3.1.0",
|
||||
"vuex": "3.1.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.3",
|
||||
"webpack": "4.30.0",
|
||||
"webpack-cli": "3.3.1",
|
||||
"web-push": "3.3.5",
|
||||
"webpack": "4.32.2",
|
||||
"webpack-cli": "3.3.2",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "7.0.0",
|
||||
"xev": "2.0.1"
|
||||
|
@ -50,7 +50,7 @@ export async function masterMain() {
|
||||
// initialize app
|
||||
config = await init();
|
||||
|
||||
if (config.port == null) {
|
||||
if (config.port == null || Number.isNaN(config.port)) {
|
||||
bootLogger.error('The port is not configured. Please configure port.', null, true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-card>
|
||||
<template #title><fa icon="broadcast-tower"/> {{ $t('announcements') }}</template>
|
||||
<template #title><fa :icon="faBroadcastTower"/> {{ $t('announcements') }}</template>
|
||||
<section v-for="(announcement, i) in announcements" class="fit-top">
|
||||
<ui-input v-model="announcement.title" @change="save">
|
||||
<span>{{ $t('title') }}</span>
|
||||
@ -18,7 +18,7 @@
|
||||
</ui-horizon-group>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="add"><fa icon="plus"/> {{ $t('add') }}</ui-button>
|
||||
<ui-button @click="add"><fa :icon="faPlus"/> {{ $t('add') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
@ -27,12 +27,14 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { faBroadcastTower, faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/announcements.vue'),
|
||||
data() {
|
||||
return {
|
||||
announcements: [],
|
||||
faBroadcastTower, faPlus
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -14,6 +14,10 @@
|
||||
<ui-button @click="show()"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
|
||||
<ui-textarea v-if="file" :value="file | json5" readonly tall style="margin-top:16px;"></ui-textarea>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="cleanUp()"><fa :icon="faTrashAlt"/> {{ $t('clean-up') }}</ui-button>
|
||||
<ui-button @click="cleanRemoteFiles()"><fa :icon="faTrashAlt"/> {{ $t('clean-remote-files') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
@ -227,6 +231,29 @@ export default Vue.extend({
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
cleanRemoteFiles() {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('clean-remote-files-are-you-sure'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
this.$root.api('admin/drive/clean-remote-files');
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
cleanUp() {
|
||||
this.$root.api('admin/drive/cleanup');
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -54,7 +54,6 @@
|
||||
<span>{{ $t('latest-request-received-at') }}</span>
|
||||
<template #prefix><fa :icon="faInbox"/></template>
|
||||
</ui-input>
|
||||
<ui-switch v-model="instance.isBlocked" @change="updateInstance()">{{ $t('block') }}</ui-switch>
|
||||
<ui-switch v-model="instance.isMarkedAsClosed" @change="updateInstance()">{{ $t('marked-as-closed') }}</ui-switch>
|
||||
<details>
|
||||
<summary>{{ $t('charts') }}</summary>
|
||||
@ -79,6 +78,10 @@
|
||||
</ui-horizon-group>
|
||||
<div ref="chart"></div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>{{ $t('delete-all-files') }}</summary>
|
||||
<ui-button @click="deleteAllFiles()" style="margin-top: 16px;"><fa :icon="faTrashAlt"/> {{ $t('delete-all-files') }}</ui-button>
|
||||
</details>
|
||||
<details>
|
||||
<summary>{{ $t('remove-all-following') }}</summary>
|
||||
<ui-button @click="removeAllFollowing()" style="margin-top: 16px;"><fa :icon="faMinusCircle"/> {{ $t('remove-all-following') }}</ui-button>
|
||||
@ -130,7 +133,7 @@
|
||||
<span>{{ $t('status') }}</span>
|
||||
</header>
|
||||
<div v-for="instance in instances" :style="{ opacity: instance.isNotResponding ? 0.5 : 1 }">
|
||||
<a @click.prevent="showInstance(instance.host)" target="_blank" :href="`https://${instance.host}`" :style="{ textDecoration: instance.isMarkedAsClosed ? 'line-through' : 'none' }">{{ instance.host }}</a>
|
||||
<a @click.prevent="showInstance(instance.host)" rel="nofollow noopener" target="_blank" :href="`https://${instance.host}`" :style="{ textDecoration: instance.isMarkedAsClosed ? 'line-through' : 'none' }">{{ instance.host }}</a>
|
||||
<span>{{ instance.notesCount | number }}</span>
|
||||
<span>{{ instance.usersCount | number }}</span>
|
||||
<span>{{ instance.followingCount | number }}</span>
|
||||
@ -142,6 +145,16 @@
|
||||
<ui-info v-if="instances.length == limit">{{ $t('result-is-truncated', { n: limit }) }}</ui-info>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faBan"/> {{ $t('blocked-hosts') }}</template>
|
||||
<section class="fit-top">
|
||||
<ui-textarea v-model="blockedHosts">
|
||||
<template #desc>{{ $t('blocked-hosts-info') }}</template>
|
||||
</ui-textarea>
|
||||
<ui-button @click="saveBlockedHosts">{{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -149,7 +162,7 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faGlobe, faTerminal, faSearch, faMinusCircle, faServer, faCrosshairs, faEnvelopeOpenText, faUsers, faCaretDown, faCaretUp, faTrafficLight, faInbox } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTrashAlt, faBan, faGlobe, faTerminal, faSearch, faMinusCircle, faServer, faCrosshairs, faEnvelopeOpenText, faUsers, faCaretDown, faCaretUp, faTrafficLight, faInbox } from '@fortawesome/free-solid-svg-icons';
|
||||
import ApexCharts from 'apexcharts';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
|
||||
@ -176,7 +189,8 @@ export default Vue.extend({
|
||||
chartSrc: 'requests',
|
||||
chartSpan: 'hour',
|
||||
chartInstance: null,
|
||||
faGlobe, faTerminal, faSearch, faMinusCircle, faServer, faCrosshairs, faEnvelopeOpenText, faUsers, faCaretDown, faCaretUp, faPaperPlane, faTrafficLight, faInbox
|
||||
blockedHosts: '',
|
||||
faTrashAlt, faBan, faGlobe, faTerminal, faSearch, faMinusCircle, faServer, faCrosshairs, faEnvelopeOpenText, faUsers, faCaretDown, faCaretUp, faPaperPlane, faTrafficLight, faInbox
|
||||
};
|
||||
},
|
||||
|
||||
@ -246,6 +260,10 @@ export default Vue.extend({
|
||||
|
||||
mounted() {
|
||||
this.fetchInstances();
|
||||
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.blockedHosts = meta.blockedHosts.join('\n');
|
||||
});
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
@ -293,6 +311,17 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllFiles() {
|
||||
this.$root.api('admin/federation/delete-all-files', {
|
||||
host: this.instance.host
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updateInstance() {
|
||||
this.$root.api('admin/federation/update-instance', {
|
||||
host: this.instance.host,
|
||||
@ -477,6 +506,22 @@ export default Vue.extend({
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
saveBlockedHosts() {
|
||||
this.$root.api('admin/update-meta', {
|
||||
blockedHosts: this.blockedHosts.split('\n')
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('saved')
|
||||
});
|
||||
}).catch(e => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-card>
|
||||
<template #title>{{ $t('hided-tags') }}</template>
|
||||
<section>
|
||||
<textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hiddenTags"></textarea>
|
||||
<ui-button @click="save">{{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/hashtags.vue'),
|
||||
data() {
|
||||
return {
|
||||
hiddenTags: '',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.hiddenTags = meta.hiddenTags.join('\n');
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
this.$root.api('admin/update-meta', {
|
||||
hiddenTags: this.hiddenTags.split('\n')
|
||||
}).then(() => {
|
||||
//this.$root.os.apis.dialog({ text: `Saved` });
|
||||
}).catch(e => {
|
||||
//this.$root.os.apis.dialog({ text: `Failed ${e}` });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -28,7 +28,6 @@
|
||||
<li @click="nav('federation')" :class="{ active: page == 'federation' }"><fa :icon="faGlobe" fixed-width/>{{ $t('federation') }}</li>
|
||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
||||
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
|
||||
<li @click="nav('abuse')" :class="{ active: page == 'abuse' }"><fa :icon="faExclamationCircle" fixed-width/>{{ $t('abuse') }}</li>
|
||||
</ul>
|
||||
<div class="back-to-misskey">
|
||||
@ -48,7 +47,6 @@
|
||||
<div v-if="page == 'users'"><x-users/></div>
|
||||
<div v-if="page == 'emoji'"><x-emoji/></div>
|
||||
<div v-if="page == 'announcements'"><x-announcements/></div>
|
||||
<div v-if="page == 'hashtags'"><x-hashtags/></div>
|
||||
<div v-if="page == 'drive'"><x-drive/></div>
|
||||
<div v-if="page == 'federation'"><x-federation/></div>
|
||||
<div v-if="page == 'abuse'"><x-abuse/></div>
|
||||
@ -68,7 +66,6 @@ import XLogs from "./logs.vue";
|
||||
import XModerators from "./moderators.vue";
|
||||
import XEmoji from "./emoji.vue";
|
||||
import XAnnouncements from "./announcements.vue";
|
||||
import XHashtags from "./hashtags.vue";
|
||||
import XUsers from "./users.vue";
|
||||
import XDrive from "./drive.vue";
|
||||
import XAbuse from "./abuse.vue";
|
||||
@ -91,7 +88,6 @@ export default Vue.extend({
|
||||
XModerators,
|
||||
XEmoji,
|
||||
XAnnouncements,
|
||||
XHashtags,
|
||||
XUsers,
|
||||
XDrive,
|
||||
XAbuse,
|
||||
|
@ -2,140 +2,242 @@
|
||||
<div>
|
||||
<ui-card>
|
||||
<template #title><fa icon="cog"/> {{ $t('instance') }}</template>
|
||||
<section class="fit-top fit-bottom">
|
||||
<section class="fit-top">
|
||||
<ui-input :value="host" readonly>{{ $t('host') }}</ui-input>
|
||||
<ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
|
||||
<ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
|
||||
<ui-input v-model="iconUrl"><template #icon><fa icon="link"/></template>{{ $t('icon-url') }}</ui-input>
|
||||
<ui-input v-model="mascotImageUrl"><template #icon><fa icon="link"/></template>{{ $t('logo-url') }}</ui-input>
|
||||
<ui-input v-model="bannerUrl"><template #icon><fa icon="link"/></template>{{ $t('banner-url') }}</ui-input>
|
||||
<ui-input v-model="errorImageUrl"><template #icon><fa icon="link"/></template>{{ $t('error-image-url') }}</ui-input>
|
||||
<ui-input v-model="languages"><template #icon><fa icon="language"/></template>{{ $t('languages') }}<template #desc>{{ $t('languages-desc') }}</template></ui-input>
|
||||
<ui-input v-model="ToSUrl"><template #icon><fa icon="link"/></template>{{ $t('tos-url') }}</ui-input>
|
||||
<details>
|
||||
<summary>{{ $t('advanced-config') }}</summary>
|
||||
<ui-input v-model="errorImageUrl"><template #icon><fa icon="link"/></template>{{ $t('error-image-url') }}</ui-input>
|
||||
<ui-input v-model="languages"><template #icon><fa icon="language"/></template>{{ $t('languages') }}<template #desc>{{ $t('languages-desc') }}</template></ui-input>
|
||||
<ui-input v-model="repositoryUrl"><template #icon><fa icon="link"/></template>{{ $t('repository-url') }}</ui-input>
|
||||
<ui-input v-model="feedbackUrl"><template #icon><fa icon="link"/></template>{{ $t('feedback-url') }}</ui-input>
|
||||
</details>
|
||||
</section>
|
||||
<section class="fit-bottom">
|
||||
<header><fa :icon="faHeadset"/> {{ $t('maintainer-config') }}</header>
|
||||
<ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
|
||||
<ui-input v-model="maintainerEmail" type="email"><template #icon><fa :icon="farEnvelope"/></template>{{ $t('maintainer-email') }}</ui-input>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
|
||||
<ui-button v-if="disableRegistration" @click="invite">{{ $t('invite') }}</ui-button>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faPencilAlt"/> {{ $t('note-and-tl') }}</template>
|
||||
<section class="fit-top fit-bottom">
|
||||
<ui-input v-model="maxNoteTextLength">{{ $t('max-note-text-length') }}</ui-input>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
|
||||
<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
|
||||
<ui-switch v-model="disableGlobalTimeline">{{ $t('disable-global-timeline') }}</ui-switch>
|
||||
<ui-info>{{ $t('disabling-timelines-info') }}</ui-info>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="enableEmojiReaction">{{ $t('enable-emoji-reaction') }}</ui-switch>
|
||||
<ui-switch v-model="useStarForReactionFallback">{{ $t('use-star-for-reaction-fallback') }}</ui-switch>
|
||||
</section>
|
||||
<section class="fit-bottom">
|
||||
<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
|
||||
<section>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa icon="cloud"/> {{ $t('drive-config') }}</template>
|
||||
<section>
|
||||
<ui-switch v-model="useObjectStorage">{{ $t('use-object-storage') }}</ui-switch>
|
||||
<template v-if="useObjectStorage">
|
||||
<ui-info>
|
||||
<i18n path="object-storage-s3-info">
|
||||
<a href="https://docs.aws.amazon.com/general/latest/gr/rande.html" target="_blank">{{ $t('object-storage-s3-info-here') }}</a>
|
||||
</i18n>
|
||||
</ui-info>
|
||||
<ui-info>{{ $t('object-storage-gcs-info') }}</ui-info>
|
||||
<ui-input v-model="objectStorageBaseUrl" :disabled="!useObjectStorage">{{ $t('object-storage-base-url') }}</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="objectStorageBucket" :disabled="!useObjectStorage">{{ $t('object-storage-bucket') }}</ui-input>
|
||||
<ui-input v-model="objectStoragePrefix" :disabled="!useObjectStorage">{{ $t('object-storage-prefix') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-input v-model="objectStorageEndpoint" :disabled="!useObjectStorage">{{ $t('object-storage-endpoint') }}</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="objectStorageRegion" :disabled="!useObjectStorage">{{ $t('object-storage-region') }}</ui-input>
|
||||
<ui-input v-model="objectStoragePort" type="number" :disabled="!useObjectStorage">{{ $t('object-storage-port') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><fa icon="key"/></template>{{ $t('object-storage-access-key') }}</ui-input>
|
||||
<ui-input v-model="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><fa icon="key"/></template>{{ $t('object-storage-secret-key') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-switch v-model="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $t('object-storage-use-ssl') }}</ui-switch>
|
||||
</template>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<template #desc>{{ $t('cache-remote-files-desc') }}</template></ui-switch>
|
||||
</section>
|
||||
<section class="fit-top fit-bottom">
|
||||
<ui-input v-model="localDriveCapacityMb" type="number">{{ $t('local-drive-capacity-mb') }}<template #suffix>MB</template><template #desc>{{ $t('mb') }}</template></ui-input>
|
||||
<ui-input v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $t('remote-drive-capacity-mb') }}<template #suffix>MB</template><template #desc>{{ $t('mb') }}</template></ui-input>
|
||||
</section>
|
||||
<section class="fit-bottom">
|
||||
<header><fa :icon="faShieldAlt"/> {{ $t('recaptcha-config') }}</header>
|
||||
<ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch>
|
||||
<ui-info>{{ $t('recaptcha-info') }}</ui-info>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-site-key') }}</ui-input>
|
||||
<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-secret-key') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
</section>
|
||||
<section>
|
||||
<header><fa :icon="faGhost"/> {{ $t('proxy-account-config') }}</header>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faThumbtack"/> {{ $t('pinned-users') }}</template>
|
||||
<section class="fit-top">
|
||||
<ui-textarea v-model="pinnedUsers">
|
||||
<template #desc>{{ $t('pinned-users-info') }}</template>
|
||||
</ui-textarea>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faGhost"/> {{ $t('proxy-account-config') }}</template>
|
||||
<section>
|
||||
<ui-info>{{ $t('proxy-account-info') }}</ui-info>
|
||||
<ui-input v-model="proxyAccount"><template #prefix>@</template>{{ $t('proxy-account-username') }}<template #desc>{{ $t('proxy-account-username-desc') }}</template></ui-input>
|
||||
<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
|
||||
</section>
|
||||
<section>
|
||||
<header><fa :icon="farEnvelope"/> {{ $t('email-config') }}</header>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="farEnvelope"/> {{ $t('email-config') }}</template>
|
||||
<section>
|
||||
<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<template #desc>{{ $t('email-config-info') }}</template></ui-switch>
|
||||
<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input>
|
||||
<ui-input v-model="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtp-port') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-switch v-model="smtpAuth">{{ $t('smtp-auth') }}</ui-switch>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="smtpUser" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-user') }}</ui-input>
|
||||
<ui-input v-model="smtpPass" type="password" :withPasswordToggle="true" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-pass') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch>
|
||||
<template v-if="enableEmail">
|
||||
<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input>
|
||||
<ui-input v-model="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtp-port') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-switch v-model="smtpAuth">{{ $t('smtp-auth') }}</ui-switch>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="smtpUser" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-user') }}</ui-input>
|
||||
<ui-input v-model="smtpPass" type="password" :with-password-toggle="true" :disabled="!enableEmail || !smtpAuth">{{ $t('smtp-pass') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch>
|
||||
</template>
|
||||
</section>
|
||||
<section>
|
||||
<header><fa :icon="faBolt"/> {{ $t('serviceworker-config') }}</header>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faBolt"/> {{ $t('serviceworker-config') }}</template>
|
||||
<section>
|
||||
<ui-switch v-model="enableServiceWorker">{{ $t('enable-serviceworker') }}<template #desc>{{ $t('serviceworker-info') }}</template></ui-switch>
|
||||
<ui-info>{{ $t('vapid-info') }}<br><code>npm i web-push -g<br>web-push generate-vapid-keys</code></ui-info>
|
||||
<ui-horizon-group inputs class="fit-bottom">
|
||||
<ui-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-publickey') }}</ui-input>
|
||||
<ui-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-privatekey') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<template v-if="enableServiceWorker">
|
||||
<ui-info>{{ $t('vapid-info') }}<br><code>npm i web-push -g<br>web-push generate-vapid-keys</code></ui-info>
|
||||
<ui-horizon-group inputs class="fit-bottom">
|
||||
<ui-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-publickey') }}</ui-input>
|
||||
<ui-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-privatekey') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
</template>
|
||||
</section>
|
||||
<section>
|
||||
<header>summaly Proxy</header>
|
||||
<ui-input v-model="summalyProxy">URL</ui-input>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title>{{ $t('pinned-users') }}</template>
|
||||
<template #title><fa :icon="faShieldAlt"/> {{ $t('recaptcha-config') }}</template>
|
||||
<section :class="enableRecaptcha ? 'fit-bottom' : ''">
|
||||
<ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch>
|
||||
<template v-if="enableRecaptcha">
|
||||
<ui-info>{{ $t('recaptcha-info') }}</ui-info>
|
||||
<ui-info warn>{{ $t('recaptcha-info2') }}</ui-info>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-site-key') }}</ui-input>
|
||||
<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-secret-key') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
</template>
|
||||
</section>
|
||||
<section v-if="enableRecaptcha && recaptchaSiteKey">
|
||||
<header>{{ $t('recaptcha-preview') }}</header>
|
||||
<div ref="recaptcha" style="margin: 16px 0 0 0;" :key="recaptchaSiteKey"></div>
|
||||
</section>
|
||||
<section>
|
||||
<ui-textarea v-model="pinnedUsers"></ui-textarea>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title>{{ $t('invite') }}</template>
|
||||
<section>
|
||||
<ui-button @click="invite">{{ $t('invite') }}</ui-button>
|
||||
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</template>
|
||||
<template #title><fa :icon="faShieldAlt"/> {{ $t('external-service-integration-config') }}</template>
|
||||
<section>
|
||||
<header><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</header>
|
||||
<ui-switch v-model="enableTwitterIntegration">{{ $t('enable-twitter-integration') }}</ui-switch>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-key') }}</ui-input>
|
||||
<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-secret') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-info>{{ $t('twitter-integration-info', { url: `${url}/api/tw/cb` }) }}</ui-info>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
<template v-if="enableTwitterIntegration">
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-key') }}</ui-input>
|
||||
<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-secret') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-info>{{ $t('twitter-integration-info', { url: `${url}/api/tw/cb` }) }}</ui-info>
|
||||
</template>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</template>
|
||||
<section>
|
||||
<header><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</header>
|
||||
<ui-switch v-model="enableGithubIntegration">{{ $t('enable-github-integration') }}</ui-switch>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-id') }}</ui-input>
|
||||
<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-secret') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-info>{{ $t('github-integration-info', { url: `${url}/api/gh/cb` }) }}</ui-info>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
<template v-if="enableGithubIntegration">
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-id') }}</ui-input>
|
||||
<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-secret') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-info>{{ $t('github-integration-info', { url: `${url}/api/gh/cb` }) }}</ui-info>
|
||||
</template>
|
||||
</section>
|
||||
<section>
|
||||
<header><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</header>
|
||||
<ui-switch v-model="enableDiscordIntegration">{{ $t('enable-discord-integration') }}</ui-switch>
|
||||
<template v-if="enableDiscordIntegration">
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-id') }}</ui-input>
|
||||
<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-secret') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-info>{{ $t('discord-integration-info', { url: `${url}/api/dc/cb` }) }}</ui-info>
|
||||
</template>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</template>
|
||||
<section>
|
||||
<ui-switch v-model="enableDiscordIntegration">{{ $t('enable-discord-integration') }}</ui-switch>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-id') }}</ui-input>
|
||||
<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-secret') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-info>{{ $t('discord-integration-info', { url: `${url}/api/dc/cb` }) }}</ui-info>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
<details>
|
||||
<summary style="color:var(--text);">{{ $t('advanced-config') }}</summary>
|
||||
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faHashtag"/> {{ $t('hidden-tags') }}</template>
|
||||
<section class="fit-top">
|
||||
<ui-textarea v-model="hiddenTags">
|
||||
<template #desc>{{ $t('hidden-tags-info') }}</template>
|
||||
</ui-textarea>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title>summaly Proxy</template>
|
||||
<section class="fit-top fit-bottom">
|
||||
<ui-input v-model="summalyProxy">URL</ui-input>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="updateMeta"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</details>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -144,8 +246,8 @@ import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { url, host } from '../../config';
|
||||
import { toUnicode } from 'punycode';
|
||||
import { faHeadset, faShieldAlt, faGhost, faUserPlus, faBolt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faEnvelope as farEnvelope } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faHeadset, faShieldAlt, faGhost, faUserPlus, faBolt, faThumbtack, faPencilAlt, faHashtag } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faEnvelope as farEnvelope, faSave } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/instance.vue'),
|
||||
@ -156,6 +258,9 @@ export default Vue.extend({
|
||||
host: toUnicode(host),
|
||||
maintainerName: null,
|
||||
maintainerEmail: null,
|
||||
ToSUrl: null,
|
||||
repositoryUrl: "https://github.com/syuilo/misskey",
|
||||
feedbackUrl: null,
|
||||
disableRegistration: false,
|
||||
disableLocalTimeline: false,
|
||||
disableGlobalTimeline: false,
|
||||
@ -185,7 +290,6 @@ export default Vue.extend({
|
||||
discordClientId: null,
|
||||
discordClientSecret: null,
|
||||
proxyAccount: null,
|
||||
inviteCode: null,
|
||||
summalyProxy: null,
|
||||
enableEmail: false,
|
||||
email: null,
|
||||
@ -198,8 +302,19 @@ export default Vue.extend({
|
||||
enableServiceWorker: false,
|
||||
swPublicKey: null,
|
||||
swPrivateKey: null,
|
||||
pinnedUsers: [],
|
||||
faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt
|
||||
pinnedUsers: '',
|
||||
hiddenTags: '',
|
||||
useObjectStorage: false,
|
||||
objectStorageBaseUrl: null,
|
||||
objectStorageBucket: null,
|
||||
objectStoragePrefix: null,
|
||||
objectStorageEndpoint: null,
|
||||
objectStorageRegion: null,
|
||||
objectStoragePort: null,
|
||||
objectStorageAccessKey: null,
|
||||
objectStorageSecretKey: null,
|
||||
objectStorageUseSSL: false,
|
||||
faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt, faThumbtack, faPencilAlt, faSave, faHashtag
|
||||
};
|
||||
},
|
||||
|
||||
@ -207,6 +322,9 @@ export default Vue.extend({
|
||||
this.$root.getMeta(true).then(meta => {
|
||||
this.maintainerName = meta.maintainerName;
|
||||
this.maintainerEmail = meta.maintainerEmail;
|
||||
this.ToSUrl = meta.ToSUrl;
|
||||
this.repositoryUrl = meta.repositoryUrl;
|
||||
this.feedbackUrl = meta.feedbackUrl;
|
||||
this.disableRegistration = meta.disableRegistration;
|
||||
this.disableLocalTimeline = meta.disableLocalTimeline;
|
||||
this.disableGlobalTimeline = meta.disableGlobalTimeline;
|
||||
@ -249,13 +367,55 @@ export default Vue.extend({
|
||||
this.swPublicKey = meta.swPublickey;
|
||||
this.swPrivateKey = meta.swPrivateKey;
|
||||
this.pinnedUsers = meta.pinnedUsers.join('\n');
|
||||
this.hiddenTags = meta.hiddenTags.join('\n');
|
||||
this.useObjectStorage = meta.useObjectStorage;
|
||||
this.objectStorageBaseUrl = meta.objectStorageBaseUrl;
|
||||
this.objectStorageBucket = meta.objectStorageBucket;
|
||||
this.objectStoragePrefix = meta.objectStoragePrefix;
|
||||
this.objectStorageEndpoint = meta.objectStorageEndpoint;
|
||||
this.objectStorageRegion = meta.objectStorageRegion;
|
||||
this.objectStoragePort = meta.objectStoragePort;
|
||||
this.objectStorageAccessKey = meta.objectStorageAccessKey;
|
||||
this.objectStorageSecretKey = meta.objectStorageSecretKey;
|
||||
this.objectStorageUseSSL = meta.objectStorageUseSSL;
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const renderRecaptchaPreview = () => {
|
||||
if (!(window as any).grecaptcha) return;
|
||||
if (!this.$refs.recaptcha) return;
|
||||
if (!this.recaptchaSiteKey) return;
|
||||
(window as any).grecaptcha.render(this.$refs.recaptcha, {
|
||||
sitekey: this.recaptchaSiteKey
|
||||
});
|
||||
};
|
||||
|
||||
window.onRecaotchaLoad = () => {
|
||||
renderRecaptchaPreview();
|
||||
};
|
||||
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('src', 'https://www.google.com/recaptcha/api.js?onload=onRecaotchaLoad');
|
||||
head.appendChild(script);
|
||||
|
||||
this.$watch('enableRecaptcha', () => {
|
||||
renderRecaptchaPreview();
|
||||
});
|
||||
|
||||
this.$watch('recaptchaSiteKey', () => {
|
||||
renderRecaptchaPreview();
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
invite() {
|
||||
this.$root.api('admin/invite').then(x => {
|
||||
this.inviteCode = x.code;
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: x.code
|
||||
});
|
||||
}).catch(e => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
@ -268,6 +428,9 @@ export default Vue.extend({
|
||||
this.$root.api('admin/update-meta', {
|
||||
maintainerName: this.maintainerName,
|
||||
maintainerEmail: this.maintainerEmail,
|
||||
ToSUrl: this.ToSUrl,
|
||||
repositoryUrl: this.repositoryUrl,
|
||||
feedbackUrl: this.feedbackUrl,
|
||||
disableRegistration: this.disableRegistration,
|
||||
disableLocalTimeline: this.disableLocalTimeline,
|
||||
disableGlobalTimeline: this.disableGlobalTimeline,
|
||||
@ -308,7 +471,18 @@ export default Vue.extend({
|
||||
enableServiceWorker: this.enableServiceWorker,
|
||||
swPublicKey: this.swPublicKey,
|
||||
swPrivateKey: this.swPrivateKey,
|
||||
pinnedUsers: this.pinnedUsers.split('\n')
|
||||
pinnedUsers: this.pinnedUsers.split('\n'),
|
||||
hiddenTags: this.hiddenTags.split('\n'),
|
||||
useObjectStorage: this.useObjectStorage,
|
||||
objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null,
|
||||
objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null,
|
||||
objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null,
|
||||
objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null,
|
||||
objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null,
|
||||
objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null,
|
||||
objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null,
|
||||
objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null,
|
||||
objectStorageUseSSL: this.objectStorageUseSSL,
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
|
@ -26,6 +26,8 @@
|
||||
</details>
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<ui-button @click="deleteAll()">{{ $t('delete-all') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
@ -78,6 +80,15 @@ export default Vue.extend({
|
||||
}).then(logs => {
|
||||
this.logs = logs.reverse();
|
||||
});
|
||||
},
|
||||
|
||||
deleteAll() {
|
||||
this.$root.api('admin/delete-logs').then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
181
src/client/app/admin/views/queue.chart.vue
Normal file
181
src/client/app/admin/views/queue.chart.vue
Normal file
@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-info warn v-if="latestStats && latestStats.waiting > 0">The queue is jammed.</ui-info>
|
||||
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
||||
<ui-input :value="latestStats.activeSincePrevTick | number" type="text" readonly>
|
||||
<span>Process</span>
|
||||
<template #prefix><fa :icon="fasPlayCircle"/></template>
|
||||
<template #suffix>jobs/tick</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.active | number" type="text" readonly>
|
||||
<span>Active</span>
|
||||
<template #prefix><fa :icon="farPlayCircle"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.waiting | number" type="text" readonly>
|
||||
<span>Waiting</span>
|
||||
<template #prefix><fa :icon="faStopCircle"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.delayed | number" type="text" readonly>
|
||||
<span>Delayed</span>
|
||||
<template #prefix><fa :icon="faStopwatch"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<div ref="chart" class="wptihjuy"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import ApexCharts from 'apexcharts';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import { faStopwatch, faPlayCircle as fasPlayCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faStopCircle, faPlayCircle as farPlayCircle } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/queue.vue'),
|
||||
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
connection: {
|
||||
required: true
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
stats: [],
|
||||
chart: null,
|
||||
faStopwatch, faStopCircle, farPlayCircle, fasPlayCircle
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
latestStats(): any {
|
||||
return this.stats.length > 0 ? this.stats[this.stats.length - 1][this.type] : null;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
stats(stats) {
|
||||
this.chart.updateSeries([{
|
||||
name: 'Process',
|
||||
type: 'area',
|
||||
data: stats.map((x, i) => ({ x: i, y: x[this.type].activeSincePrevTick }))
|
||||
}, {
|
||||
name: 'Active',
|
||||
type: 'area',
|
||||
data: stats.map((x, i) => ({ x: i, y: x[this.type].active }))
|
||||
}, {
|
||||
name: 'Waiting',
|
||||
type: 'line',
|
||||
data: stats.map((x, i) => ({ x: i, y: x[this.type].waiting }))
|
||||
}, {
|
||||
name: 'Delayed',
|
||||
type: 'line',
|
||||
data: stats.map((x, i) => ({ x: i, y: x[this.type].delayed }))
|
||||
}]);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.chart = new ApexCharts(this.$refs.chart, {
|
||||
chart: {
|
||||
id: this.type,
|
||||
group: 'queue',
|
||||
type: 'area',
|
||||
height: 200,
|
||||
animations: {
|
||||
dynamicAnimation: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
grid: {
|
||||
clipMarkers: false,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)',
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
width: 2
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
},
|
||||
},
|
||||
series: [] as any,
|
||||
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
|
||||
xaxis: {
|
||||
type: 'numeric',
|
||||
labels: {
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
show: false,
|
||||
min: 0,
|
||||
}
|
||||
});
|
||||
|
||||
this.chart.render();
|
||||
|
||||
this.connection.on('stats', this.onStats);
|
||||
this.connection.on('statsLog', this.onStatsLog);
|
||||
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
if (this.chart) this.chart.destroy();
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
onStats(stats) {
|
||||
this.stats.push(stats);
|
||||
if (this.stats.length > this.limit) this.stats.shift();
|
||||
},
|
||||
|
||||
onStatsLog(statsLog) {
|
||||
for (const stats of statsLog.reverse()) {
|
||||
this.onStats(stats);
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.wptihjuy
|
||||
min-height 200px !important
|
||||
margin -8px
|
||||
|
||||
</style>
|
@ -2,59 +2,27 @@
|
||||
<div>
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faChartBar"/> {{ $t('title') }}</template>
|
||||
<section class="wptihjuy">
|
||||
<header><fa :icon="faPaperPlane"/> Deliver</header>
|
||||
<ui-info warn v-if="latestStats && latestStats.deliver.waiting > 0">The queue is jammed.</ui-info>
|
||||
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
||||
<ui-input :value="latestStats.deliver.activeSincePrevTick | number" type="text" readonly>
|
||||
<span>Process</span>
|
||||
<template #prefix><fa :icon="fasPlayCircle"/></template>
|
||||
<template #suffix>jobs/tick</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.deliver.active | number" type="text" readonly>
|
||||
<span>Active</span>
|
||||
<template #prefix><fa :icon="farPlayCircle"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.deliver.waiting | number" type="text" readonly>
|
||||
<span>Waiting</span>
|
||||
<template #prefix><fa :icon="faStopCircle"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.deliver.delayed | number" type="text" readonly>
|
||||
<span>Delayed</span>
|
||||
<template #prefix><fa :icon="faStopwatch"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<div ref="deliverChart" class="chart"></div>
|
||||
<section>
|
||||
<header><fa :icon="faPaperPlane"/> {{ $t('domains.deliver') }}</header>
|
||||
<x-chart v-if="connection" :connection="connection" :limit="chartLimit" type="deliver"/>
|
||||
</section>
|
||||
<section class="wptihjuy">
|
||||
<header><fa :icon="faInbox"/> Inbox</header>
|
||||
<ui-info warn v-if="latestStats && latestStats.inbox.waiting > 0">The queue is jammed.</ui-info>
|
||||
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
||||
<ui-input :value="latestStats.inbox.activeSincePrevTick | number" type="text" readonly>
|
||||
<span>Process</span>
|
||||
<template #prefix><fa :icon="fasPlayCircle"/></template>
|
||||
<template #suffix>jobs/tick</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.inbox.active | number" type="text" readonly>
|
||||
<span>Active</span>
|
||||
<template #prefix><fa :icon="farPlayCircle"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.inbox.waiting | number" type="text" readonly>
|
||||
<span>Waiting</span>
|
||||
<template #prefix><fa :icon="faStopCircle"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
<ui-input :value="latestStats.inbox.delayed | number" type="text" readonly>
|
||||
<span>Delayed</span>
|
||||
<template #prefix><fa :icon="faStopwatch"/></template>
|
||||
<template #suffix>jobs</template>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<div ref="inboxChart" class="chart"></div>
|
||||
<section>
|
||||
<header><fa :icon="faInbox"/> {{ $t('domains.inbox') }}</header>
|
||||
<x-chart v-if="connection" :connection="connection" :limit="chartLimit" type="inbox"/>
|
||||
</section>
|
||||
<section>
|
||||
<details>
|
||||
<summary>{{ $t('other-queues') }}</summary>
|
||||
<section>
|
||||
<header><fa :icon="faDatabase"/> {{ $t('domains.db') }}</header>
|
||||
<x-chart v-if="connection" :connection="connection" :limit="chartLimit" type="db"/>
|
||||
</section>
|
||||
<ui-hr/>
|
||||
<section>
|
||||
<header><fa :icon="faCloud"/> {{ $t('domains.objectStorage') }}</header>
|
||||
<x-chart v-if="connection" :connection="connection" :limit="chartLimit" type="objectStorage"/>
|
||||
</section>
|
||||
</details>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>
|
||||
@ -69,9 +37,13 @@
|
||||
<template #label>{{ $t('queue') }}</template>
|
||||
<option value="deliver">{{ $t('domains.deliver') }}</option>
|
||||
<option value="inbox">{{ $t('domains.inbox') }}</option>
|
||||
<option value="db">{{ $t('domains.db') }}</option>
|
||||
<option value="objectStorage">{{ $t('domains.objectStorage') }}</option>
|
||||
</ui-select>
|
||||
<ui-select v-model="state">
|
||||
<template #label>{{ $t('state') }}</template>
|
||||
<option value="active">{{ $t('states.active') }}</option>
|
||||
<option value="waiting">{{ $t('states.waiting') }}</option>
|
||||
<option value="delayed">{{ $t('states.delayed') }}</option>
|
||||
</ui-select>
|
||||
</ui-horizon-group>
|
||||
@ -94,74 +66,31 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faTasks, faInbox, faDatabase, faCloud } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faPaperPlane, faChartBar } from '@fortawesome/free-regular-svg-icons';
|
||||
import i18n from '../../i18n';
|
||||
import ApexCharts from 'apexcharts';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import { faTasks, faInbox, faStopwatch, faPlayCircle as fasPlayCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faPaperPlane, faStopCircle, faPlayCircle as farPlayCircle, faChartBar } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
const limit = 200;
|
||||
import XChart from './queue.chart.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/queue.vue'),
|
||||
|
||||
components: {
|
||||
XChart
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
stats: [],
|
||||
deliverChart: null,
|
||||
inboxChart: null,
|
||||
connection: null,
|
||||
chartLimit: 200,
|
||||
jobs: [],
|
||||
jobsLimit: 50,
|
||||
domain: 'deliver',
|
||||
state: 'delayed',
|
||||
faTasks, faPaperPlane, faInbox, faStopwatch, faStopCircle, farPlayCircle, fasPlayCircle, faChartBar
|
||||
faTasks, faPaperPlane, faInbox, faChartBar, faDatabase, faCloud
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
latestStats(): any {
|
||||
return this.stats[this.stats.length - 1];
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
stats(stats) {
|
||||
this.inboxChart.updateSeries([{
|
||||
name: 'Process',
|
||||
type: 'area',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
|
||||
}, {
|
||||
name: 'Active',
|
||||
type: 'area',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
|
||||
}, {
|
||||
name: 'Waiting',
|
||||
type: 'line',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
|
||||
}, {
|
||||
name: 'Delayed',
|
||||
type: 'line',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
|
||||
}]);
|
||||
this.deliverChart.updateSeries([{
|
||||
name: 'Process',
|
||||
type: 'area',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
|
||||
}, {
|
||||
name: 'Active',
|
||||
type: 'area',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
|
||||
}, {
|
||||
name: 'Waiting',
|
||||
type: 'line',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
|
||||
}, {
|
||||
name: 'Delayed',
|
||||
type: 'line',
|
||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
|
||||
}]);
|
||||
},
|
||||
|
||||
domain() {
|
||||
this.jobs = [];
|
||||
this.fetchJobs();
|
||||
@ -176,83 +105,14 @@ export default Vue.extend({
|
||||
mounted() {
|
||||
this.fetchJobs();
|
||||
|
||||
const chartOpts = id => ({
|
||||
chart: {
|
||||
id,
|
||||
group: 'queue',
|
||||
type: 'area',
|
||||
height: 200,
|
||||
animations: {
|
||||
dynamicAnimation: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
grid: {
|
||||
clipMarkers: false,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)',
|
||||
xaxis: {
|
||||
lines: {
|
||||
show: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
width: 2
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
},
|
||||
},
|
||||
series: [] as any,
|
||||
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
|
||||
xaxis: {
|
||||
type: 'numeric',
|
||||
labels: {
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
show: false,
|
||||
min: 0,
|
||||
}
|
||||
});
|
||||
|
||||
this.inboxChart = new ApexCharts(this.$refs.inboxChart, chartOpts('a'));
|
||||
this.deliverChart = new ApexCharts(this.$refs.deliverChart, chartOpts('b'));
|
||||
|
||||
this.inboxChart.render();
|
||||
this.deliverChart.render();
|
||||
|
||||
const connection = this.$root.stream.useSharedConnection('queueStats');
|
||||
connection.on('stats', this.onStats);
|
||||
connection.on('statsLog', this.onStatsLog);
|
||||
connection.send('requestLog', {
|
||||
this.connection = this.$root.stream.useSharedConnection('queueStats');
|
||||
this.connection.send('requestLog', {
|
||||
id: Math.random().toString().substr(2, 8),
|
||||
length: limit
|
||||
length: this.chartLimit
|
||||
});
|
||||
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
connection.dispose();
|
||||
this.inboxChart.destroy();
|
||||
this.deliverChart.destroy();
|
||||
this.connection.dispose();
|
||||
});
|
||||
},
|
||||
|
||||
@ -274,17 +134,6 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
onStats(stats) {
|
||||
this.stats.push(stats);
|
||||
if (this.stats.length > limit) this.stats.shift();
|
||||
},
|
||||
|
||||
onStatsLog(statsLog) {
|
||||
for (const stats of statsLog.reverse()) {
|
||||
this.onStats(stats);
|
||||
}
|
||||
},
|
||||
|
||||
fetchJobs() {
|
||||
this.$root.api('admin/queue/jobs', {
|
||||
domain: this.domain,
|
||||
@ -299,11 +148,6 @@ export default Vue.extend({
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.wptihjuy
|
||||
> .chart
|
||||
min-height 200px !important
|
||||
margin 0 -8px
|
||||
|
||||
.xvvuvgsv
|
||||
> b
|
||||
margin-right 16px
|
||||
|
@ -9,8 +9,9 @@
|
||||
<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
|
||||
|
||||
<div class="user" v-if="user">
|
||||
<x-user :user='user'/>
|
||||
<x-user :user="user"/>
|
||||
<div class="actions">
|
||||
<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button>
|
||||
<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="silenceUser"><fa :icon="faMicrophoneSlash"/> {{ $t('make-silence') }}</ui-button>
|
||||
@ -20,7 +21,7 @@
|
||||
<ui-button @click="suspendUser" :disabled="suspending"><fa :icon="faSnowflake"/> {{ $t('suspend') }}</ui-button>
|
||||
<ui-button @click="unsuspendUser" :disabled="unsuspending">{{ $t('unsuspend') }}</ui-button>
|
||||
</ui-horizon-group>
|
||||
<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button>
|
||||
<ui-button @click="deleteAllFiles"><fa :icon="faTrashAlt"/> {{ $t('delete-all-files') }}</ui-button>
|
||||
<ui-textarea v-if="user" :value="user | json5" readonly tall style="margin-top:16px;"></ui-textarea>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,7 +68,7 @@ import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import parseAcct from "../../../../misc/acct/parse";
|
||||
import { faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faSnowflake, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
import XUser from './users.user.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
@ -88,7 +89,7 @@ export default Vue.extend({
|
||||
offset: 0,
|
||||
users: [],
|
||||
existMore: false,
|
||||
faTerminal, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
|
||||
faTerminal, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash, faTrashAlt
|
||||
};
|
||||
},
|
||||
|
||||
@ -277,6 +278,25 @@ export default Vue.extend({
|
||||
this.refreshUser();
|
||||
},
|
||||
|
||||
async deleteAllFiles() {
|
||||
if (!await this.getConfirmed(this.$t('delete-all-files-confirm'))) return;
|
||||
|
||||
const process = async () => {
|
||||
await this.$root.api('admin/delete-all-files-of-a-user', { userId: this.user.id });
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async getConfirmed(text: string): Promise<Boolean> {
|
||||
const confirm = await this.$root.dialog({
|
||||
type: 'warning',
|
||||
|
@ -144,8 +144,6 @@ export default prop => ({
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.$emit(`update:${prop}`, this.$_ns_note_);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
171
src/client/app/common/scripts/paging.ts
Normal file
171
src/client/app/common/scripts/paging.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
export default (opts) => ({
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
queue: [],
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
inited: false,
|
||||
more: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
empty(): boolean {
|
||||
return this.items.length == 0 && !this.fetching && this.inited;
|
||||
},
|
||||
|
||||
error(): boolean {
|
||||
return !this.fetching && !this.inited;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
queue(x) {
|
||||
if (opts.onQueueChanged) opts.onQueueChanged(this, x);
|
||||
},
|
||||
|
||||
pagination() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
opts.displayLimit = opts.displayLimit || 30;
|
||||
this.init();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (opts.captureWindowScroll) {
|
||||
this.isScrollTop = () => {
|
||||
return window.scrollY <= 8;
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', this.onWindowScroll, { passive: true });
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (opts.captureWindowScroll) {
|
||||
window.removeEventListener('scroll', this.onWindowScroll);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateItem(i, item) {
|
||||
Vue.set((this as any).items, i, item);
|
||||
},
|
||||
|
||||
reload() {
|
||||
this.queue = [];
|
||||
this.items = [];
|
||||
this.init();
|
||||
},
|
||||
|
||||
async init() {
|
||||
this.fetching = true;
|
||||
let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;
|
||||
if (params && params.then) params = await params;
|
||||
await this.$root.api(this.pagination.endpoint, {
|
||||
limit: (this.pagination.limit || 10) + 1,
|
||||
...params
|
||||
}).then(x => {
|
||||
if (x.length == (this.pagination.limit || 10) + 1) {
|
||||
x.pop();
|
||||
this.items = x;
|
||||
this.more = true;
|
||||
} else {
|
||||
this.items = x;
|
||||
this.more = false;
|
||||
}
|
||||
this.inited = true;
|
||||
this.fetching = false;
|
||||
if (opts.onInited) opts.onInited(this);
|
||||
}, e => {
|
||||
this.fetching = false;
|
||||
if (opts.onInited) opts.onInited(this);
|
||||
});
|
||||
},
|
||||
|
||||
async fetchMore() {
|
||||
if (!this.more || this.moreFetching || this.items.length === 0) return;
|
||||
this.moreFetching = true;
|
||||
let params = typeof this.pagination.params === 'function' ? this.pagination.params(false) : this.pagination.params;
|
||||
if (params && params.then) params = await params;
|
||||
await this.$root.api(this.pagination.endpoint, {
|
||||
limit: (this.pagination.limit || 10) + 1,
|
||||
untilId: this.items[this.items.length - 1].id,
|
||||
...params
|
||||
}).then(x => {
|
||||
if (x.length == (this.pagination.limit || 10) + 1) {
|
||||
x.pop();
|
||||
this.items = this.items.concat(x);
|
||||
this.more = true;
|
||||
} else {
|
||||
this.items = this.items.concat(x);
|
||||
this.more = false;
|
||||
}
|
||||
this.moreFetching = false;
|
||||
}, e => {
|
||||
this.moreFetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
prepend(item, silent = false) {
|
||||
if (opts.onPrepend) {
|
||||
const cancel = opts.onPrepend(this, item, silent);
|
||||
if (cancel) return;
|
||||
}
|
||||
|
||||
if (this.isScrollTop == null || this.isScrollTop()) {
|
||||
// Prepend the item
|
||||
this.items.unshift(item);
|
||||
|
||||
// オーバーフローしたら古い投稿は捨てる
|
||||
if (this.items.length >= opts.displayLimit) {
|
||||
this.items = this.items.slice(0, opts.displayLimit);
|
||||
this.more = true;
|
||||
}
|
||||
} else {
|
||||
this.queue.push(item);
|
||||
}
|
||||
},
|
||||
|
||||
append(item) {
|
||||
this.items.push(item);
|
||||
},
|
||||
|
||||
releaseQueue() {
|
||||
for (const n of this.queue) {
|
||||
this.prepend(n, true);
|
||||
}
|
||||
this.queue = [];
|
||||
},
|
||||
|
||||
onWindowScroll() {
|
||||
if (this.isScrollTop()) {
|
||||
this.onTop();
|
||||
}
|
||||
|
||||
if (this.$store.state.settings.fetchOnScroll) {
|
||||
// 親要素が display none だったら弾く
|
||||
// https://github.com/syuilo/misskey/issues/1569
|
||||
// http://d.hatena.ne.jp/favril/20091105/1257403319
|
||||
if (this.$el.offsetHeight == 0) return;
|
||||
|
||||
const current = window.scrollY + window.innerHeight;
|
||||
if (current > document.body.offsetHeight - 8) this.onBottom();
|
||||
}
|
||||
},
|
||||
|
||||
onTop() {
|
||||
this.releaseQueue();
|
||||
},
|
||||
|
||||
onBottom() {
|
||||
this.fetchMore();
|
||||
}
|
||||
}
|
||||
});
|
478
src/client/app/common/scripts/post-form.ts
Normal file
478
src/client/app/common/scripts/post-form.ts
Normal file
@ -0,0 +1,478 @@
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import { length } from 'stringz';
|
||||
import { toASCII } from 'punycode';
|
||||
import MkVisibilityChooser from '../views/components/visibility-chooser.vue';
|
||||
import getFace from './get-face';
|
||||
import { parse } from '../../../../mfm/parse';
|
||||
import { host, url } from '../../config';
|
||||
import i18n from '../../i18n';
|
||||
import { erase, unique } from '../../../../prelude/array';
|
||||
import extractMentions from '../../../../misc/extract-mentions';
|
||||
|
||||
export default (opts) => ({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XPostFormAttaches: () => import('../views/components/post-form-attaches.vue').then(m => m.default),
|
||||
XPollEditor: () => import('../views/components/poll-editor.vue').then(m => m.default)
|
||||
},
|
||||
|
||||
props: {
|
||||
reply: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
renote: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
mention: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
initialText: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
instant: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
posting: false,
|
||||
text: '',
|
||||
files: [],
|
||||
uploadings: [],
|
||||
poll: false,
|
||||
pollChoices: [],
|
||||
pollMultiple: false,
|
||||
pollExpiration: [],
|
||||
useCw: false,
|
||||
cw: null,
|
||||
geo: null,
|
||||
visibility: 'public',
|
||||
visibleUsers: [],
|
||||
localOnly: false,
|
||||
autocomplete: null,
|
||||
draghover: false,
|
||||
quoteId: null,
|
||||
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
|
||||
maxNoteTextLength: 1000
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
draftId(): string {
|
||||
return this.renote
|
||||
? `renote:${this.renote.id}`
|
||||
: this.reply
|
||||
? `reply:${this.reply.id}`
|
||||
: 'note';
|
||||
},
|
||||
|
||||
placeholder(): string {
|
||||
const xs = [
|
||||
this.$t('@.note-placeholders.a'),
|
||||
this.$t('@.note-placeholders.b'),
|
||||
this.$t('@.note-placeholders.c'),
|
||||
this.$t('@.note-placeholders.d'),
|
||||
this.$t('@.note-placeholders.e'),
|
||||
this.$t('@.note-placeholders.f')
|
||||
];
|
||||
const x = xs[Math.floor(Math.random() * xs.length)];
|
||||
|
||||
return this.renote
|
||||
? opts.mobile ? this.$t('@.post-form.option-quote-placeholder') : this.$t('@.post-form.quote-placeholder')
|
||||
: this.reply
|
||||
? this.$t('@.post-form.reply-placeholder')
|
||||
: x;
|
||||
},
|
||||
|
||||
submitText(): string {
|
||||
return this.renote
|
||||
? this.$t('@.post-form.renote')
|
||||
: this.reply
|
||||
? this.$t('@.post-form.reply')
|
||||
: this.$t('@.post-form.submit');
|
||||
},
|
||||
|
||||
canPost(): boolean {
|
||||
return !this.posting &&
|
||||
(1 <= this.text.length || 1 <= this.files.length || this.poll || this.renote) &&
|
||||
(length(this.text.trim()) <= this.maxNoteTextLength) &&
|
||||
(!this.poll || this.pollChoices.length >= 2);
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.maxNoteTextLength = meta.maxNoteTextLength;
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.initialText) {
|
||||
this.text = this.initialText;
|
||||
}
|
||||
|
||||
if (this.mention) {
|
||||
this.text = this.mention.host ? `@${this.mention.username}@${toASCII(this.mention.host)}` : `@${this.mention.username}`;
|
||||
this.text += ' ';
|
||||
}
|
||||
|
||||
if (this.reply && this.reply.user.host != null) {
|
||||
this.text = `@${this.reply.user.username}@${toASCII(this.reply.user.host)} `;
|
||||
}
|
||||
|
||||
if (this.reply && this.reply.text != null) {
|
||||
const ast = parse(this.reply.text);
|
||||
|
||||
for (const x of extractMentions(ast)) {
|
||||
const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : `@${x.username}`;
|
||||
|
||||
// 自分は除外
|
||||
if (this.$store.state.i.username == x.username && x.host == null) continue;
|
||||
if (this.$store.state.i.username == x.username && x.host == host) continue;
|
||||
|
||||
// 重複は除外
|
||||
if (this.text.indexOf(`${mention} `) != -1) continue;
|
||||
|
||||
this.text += `${mention} `;
|
||||
}
|
||||
}
|
||||
|
||||
// デフォルト公開範囲
|
||||
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? (this.$store.state.device.visibility || this.$store.state.settings.defaultNoteVisibility) : this.$store.state.settings.defaultNoteVisibility);
|
||||
|
||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||
if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {
|
||||
this.visibility = this.reply.visibility;
|
||||
}
|
||||
|
||||
if (this.reply) {
|
||||
this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
|
||||
this.visibleUsers.push(user);
|
||||
});
|
||||
}
|
||||
|
||||
// keep cw when reply
|
||||
if (this.$store.state.settings.keepCw && this.reply && this.reply.cw) {
|
||||
this.useCw = true;
|
||||
this.cw = this.reply.cw;
|
||||
}
|
||||
|
||||
this.focus();
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.focus();
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
// 書きかけの投稿を復元
|
||||
if (!this.instant && !this.mention) {
|
||||
const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[this.draftId];
|
||||
if (draft) {
|
||||
this.text = draft.data.text;
|
||||
this.files = (draft.data.files || []).filter(e => e);
|
||||
if (draft.data.poll) {
|
||||
this.poll = true;
|
||||
this.$nextTick(() => {
|
||||
(this.$refs.poll as any).set(draft.data.poll);
|
||||
});
|
||||
}
|
||||
this.$emit('change-attached-files', this.files);
|
||||
}
|
||||
}
|
||||
|
||||
this.$nextTick(() => this.watch());
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
watch() {
|
||||
this.$watch('text', () => this.saveDraft());
|
||||
this.$watch('poll', () => this.saveDraft());
|
||||
this.$watch('files', () => this.saveDraft());
|
||||
},
|
||||
|
||||
trimmedLength(text: string) {
|
||||
return length(text.trim());
|
||||
},
|
||||
|
||||
addTag(tag: string) {
|
||||
insertTextAtCursor(this.$refs.text, ` #${tag} `);
|
||||
},
|
||||
|
||||
focus() {
|
||||
(this.$refs.text as any).focus();
|
||||
},
|
||||
|
||||
chooseFile() {
|
||||
(this.$refs.file as any).click();
|
||||
},
|
||||
|
||||
chooseFileFromDrive() {
|
||||
this.$chooseDriveFile({
|
||||
multiple: true
|
||||
}).then(files => {
|
||||
for (const x of files) this.attachMedia(x);
|
||||
});
|
||||
},
|
||||
|
||||
attachMedia(driveFile) {
|
||||
this.files.push(driveFile);
|
||||
this.$emit('change-attached-files', this.files);
|
||||
},
|
||||
|
||||
detachMedia(id) {
|
||||
this.files = this.files.filter(x => x.id != id);
|
||||
this.$emit('change-attached-files', this.files);
|
||||
},
|
||||
|
||||
onChangeFile() {
|
||||
for (const x of Array.from((this.$refs.file as any).files)) this.upload(x);
|
||||
},
|
||||
|
||||
upload(file) {
|
||||
(this.$refs.uploader as any).upload(file);
|
||||
},
|
||||
|
||||
onChangeUploadings(uploads) {
|
||||
this.$emit('change-uploadings', uploads);
|
||||
},
|
||||
|
||||
onPollUpdate() {
|
||||
const got = this.$refs.poll.get();
|
||||
this.pollChoices = got.choices;
|
||||
this.pollMultiple = got.multiple;
|
||||
this.pollExpiration = [got.expiration, got.expiresAt || got.expiredAfter];
|
||||
this.saveDraft();
|
||||
},
|
||||
|
||||
setGeo() {
|
||||
if (navigator.geolocation == null) {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('@.post-form.geolocation-alert')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(pos => {
|
||||
this.geo = pos.coords;
|
||||
this.$emit('geo-attached', this.geo);
|
||||
}, err => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('@.post-form.error'),
|
||||
text: err.message
|
||||
});
|
||||
}, {
|
||||
enableHighAccuracy: true
|
||||
});
|
||||
},
|
||||
|
||||
removeGeo() {
|
||||
this.geo = null;
|
||||
this.$emit('geo-dettached');
|
||||
},
|
||||
|
||||
setVisibility() {
|
||||
const w = this.$root.new(MkVisibilityChooser, {
|
||||
source: this.$refs.visibilityButton,
|
||||
currentVisibility: this.visibility
|
||||
});
|
||||
w.$once('chosen', v => {
|
||||
this.applyVisibility(v);
|
||||
});
|
||||
},
|
||||
|
||||
applyVisibility(v: string) {
|
||||
const m = v.match(/^local-(.+)/);
|
||||
if (m) {
|
||||
this.localOnly = true;
|
||||
this.visibility = m[1];
|
||||
} else {
|
||||
this.localOnly = false;
|
||||
this.visibility = v;
|
||||
}
|
||||
},
|
||||
|
||||
addVisibleUser() {
|
||||
this.$root.dialog({
|
||||
title: this.$t('@.post-form.enter-username'),
|
||||
user: true
|
||||
}).then(({ canceled, result: user }) => {
|
||||
if (canceled) return;
|
||||
this.visibleUsers.push(user);
|
||||
});
|
||||
},
|
||||
|
||||
removeVisibleUser(user) {
|
||||
this.visibleUsers = erase(user, this.visibleUsers);
|
||||
},
|
||||
|
||||
clear() {
|
||||
this.text = '';
|
||||
this.files = [];
|
||||
this.poll = false;
|
||||
this.$emit('change-attached-files', this.files);
|
||||
},
|
||||
|
||||
onKeydown(e) {
|
||||
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canPost) this.post();
|
||||
},
|
||||
|
||||
async onPaste(e) {
|
||||
for (const item of Array.from(e.clipboardData.items)) {
|
||||
if (item.kind == 'file') {
|
||||
this.upload(item.getAsFile());
|
||||
}
|
||||
}
|
||||
|
||||
const paste = e.clipboardData.getData('text');
|
||||
|
||||
if (paste.startsWith(url + '/notes/')) {
|
||||
e.preventDefault();
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: this.$t('@.post-form.quote-question'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
insertTextAtCursor(this.$refs.text, paste);
|
||||
return;
|
||||
}
|
||||
|
||||
this.quoteId = paste.substr(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onDragover(e) {
|
||||
const isFile = e.dataTransfer.items[0].kind == 'file';
|
||||
const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
|
||||
if (isFile || isDriveFile) {
|
||||
e.preventDefault();
|
||||
this.draghover = true;
|
||||
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
|
||||
}
|
||||
},
|
||||
|
||||
onDragenter(e) {
|
||||
this.draghover = true;
|
||||
},
|
||||
|
||||
onDragleave(e) {
|
||||
this.draghover = false;
|
||||
},
|
||||
|
||||
onDrop(e): void {
|
||||
this.draghover = false;
|
||||
|
||||
// ファイルだったら
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
e.preventDefault();
|
||||
for (const x of Array.from(e.dataTransfer.files)) this.upload(x);
|
||||
return;
|
||||
}
|
||||
|
||||
//#region ドライブのファイル
|
||||
const driveFile = e.dataTransfer.getData('mk_drive_file');
|
||||
if (driveFile != null && driveFile != '') {
|
||||
const file = JSON.parse(driveFile);
|
||||
this.files.push(file);
|
||||
this.$emit('change-attached-files', this.files);
|
||||
e.preventDefault();
|
||||
}
|
||||
//#endregion
|
||||
},
|
||||
|
||||
async emoji() {
|
||||
const Picker = await import('../../desktop/views/components/emoji-picker-dialog.vue').then(m => m.default);
|
||||
const button = this.$refs.emoji;
|
||||
const rect = button.getBoundingClientRect();
|
||||
const vm = this.$root.new(Picker, {
|
||||
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||
y: rect.top + window.pageYOffset
|
||||
});
|
||||
vm.$once('chosen', emoji => {
|
||||
insertTextAtCursor(this.$refs.text, emoji);
|
||||
});
|
||||
},
|
||||
|
||||
saveDraft() {
|
||||
if (this.instant) return;
|
||||
|
||||
const data = JSON.parse(localStorage.getItem('drafts') || '{}');
|
||||
|
||||
data[this.draftId] = {
|
||||
updatedAt: new Date(),
|
||||
data: {
|
||||
text: this.text,
|
||||
files: this.files,
|
||||
poll: this.poll && this.$refs.poll ? (this.$refs.poll as any).get() : undefined
|
||||
}
|
||||
};
|
||||
|
||||
localStorage.setItem('drafts', JSON.stringify(data));
|
||||
},
|
||||
|
||||
deleteDraft() {
|
||||
const data = JSON.parse(localStorage.getItem('drafts') || '{}');
|
||||
|
||||
delete data[this.draftId];
|
||||
|
||||
localStorage.setItem('drafts', JSON.stringify(data));
|
||||
},
|
||||
|
||||
kao() {
|
||||
this.text += getFace();
|
||||
},
|
||||
|
||||
post() {
|
||||
this.posting = true;
|
||||
const viaMobile = opts.mobile && !this.$store.state.settings.disableViaMobile;
|
||||
this.$root.api('notes/create', {
|
||||
text: this.text == '' ? undefined : this.text,
|
||||
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||
replyId: this.reply ? this.reply.id : undefined,
|
||||
renoteId: this.renote ? this.renote.id : this.quoteId ? this.quoteId : undefined,
|
||||
poll: this.poll ? (this.$refs.poll as any).get() : undefined,
|
||||
cw: this.useCw ? this.cw || '' : undefined,
|
||||
visibility: this.visibility,
|
||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||
localOnly: this.localOnly,
|
||||
geo: this.geo ? {
|
||||
coordinates: [this.geo.longitude, this.geo.latitude],
|
||||
altitude: this.geo.altitude,
|
||||
accuracy: this.geo.accuracy,
|
||||
altitudeAccuracy: this.geo.altitudeAccuracy,
|
||||
heading: isNaN(this.geo.heading) ? null : this.geo.heading,
|
||||
speed: this.geo.speed,
|
||||
} : null,
|
||||
viaMobile: viaMobile
|
||||
}).then(data => {
|
||||
this.clear();
|
||||
this.deleteDraft();
|
||||
this.$emit('posted');
|
||||
if (opts.onSuccess) opts.onSuccess(this);
|
||||
}).catch(err => {
|
||||
if (opts.onSuccess) opts.onFailure(this);
|
||||
}).then(() => {
|
||||
this.posting = false;
|
||||
});
|
||||
|
||||
if (this.text && this.text != '') {
|
||||
const hashtags = parse(this.text).filter(x => x.node.type === 'hashtag').map(x => x.node.props.hashtag);
|
||||
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
|
||||
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
27
src/client/app/common/views/components/avatars.vue
Normal file
27
src/client/app/common/views/components/avatars.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<mk-avatar v-for="user in us" :user="user" :key="user.id" style="width:32px;height:32px;"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
userIds: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
us: []
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.us = await this.$root.api('users/show', {
|
||||
userIds: this.userIds
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
@ -18,6 +18,7 @@
|
||||
<fa icon="spinner" pulse v-if="type === 'waiting'"/>
|
||||
</div>
|
||||
<header v-if="title" v-html="title"></header>
|
||||
<header v-if="title == null && user">{{ $t('@.enter-username') }}</header>
|
||||
<div class="body" v-if="text" v-html="text"></div>
|
||||
<ui-input v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></ui-input>
|
||||
<ui-input v-if="user" v-model="userInputValue" autofocus @keydown="onInputKeydown"><template #prefix>@</template></ui-input>
|
||||
@ -97,7 +98,7 @@ export default Vue.extend({
|
||||
return {
|
||||
inputValue: this.input && this.input.default ? this.input.default : null,
|
||||
userInputValue: null,
|
||||
selectedValue: null,
|
||||
selectedValue: this.select ? this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
|
||||
faTimesCircle, faQuestionCircle
|
||||
};
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a class="a" :href="repositoryUrl" target="_blank" title="View source on GitHub">
|
||||
<a class="a" :href="repositoryUrl" rel="noopener" target="_blank" title="View source on GitHub">
|
||||
<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="aria-hidden">
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||
<path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor"></path>
|
||||
@ -19,7 +19,6 @@ export default Vue.extend({
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.a
|
||||
display block
|
||||
|
@ -21,7 +21,6 @@ import avatar from './avatar.vue';
|
||||
import nav from './nav.vue';
|
||||
import misskeyFlavoredMarkdown from './misskey-flavored-markdown.vue';
|
||||
import poll from './poll.vue';
|
||||
import pollEditor from './poll-editor.vue';
|
||||
import reactionIcon from './reaction-icon.vue';
|
||||
import reactionsViewer from './reactions-viewer.vue';
|
||||
import time from './time.vue';
|
||||
@ -44,6 +43,9 @@ import uiSwitch from './ui/switch.vue';
|
||||
import uiRadio from './ui/radio.vue';
|
||||
import uiSelect from './ui/select.vue';
|
||||
import uiInfo from './ui/info.vue';
|
||||
import uiMargin from './ui/margin.vue';
|
||||
import uiHr from './ui/hr.vue';
|
||||
import uiPagination from './ui/pagination.vue';
|
||||
import formButton from './ui/form/button.vue';
|
||||
import formRadio from './ui/form/radio.vue';
|
||||
|
||||
@ -68,7 +70,6 @@ Vue.component('mk-acct', acct);
|
||||
Vue.component('mk-avatar', avatar);
|
||||
Vue.component('mk-nav', nav);
|
||||
Vue.component('mk-poll', poll);
|
||||
Vue.component('mk-poll-editor', pollEditor);
|
||||
Vue.component('mk-reaction-icon', reactionIcon);
|
||||
Vue.component('mk-reactions-viewer', reactionsViewer);
|
||||
Vue.component('mk-time', time);
|
||||
@ -91,5 +92,8 @@ Vue.component('ui-switch', uiSwitch);
|
||||
Vue.component('ui-radio', uiRadio);
|
||||
Vue.component('ui-select', uiSelect);
|
||||
Vue.component('ui-info', uiInfo);
|
||||
Vue.component('ui-margin', uiMargin);
|
||||
Vue.component('ui-hr', uiHr);
|
||||
Vue.component('ui-pagination', uiPagination);
|
||||
Vue.component('form-button', formButton);
|
||||
Vue.component('form-radio', formRadio);
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a class="zxrjzpcj" :href="url" :class="service" target="_blank">
|
||||
<a class="zxrjzpcj" :href="url" :class="service" rel="noopener" target="_blank">
|
||||
<fa :icon="icon" size="lg" fixed-width /><span>{{ text }}</span>
|
||||
</a>
|
||||
</template>
|
||||
|
@ -33,7 +33,16 @@ import * as autosize from 'autosize';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/messaging-room.form.vue'),
|
||||
props: ['user'],
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
requird: false,
|
||||
},
|
||||
group: {
|
||||
type: Object,
|
||||
requird: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: null,
|
||||
@ -43,7 +52,7 @@ export default Vue.extend({
|
||||
},
|
||||
computed: {
|
||||
draftId(): string {
|
||||
return this.user.id;
|
||||
return this.user ? 'user:' + this.user.id : 'group:' + this.group.id;
|
||||
},
|
||||
canSend(): boolean {
|
||||
return (this.text != null && this.text != '') || this.file != null;
|
||||
@ -159,7 +168,8 @@ export default Vue.extend({
|
||||
send() {
|
||||
this.sending = true;
|
||||
this.$root.api('messaging/messages/create', {
|
||||
userId: this.user.id,
|
||||
userId: this.user ? this.user.id : undefined,
|
||||
groupId: this.group ? this.group.id : undefined,
|
||||
text: this.text ? this.text : undefined,
|
||||
fileId: this.file ? this.file.id : undefined
|
||||
}).then(message => {
|
||||
|
@ -9,7 +9,7 @@
|
||||
<div class="content" v-if="!message.isDeleted">
|
||||
<mfm class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/>
|
||||
<div class="file" v-if="message.file">
|
||||
<a :href="message.file.url" target="_blank" :title="message.file.name">
|
||||
<a :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name">
|
||||
<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"
|
||||
:style="{ backgroundColor: message.file.properties.avgColor || 'transparent' }"/>
|
||||
<p v-else>{{ message.file.name }}</p>
|
||||
@ -23,7 +23,12 @@
|
||||
<div></div>
|
||||
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||
<footer>
|
||||
<span class="read" v-if="isMe && message.isRead">{{ $t('is-read') }}</span>
|
||||
<template v-if="isGroup">
|
||||
<span class="read" v-if="message.reads.length > 0">{{ $t('is-read') }} {{ message.reads.length }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="read" v-if="isMe && message.isRead">{{ $t('is-read') }}</span>
|
||||
</template>
|
||||
<mk-time :time="message.createdAt"/>
|
||||
<template v-if="message.is_edited"><fa icon="pencil-alt"/></template>
|
||||
</footer>
|
||||
@ -42,6 +47,9 @@ export default Vue.extend({
|
||||
props: {
|
||||
message: {
|
||||
required: true
|
||||
},
|
||||
isGroup: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -4,14 +4,14 @@
|
||||
@drop.prevent.stop="onDrop"
|
||||
>
|
||||
<div class="body">
|
||||
<p class="init" v-if="init"><fa icon="spinner .spin"/>{{ $t('@.loading') }}</p>
|
||||
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
|
||||
<p class="init" v-if="init"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}</p>
|
||||
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ user ? $t('not-talked-user') : $t('not-talked-group') }}</p>
|
||||
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa :icon="faFlag"/>{{ $t('no-history') }}</p>
|
||||
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
|
||||
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
|
||||
</button>
|
||||
<template v-for="(message, i) in _messages">
|
||||
<x-message :message="message" :key="message.id"/>
|
||||
<x-message :message="message" :key="message.id" :is-group="group != null"/>
|
||||
<p class="date" v-if="i != messages.length - 1 && message._date != _messages[i + 1]._date">
|
||||
<span>{{ _messages[i + 1]._datetext }}</span>
|
||||
</p>
|
||||
@ -23,7 +23,7 @@
|
||||
<button @click="onIndicatorClick"><i><fa :icon="faArrowCircleDown"/></i>{{ $t('new-message') }}</button>
|
||||
</div>
|
||||
</transition>
|
||||
<x-form :user="user" ref="form"/>
|
||||
<x-form :user="user" :group="group" ref="form"/>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
@ -34,17 +34,30 @@ import i18n from '../../../i18n';
|
||||
import XMessage from './messaging-room.message.vue';
|
||||
import XForm from './messaging-room.form.vue';
|
||||
import { url } from '../../../config';
|
||||
import { faArrowCircleDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faFlag } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faArrowCircleDown, faFlag } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/messaging-room.vue'),
|
||||
|
||||
components: {
|
||||
XMessage,
|
||||
XForm
|
||||
},
|
||||
|
||||
props: ['user', 'isNaked'],
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
requird: false,
|
||||
},
|
||||
group: {
|
||||
type: Object,
|
||||
requird: false,
|
||||
},
|
||||
isNaked: {
|
||||
type: Boolean,
|
||||
requird: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
@ -76,7 +89,10 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.connectToChannel('messaging', { otherparty: this.user.id });
|
||||
this.connection = this.$root.stream.connectToChannel('messaging', {
|
||||
otherparty: this.user ? this.user.id : undefined,
|
||||
group: this.group ? this.group.id : undefined,
|
||||
});
|
||||
|
||||
this.connection.on('message', this.onMessage);
|
||||
this.connection.on('read', this.onRead);
|
||||
@ -147,7 +163,8 @@ export default Vue.extend({
|
||||
const max = this.existMoreMessages ? 20 : 10;
|
||||
|
||||
this.$root.api('messaging/messages', {
|
||||
userId: this.user.id,
|
||||
userId: this.user ? this.user.id : undefined,
|
||||
groupId: this.group ? this.group.id : undefined,
|
||||
limit: max + 1,
|
||||
untilId: this.existMoreMessages ? this.messages[0].id : undefined
|
||||
}).then(messages => {
|
||||
@ -199,12 +216,21 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
onRead(ids) {
|
||||
if (!Array.isArray(ids)) ids = [ids];
|
||||
for (const id of ids) {
|
||||
if (this.messages.some(x => x.id == id)) {
|
||||
const exist = this.messages.map(x => x.id).indexOf(id);
|
||||
this.messages[exist].isRead = true;
|
||||
onRead(x) {
|
||||
if (this.user) {
|
||||
if (!Array.isArray(x)) x = [x];
|
||||
for (const id of x) {
|
||||
if (this.messages.some(x => x.id == id)) {
|
||||
const exist = this.messages.map(x => x.id).indexOf(id);
|
||||
this.messages[exist].isRead = true;
|
||||
}
|
||||
}
|
||||
} else if (this.group) {
|
||||
for (const id of x.ids) {
|
||||
if (this.messages.some(x => x.id == id)) {
|
||||
const exist = this.messages.map(x => x.id).indexOf(id);
|
||||
this.messages[exist].reads.push(x.userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -274,17 +300,13 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mk-messaging-room
|
||||
display flex
|
||||
flex 1
|
||||
flex-direction column
|
||||
height 100%
|
||||
background var(--messagingRoomBg)
|
||||
|
||||
> .body
|
||||
width 100%
|
||||
max-width 600px
|
||||
margin 0 auto
|
||||
flex 1
|
||||
min-height calc(100% - 103px)
|
||||
|
||||
> .init,
|
||||
> .empty
|
||||
|
@ -21,36 +21,43 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="history" v-if="messages.length > 0">
|
||||
<template>
|
||||
<a v-for="message in messages"
|
||||
class="user"
|
||||
:href="`/i/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
|
||||
:data-is-me="isMe(message)"
|
||||
:data-is-read="message.isRead"
|
||||
@click.prevent="navigate(isMe(message) ? message.recipient : message.user)"
|
||||
:key="message.id"
|
||||
>
|
||||
<div>
|
||||
<mk-avatar class="avatar" :user="isMe(message) ? message.recipient : message.user"/>
|
||||
<header>
|
||||
<span class="name"><mk-user-name :user="isMe(message) ? message.recipient : message.user"/></span>
|
||||
<span class="username">@{{ isMe(message) ? message.recipient : message.user | acct }}</span>
|
||||
<mk-time :time="message.createdAt"/>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p class="text"><span class="me" v-if="isMe(message)">{{ $t('you') }}:</span>{{ message.text }}</p>
|
||||
</div>
|
||||
<a v-for="message in messages"
|
||||
class="user"
|
||||
:href="message.groupId ? `/i/messaging/group/${message.groupId}` : `/i/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`"
|
||||
:data-is-me="isMe(message)"
|
||||
:data-is-read="message.groupId ? message.reads.includes($store.state.i.id) : message.isRead"
|
||||
@click.prevent="message.groupId ? navigateGroup(message.group) : navigate(isMe(message) ? message.recipient : message.user)"
|
||||
:key="message.id"
|
||||
>
|
||||
<div>
|
||||
<mk-avatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user"/>
|
||||
<header v-if="message.groupId">
|
||||
<span class="name">{{ message.group.name }}</span>
|
||||
<mk-time :time="message.createdAt"/>
|
||||
</header>
|
||||
<header v-else>
|
||||
<span class="name"><mk-user-name :user="isMe(message) ? message.recipient : message.user"/></span>
|
||||
<span class="username">@{{ isMe(message) ? message.recipient : message.user | acct }}</span>
|
||||
<mk-time :time="message.createdAt"/>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p class="text"><span class="me" v-if="isMe(message)">{{ $t('you') }}:</span>{{ message.text }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
|
||||
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||
<ui-margin>
|
||||
<ui-button @click="startUser()"><fa :icon="faUser"/> {{ $t('start-with-user') }}</ui-button>
|
||||
<ui-button @click="startGroup()"><fa :icon="faUsers"/> {{ $t('start-with-group') }}</ui-button>
|
||||
</ui-margin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faUser, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../i18n';
|
||||
import getAcct from '../../../../../misc/acct/render';
|
||||
|
||||
@ -73,7 +80,8 @@ export default Vue.extend({
|
||||
messages: [],
|
||||
q: null,
|
||||
result: [],
|
||||
connection: null
|
||||
connection: null,
|
||||
faUser, faUsers
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -82,9 +90,13 @@ export default Vue.extend({
|
||||
this.connection.on('message', this.onMessage);
|
||||
this.connection.on('read', this.onRead);
|
||||
|
||||
this.$root.api('messaging/history').then(messages => {
|
||||
this.messages = messages;
|
||||
this.fetching = false;
|
||||
this.$root.api('messaging/history', { group: false }).then(userMessages => {
|
||||
this.$root.api('messaging/history', { group: true }).then(groupMessages => {
|
||||
const messages = userMessages.concat(groupMessages);
|
||||
messages.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
this.messages = messages;
|
||||
this.fetching = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
@ -96,16 +108,27 @@ export default Vue.extend({
|
||||
return message.userId == this.$store.state.i.id;
|
||||
},
|
||||
onMessage(message) {
|
||||
this.messages = this.messages.filter(m => !(
|
||||
(m.recipientId == message.recipientId && m.userId == message.userId) ||
|
||||
(m.recipientId == message.userId && m.userId == message.recipientId)));
|
||||
if (message.recipientId) {
|
||||
this.messages = this.messages.filter(m => !(
|
||||
(m.recipientId == message.recipientId && m.userId == message.userId) ||
|
||||
(m.recipientId == message.userId && m.userId == message.recipientId)));
|
||||
|
||||
this.messages.unshift(message);
|
||||
this.messages.unshift(message);
|
||||
} else if (message.groupId) {
|
||||
this.messages = this.messages.filter(m => m.groupId !== message.groupId);
|
||||
this.messages.unshift(message);
|
||||
}
|
||||
},
|
||||
onRead(ids) {
|
||||
for (const id of ids) {
|
||||
const found = this.messages.find(m => m.id == id);
|
||||
if (found) found.isRead = true;
|
||||
if (found) {
|
||||
if (found.recipientId) {
|
||||
found.isRead = true;
|
||||
} else if (found.groupId) {
|
||||
found.reads.push(this.$store.state.i.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
search() {
|
||||
@ -125,6 +148,9 @@ export default Vue.extend({
|
||||
navigate(user) {
|
||||
this.$emit('navigate', user);
|
||||
},
|
||||
navigateGroup(group) {
|
||||
this.$emit('navigateGroup', group);
|
||||
},
|
||||
onSearchKeydown(e) {
|
||||
switch (e.which) {
|
||||
case 9: // [TAB]
|
||||
@ -161,6 +187,31 @@ export default Vue.extend({
|
||||
(list.childNodes[i].nextElementSibling || list.childNodes[0]).focus();
|
||||
break;
|
||||
}
|
||||
},
|
||||
async startUser() {
|
||||
const { result: user } = await this.$root.dialog({
|
||||
user: {
|
||||
local: true
|
||||
}
|
||||
});
|
||||
if (user == null) return;
|
||||
this.navigate(user);
|
||||
},
|
||||
async startGroup() {
|
||||
const groups1 = await this.$root.api('users/groups/owned');
|
||||
const groups2 = await this.$root.api('users/groups/joined');
|
||||
const { canceled, result: group } = await this.$root.dialog({
|
||||
type: null,
|
||||
title: this.$t('select-group'),
|
||||
select: {
|
||||
items: groups1.concat(groups2).map(group => ({
|
||||
value: group, text: group.name
|
||||
}))
|
||||
},
|
||||
showCancelButton: true
|
||||
});
|
||||
if (canceled) return;
|
||||
this.navigateGroup(group);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -311,7 +362,6 @@ export default Vue.extend({
|
||||
color rgba(#000, 0.3)
|
||||
|
||||
> .history
|
||||
|
||||
> a
|
||||
display block
|
||||
text-decoration none
|
||||
|
@ -174,7 +174,7 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
key: Math.random(),
|
||||
props: {
|
||||
url: token.node.props.url,
|
||||
target: '_blank'
|
||||
rel: 'nofollow noopener',
|
||||
},
|
||||
attrs: {
|
||||
style: 'color:var(--mfmUrl);'
|
||||
@ -187,6 +187,7 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
attrs: {
|
||||
class: 'link',
|
||||
href: token.node.props.url,
|
||||
rel: 'nofollow noopener',
|
||||
target: '_blank',
|
||||
title: token.node.props.url,
|
||||
style: 'color:var(--mfmLink);'
|
||||
|
@ -1,12 +1,16 @@
|
||||
<template>
|
||||
<span class="mk-nav">
|
||||
<a :href="aboutUrl">{{ $t('about') }}</a>
|
||||
<template v-if="ToSUrl !== null">
|
||||
<i>・</i>
|
||||
<a :href="ToSUrl" target="_blank">{{ $t('tos') }}</a>
|
||||
</template>
|
||||
<i>・</i>
|
||||
<a :href="repositoryUrl">{{ $t('repository') }}</a>
|
||||
<a :href="repositoryUrl" rel="noopener" target="_blank">{{ $t('repository') }}</a>
|
||||
<i>・</i>
|
||||
<a :href="feedbackUrl" target="_blank">{{ $t('feedback') }}</a>
|
||||
<a :href="feedbackUrl" rel="noopener" target="_blank">{{ $t('feedback') }}</a>
|
||||
<i>・</i>
|
||||
<a href="/dev">{{ $t('develop') }}</a>
|
||||
<a href="/dev" target="_blank">{{ $t('develop') }}</a>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@ -21,8 +25,17 @@ export default Vue.extend({
|
||||
return {
|
||||
aboutUrl: `/docs/${lang}/about`,
|
||||
repositoryUrl: 'https://github.com/syuilo/misskey',
|
||||
feedbackUrl: 'https://github.com/syuilo/misskey/issues/new'
|
||||
feedbackUrl: 'https://github.com/syuilo/misskey/issues/new',
|
||||
ToSUrl: null
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$root.getMeta(true).then(meta => {
|
||||
this.repositoryUrl = meta.repositoryUrl;
|
||||
this.feedbackUrl = meta.feedbackUrl;
|
||||
this.ToSUrl = meta.ToSUrl;
|
||||
})
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<router-link :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
|
||||
<router-link :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
|
||||
<div class="thumbnail" v-if="page.eyeCatchingImage" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
|
||||
<article>
|
||||
<header>
|
||||
@ -32,16 +32,13 @@ export default Vue.extend({
|
||||
display block
|
||||
overflow hidden
|
||||
width 100%
|
||||
background var(--face)
|
||||
border solid var(--lineWidth) var(--urlPreviewBorder)
|
||||
border-radius 4px
|
||||
overflow hidden
|
||||
|
||||
&.round
|
||||
border-radius 8px
|
||||
|
||||
&.shadow
|
||||
box-shadow 0 4px 16px rgba(#000, 0.1)
|
||||
|
||||
@media (min-width 500px)
|
||||
box-shadow 0 8px 32px rgba(#000, 0.1)
|
||||
&:hover
|
||||
text-decoration none
|
||||
border-color var(--urlPreviewBorderHover)
|
||||
|
||||
> .thumbnail
|
||||
position absolute
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="mk-poll-editor">
|
||||
<div class="zmdxowus">
|
||||
<p class="caution" v-if="choices.length < 2">
|
||||
<fa icon="exclamation-triangle"/>{{ $t('no-only-one-choice') }}
|
||||
</p>
|
||||
@ -134,7 +134,7 @@ export default Vue.extend({
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mk-poll-editor
|
||||
.zmdxowus
|
||||
padding 8px
|
||||
|
||||
> .caution
|
||||
|
@ -16,7 +16,7 @@
|
||||
<button @click="react('pudding')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="10" :title="$t('@.reactions.pudding')" v-particle><mk-reaction-icon reaction="pudding"/></button>
|
||||
</div>
|
||||
<div v-if="enableEmojiReaction" class="text">
|
||||
<input v-model="text" placeholder="または絵文字を入力" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
|
||||
<input v-model="text" :placeholder="$t('input-reaction-placeholder')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</template>
|
||||
<div v-if="data && !$store.state.i.twoFactorEnabled">
|
||||
<ol>
|
||||
<li>{{ $t('authenticator') }}<a href="https://support.google.com/accounts/answer/1066447" target="_blank">{{ $t('howtoinstall') }}</a></li>
|
||||
<li>{{ $t('authenticator') }}<a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank">{{ $t('howtoinstall') }}</a></li>
|
||||
<li>{{ $t('scan') }}<br><img :src="data.qr"></li>
|
||||
<li>{{ $t('done') }}<br>
|
||||
<ui-input v-model="token">{{ $t('token') }}</ui-input>
|
||||
|
@ -4,21 +4,21 @@
|
||||
|
||||
<section v-if="enableTwitterIntegration">
|
||||
<header><fa :icon="['fab', 'twitter']"/> Twitter</header>
|
||||
<p v-if="$store.state.i.twitter">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
|
||||
<p v-if="$store.state.i.twitter">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
|
||||
<ui-button v-if="$store.state.i.twitter" @click="disconnectTwitter">{{ $t('disconnect') }}</ui-button>
|
||||
<ui-button v-else @click="connectTwitter">{{ $t('connect') }}</ui-button>
|
||||
</section>
|
||||
|
||||
<section v-if="enableDiscordIntegration">
|
||||
<header><fa :icon="['fab', 'discord']"/> Discord</header>
|
||||
<p v-if="$store.state.i.discord">{{ $t('connected-to') }}: <a :href="`https://discordapp.com/users/${$store.state.i.discord.id}`" target="_blank">@{{ $store.state.i.discord.username }}#{{ $store.state.i.discord.discriminator }}</a></p>
|
||||
<p v-if="$store.state.i.discord">{{ $t('connected-to') }}: <a :href="`https://discordapp.com/users/${$store.state.i.discord.id}`" rel="nofollow noopener" target="_blank">@{{ $store.state.i.discord.username }}#{{ $store.state.i.discord.discriminator }}</a></p>
|
||||
<ui-button v-if="$store.state.i.discord" @click="disconnectDiscord">{{ $t('disconnect') }}</ui-button>
|
||||
<ui-button v-else @click="connectDiscord">{{ $t('connect') }}</ui-button>
|
||||
</section>
|
||||
|
||||
<section v-if="enableGithubIntegration">
|
||||
<header><fa :icon="['fab', 'github']"/> GitHub</header>
|
||||
<p v-if="$store.state.i.github">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
|
||||
<p v-if="$store.state.i.github">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" rel="nofollow noopener" target="_blank">@{{ $store.state.i.github.login }}</a></p>
|
||||
<ui-button v-if="$store.state.i.github" @click="disconnectGithub">{{ $t('disconnect') }}</ui-button>
|
||||
<ui-button v-else @click="connectGithub">{{ $t('connect') }}</ui-button>
|
||||
</section>
|
||||
|
@ -45,7 +45,7 @@
|
||||
</ui-select>
|
||||
</label>
|
||||
|
||||
<a href="https://assets.msky.cafe/theme/list" target="_blank">{{ $t('find-more-theme') }}</a>
|
||||
<a href="https://assets.msky.cafe/theme/list" rel="noopener" target="_blank">{{ $t('find-more-theme') }}</a>
|
||||
|
||||
<details class="creator">
|
||||
<summary><fa icon="palette"/> {{ $t('create-a-theme') }}</summary>
|
||||
|
@ -37,8 +37,13 @@
|
||||
<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('password-not-matched') }}</p>
|
||||
</template>
|
||||
</ui-input>
|
||||
<ui-switch v-model="ToSAgreement" v-if="meta.ToSUrl">
|
||||
<i18n path="agree-to">
|
||||
<a :href="meta.ToSUrl" target="_blank">{{ $t('tos') }}</a>
|
||||
</i18n>
|
||||
</ui-switch>
|
||||
<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>
|
||||
<ui-button type="submit">{{ $t('create') }}</ui-button>
|
||||
<ui-button type="submit" :disabled="!(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
@ -64,7 +69,8 @@ export default Vue.extend({
|
||||
usernameState: null,
|
||||
passwordStrength: '',
|
||||
passwordRetypeState: null,
|
||||
meta: null
|
||||
meta: {},
|
||||
ToSAgreement: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
|
||||
<div v-else>
|
||||
<vue-word-cloud
|
||||
:words="tags.slice(0, 20).map(x => [x.name, x.count])"
|
||||
:words="tags.slice(0, 20).map(x => [x.tag, x.count])"
|
||||
:color="color"
|
||||
:spacing="1">
|
||||
<template slot-scope="{word, text, weight}">
|
||||
@ -43,7 +43,7 @@ export default Vue.extend({
|
||||
},
|
||||
methods: {
|
||||
fetch() {
|
||||
this.$root.api('aggregation/hashtags').then(tags => {
|
||||
this.$root.api('hashtags/trend').then(tags => {
|
||||
this.tags = tags;
|
||||
this.fetching = false;
|
||||
});
|
||||
|
@ -113,7 +113,7 @@ export default Vue.extend({
|
||||
padding 8px 10px
|
||||
text-align center
|
||||
font-weight normal
|
||||
font-size 16px
|
||||
font-size 14px
|
||||
line-height 24px
|
||||
border none
|
||||
outline none
|
||||
|
15
src/client/app/common/views/components/ui/hr.vue
Normal file
15
src/client/app/common/views/components/ui/hr.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div class="evrzpitu"></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.evrzpitu
|
||||
margin 16px 0
|
||||
border-bottom solid var(--lineWidth) var(--faceDivider)
|
||||
|
||||
</style>
|
@ -210,17 +210,25 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.prefix) {
|
||||
this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
|
||||
if (this.$refs.prefix.offsetWidth) {
|
||||
this.$refs.input.style.paddingLeft = this.$refs.prefix.offsetWidth + 'px';
|
||||
// このコンポーネントが作成された時、非表示状態である場合がある
|
||||
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
||||
const clock = setInterval(() => {
|
||||
if (this.$refs.prefix) {
|
||||
this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
|
||||
if (this.$refs.prefix.offsetWidth) {
|
||||
this.$refs.input.style.paddingLeft = this.$refs.prefix.offsetWidth + 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.$refs.suffix) {
|
||||
if (this.$refs.suffix.offsetWidth) {
|
||||
this.$refs.input.style.paddingRight = this.$refs.suffix.offsetWidth + 'px';
|
||||
if (this.$refs.suffix) {
|
||||
if (this.$refs.suffix.offsetWidth) {
|
||||
this.$refs.input.style.paddingRight = this.$refs.suffix.offsetWidth + 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
clearInterval(clock);
|
||||
});
|
||||
});
|
||||
|
||||
this.$on('keydown', (e: KeyboardEvent) => {
|
||||
|
16
src/client/app/common/views/components/ui/margin.vue
Normal file
16
src/client/app/common/views/components/ui/margin.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="zdcrxcne">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.zdcrxcne
|
||||
margin 16px
|
||||
|
||||
</style>
|
36
src/client/app/common/views/components/ui/pagination.vue
Normal file
36
src/client/app/common/views/components/ui/pagination.vue
Normal file
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="mwermpua" v-if="!fetching">
|
||||
<sequential-entrance animation="entranceFromTop" delay="25">
|
||||
<slot :items="items"></slot>
|
||||
</sequential-entrance>
|
||||
<div class="more" v-if="more">
|
||||
<ui-button @click="fetchMore()">{{ $t('@.load-more') }}</ui-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import paging from '../../../scripts/paging';
|
||||
|
||||
export default Vue.extend({
|
||||
mixins: [
|
||||
paging({
|
||||
captureWindowScroll: false,
|
||||
}),
|
||||
],
|
||||
|
||||
props: {
|
||||
pagination: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mwermpua
|
||||
> .more
|
||||
margin-top 16px
|
||||
|
||||
</style>
|
@ -9,7 +9,7 @@
|
||||
</blockquote>
|
||||
</div>
|
||||
<div v-else class="mk-url-preview">
|
||||
<a :class="{ mini: narrow, compact }" :href="url" target="_blank" :title="url" v-if="!fetching">
|
||||
<component :is="hasRoute ? 'router-link' : 'a'" :class="{ mini: narrow, compact }" :[attr]="hasRoute ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
|
||||
<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
|
||||
<button v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enable-player')"><fa :icon="['far', 'play-circle']"/></button>
|
||||
</div>
|
||||
@ -23,17 +23,18 @@
|
||||
<p :title="sitename">{{ sitename }}</p>
|
||||
</footer>
|
||||
</article>
|
||||
</a>
|
||||
</component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { url as misskeyUrl } from '../../../config';
|
||||
import { url as local } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/url-preview.vue'),
|
||||
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
@ -60,7 +61,13 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
data() {
|
||||
const isSelf = this.url.startsWith(local);
|
||||
const hasRoute =
|
||||
this.url.substr(local.length).startsWith('/@') ||
|
||||
this.url.substr(local.length).startsWith('/notes/') ||
|
||||
this.url.substr(local.length).startsWith('/pages/');
|
||||
return {
|
||||
local,
|
||||
fetching: true,
|
||||
title: null,
|
||||
description: null,
|
||||
@ -74,7 +81,10 @@ export default Vue.extend({
|
||||
},
|
||||
tweetUrl: null,
|
||||
playerEnabled: false,
|
||||
misskeyUrl,
|
||||
self: isSelf,
|
||||
hasRoute: hasRoute,
|
||||
attr: hasRoute ? 'to' : 'href',
|
||||
target: hasRoute ? null : '_blank'
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1,29 +1,42 @@
|
||||
<template>
|
||||
<a class="mk-url" :href="url" :target="target">
|
||||
<span class="schema">{{ schema }}//</span>
|
||||
<span class="hostname">{{ hostname }}</span>
|
||||
<span class="port" v-if="port != ''">:{{ port }}</span>
|
||||
<span class="pathname" v-if="pathname != ''">{{ pathname }}</span>
|
||||
<component :is="hasRoute ? 'router-link' : 'a'" class="mk-url" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target">
|
||||
<template v-if="!self">
|
||||
<span class="schema">{{ schema }}//</span>
|
||||
<span class="hostname">{{ hostname }}</span>
|
||||
<span class="port" v-if="port != ''">:{{ port }}</span>
|
||||
</template>
|
||||
<span class="pathname" v-if="pathname != ''">{{ self ? pathname.substr(1) : pathname }}</span>
|
||||
<span class="query">{{ query }}</span>
|
||||
<span class="hash">{{ hash }}</span>
|
||||
<fa icon="external-link-square-alt"/>
|
||||
</a>
|
||||
<fa icon="external-link-square-alt" v-if="target === '_blank'"/>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { toUnicode as decodePunycode } from 'punycode';
|
||||
import { url as local } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
props: ['url', 'target'],
|
||||
props: ['url', 'rel'],
|
||||
data() {
|
||||
const isSelf = this.url.startsWith(local);
|
||||
const hasRoute =
|
||||
this.url.substr(local.length).startsWith('/@') ||
|
||||
this.url.substr(local.length).startsWith('/notes/') ||
|
||||
this.url.substr(local.length).startsWith('/pages/');
|
||||
return {
|
||||
local,
|
||||
schema: null,
|
||||
hostname: null,
|
||||
port: null,
|
||||
pathname: null,
|
||||
query: null,
|
||||
hash: null
|
||||
hash: null,
|
||||
self: isSelf,
|
||||
hasRoute: hasRoute,
|
||||
attr: hasRoute ? 'to' : 'href',
|
||||
target: hasRoute ? null : '_blank'
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
@ -2,13 +2,13 @@
|
||||
<ui-container :body-togglable="true">
|
||||
<template #header><slot></slot></template>
|
||||
|
||||
<mk-error v-if="!fetching && !inited" @retry="init()"/>
|
||||
<mk-error v-if="error" @retry="init()"/>
|
||||
|
||||
<div class="efvhhmdq" :class="{ iconOnly }" v-size="[{ lt: 500, class: 'narrow' }]">
|
||||
<div class="no-users" v-if="inited && us.length == 0">
|
||||
<div class="no-users" v-if="empty">
|
||||
<p>{{ $t('no-users') }}</p>
|
||||
</div>
|
||||
<div class="user" v-for="user in us" :key="user.id">
|
||||
<div class="user" v-for="user in users" :key="user.id">
|
||||
<mk-avatar class="avatar" :user="user"/>
|
||||
<div class="body" v-if="!iconOnly">
|
||||
<div class="name">
|
||||
@ -21,8 +21,8 @@
|
||||
<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
|
||||
</div>
|
||||
</div>
|
||||
<button class="more" :class="{ fetching: fetchingMoreUsers }" v-if="cursor != null" @click="fetchMoreUsers()" :disabled="fetchingMoreUsers">
|
||||
<template v-if="fetchingMoreUsers"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreUsers ? $t('@.loading') : $t('@.load-more') }}
|
||||
<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMore()" :disabled="moreFetching">
|
||||
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('@.loading') : $t('@.load-more') }}
|
||||
</button>
|
||||
</div>
|
||||
</ui-container>
|
||||
@ -31,60 +31,31 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import paging from '../../../common/scripts/paging';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/user-list.vue'),
|
||||
|
||||
mixins: [
|
||||
paging({}),
|
||||
],
|
||||
|
||||
props: {
|
||||
makePromise: {
|
||||
pagination: {
|
||||
required: true
|
||||
},
|
||||
extract: {
|
||||
required: false
|
||||
},
|
||||
iconOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
fetchingMoreUsers: false,
|
||||
us: [],
|
||||
inited: false,
|
||||
more: false
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async init() {
|
||||
this.fetching = true;
|
||||
await (this.makePromise()).then(x => {
|
||||
if (Array.isArray(x)) {
|
||||
this.us = x;
|
||||
} else {
|
||||
this.us = x.users;
|
||||
this.cursor = x.cursor;
|
||||
}
|
||||
this.inited = true;
|
||||
this.fetching = false;
|
||||
}, e => {
|
||||
this.fetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
async fetchMoreUsers() {
|
||||
this.fetchingMoreUsers = true;
|
||||
await (this.makePromise(this.cursor)).then(x => {
|
||||
this.us = this.us.concat(x.users);
|
||||
this.cursor = x.cursor;
|
||||
this.fetchingMoreUsers = false;
|
||||
}, e => {
|
||||
this.fetchingMoreUsers = false;
|
||||
});
|
||||
computed: {
|
||||
users() {
|
||||
return this.extract ? this.extract(this.items) : this.items;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,95 +0,0 @@
|
||||
<template>
|
||||
<div class="xkxvokkjlptzyewouewmceqcxhpgzprp">
|
||||
<button class="ui" @click="add">{{ $t('create-list') }}</button>
|
||||
<a v-for="list in lists" :key="list.id" @click="choice(list)">{{ list.name }}</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/user-lists.vue'),
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
lists: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$root.api('users/lists/list').then(lists => {
|
||||
this.fetching = false;
|
||||
this.lists = lists;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
add() {
|
||||
this.$root.dialog({
|
||||
title: this.$t('list-name'),
|
||||
input: true
|
||||
}).then(async ({ canceled, result: name }) => {
|
||||
if (canceled) return;
|
||||
const list = await this.$root.api('users/lists/create', {
|
||||
name
|
||||
});
|
||||
|
||||
this.lists.push(list)
|
||||
this.$emit('choosen', list);
|
||||
});
|
||||
},
|
||||
choice(list) {
|
||||
this.$emit('choosen', list);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.xkxvokkjlptzyewouewmceqcxhpgzprp
|
||||
padding 16px
|
||||
background: var(--bg)
|
||||
|
||||
> button
|
||||
display block
|
||||
margin-bottom 16px
|
||||
color var(--primaryForeground)
|
||||
background var(--primary)
|
||||
width 100%
|
||||
border-radius 38px
|
||||
user-select none
|
||||
cursor pointer
|
||||
padding 0 16px
|
||||
min-width 100px
|
||||
line-height 38px
|
||||
font-size 14px
|
||||
font-weight 700
|
||||
|
||||
&:hover
|
||||
background var(--primaryLighten10)
|
||||
|
||||
&:active
|
||||
background var(--primaryDarken10)
|
||||
|
||||
a
|
||||
display block
|
||||
margin 8px 0
|
||||
padding 8px
|
||||
color var(--text)
|
||||
background var(--face)
|
||||
box-shadow 0 2px 16px var(--reversiListItemShadow)
|
||||
border-radius 6px
|
||||
cursor pointer
|
||||
line-height 32px
|
||||
|
||||
*
|
||||
pointer-events none
|
||||
user-select none
|
||||
|
||||
&:hover
|
||||
box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.05)
|
||||
|
||||
&:active
|
||||
box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.1)
|
||||
|
||||
</style>
|
@ -7,7 +7,6 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
|
||||
import { faExclamationCircle, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
@ -27,19 +26,23 @@ export default Vue.extend({
|
||||
icon: ['fas', 'list'],
|
||||
text: this.$t('push-to-list'),
|
||||
action: this.pushList
|
||||
}, null, {
|
||||
icon: this.user.isMuted ? ['fas', 'eye'] : ['far', 'eye-slash'],
|
||||
text: this.user.isMuted ? this.$t('unmute') : this.$t('mute'),
|
||||
action: this.toggleMute
|
||||
}, {
|
||||
icon: 'ban',
|
||||
text: this.user.isBlocking ? this.$t('unblock') : this.$t('block'),
|
||||
action: this.toggleBlock
|
||||
}, null, {
|
||||
icon: faExclamationCircle,
|
||||
text: this.$t('report-abuse'),
|
||||
action: this.reportAbuse
|
||||
}];
|
||||
}] as any;
|
||||
|
||||
if (this.$store.getters.isSignedIn && this.$store.state.i.id != this.user.id) {
|
||||
menu = menu.concat([null, {
|
||||
icon: this.user.isMuted ? ['fas', 'eye'] : ['far', 'eye-slash'],
|
||||
text: this.user.isMuted ? this.$t('unmute') : this.$t('mute'),
|
||||
action: this.toggleMute
|
||||
}, {
|
||||
icon: 'ban',
|
||||
text: this.user.isBlocking ? this.$t('unblock') : this.$t('block'),
|
||||
action: this.toggleBlock
|
||||
}, null, {
|
||||
icon: faExclamationCircle,
|
||||
text: this.$t('report-abuse'),
|
||||
action: this.reportAbuse
|
||||
}]);
|
||||
}
|
||||
|
||||
if (this.$store.getters.isSignedIn && (this.$store.state.i.isAdmin || this.$store.state.i.isModerator)) {
|
||||
menu = menu.concat([null, {
|
||||
|
45
src/client/app/common/views/deck/deck.column-template.vue
Normal file
45
src/client/app/common/views/deck/deck.column-template.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<x-column>
|
||||
<template #header>
|
||||
<fa v-if="icon" :icon="icon"/>{{ title }}
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<component :is="component" @init="init" v-bind="$attrs"/>
|
||||
</div>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XColumn from './deck.column.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XColumn,
|
||||
},
|
||||
|
||||
props: {
|
||||
component: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
title: null,
|
||||
icon: null,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
},
|
||||
|
||||
methods: {
|
||||
init(v) {
|
||||
this.title = v.title;
|
||||
this.icon = v.icon;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -2,7 +2,7 @@
|
||||
<x-column :name="name" :column="column" :is-stacked="isStacked">
|
||||
<template #header><fa :icon="['far', 'envelope']"/>{{ name }}</template>
|
||||
|
||||
<x-direct/>
|
||||
<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
@ -10,13 +10,14 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XColumn from './deck.column.vue';
|
||||
import XDirect from './deck.direct.vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XColumn,
|
||||
XDirect
|
||||
XNotes
|
||||
},
|
||||
|
||||
props: {
|
||||
@ -30,6 +31,22 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
limit: 10,
|
||||
params: {
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
|
||||
visibility: 'specified'
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
name(): string {
|
||||
if (this.column.name) return this.column.name;
|
||||
@ -37,9 +54,25 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', this.onNote);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onNote(note) {
|
||||
// Prepend a note
|
||||
if (note.visibility == 'specified') {
|
||||
(this.$refs.timeline as any).prepend(note);
|
||||
}
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$refs.tl.focus();
|
||||
this.$refs.timeline.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
makePromise: cursor => this.$root.api('notes/mentions', {
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
|
||||
visibility: 'specified'
|
||||
}).then(notes => {
|
||||
if (notes.length == fetchLimit + 1) {
|
||||
notes.pop();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', this.onNote);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onNote(note) {
|
||||
// Prepend a note
|
||||
if (note.visibility == 'specified') {
|
||||
(this.$refs.timeline as any).prepend(note);
|
||||
}
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$refs.timeline.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<x-column>
|
||||
<template #header>
|
||||
<fa :icon="faHashtag"/>{{ $t('@.explore') }}
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<x-explore v-bind="$attrs"/>
|
||||
</div>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XColumn from './deck.column.vue';
|
||||
import XExplore from '../../../common/views/pages/explore.vue';
|
||||
import { faHashtag } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XColumn,
|
||||
XExplore,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faHashtag
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<x-column>
|
||||
<template #header>
|
||||
<fa :icon="['fa', 'star']"/>{{ $t('@.favorites') }}
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</div>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XColumn from './deck.column.vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XColumn,
|
||||
XNotes,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
makePromise: cursor => this.$root.api('i/favorites', {
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
}).then(notes => {
|
||||
notes = notes.map(x => x.note);
|
||||
if (notes.length == fetchLimit + 1) {
|
||||
notes.pop();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.timeline.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<x-column>
|
||||
<template #header>
|
||||
<fa :icon="faNewspaper"/>{{ $t('@.featured-notes') }}
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</div>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XColumn from './deck.column.vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
import { faNewspaper } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XColumn,
|
||||
XNotes,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faNewspaper,
|
||||
makePromise: cursor => this.$root.api('notes/featured', {
|
||||
limit: 30,
|
||||
}).then(notes => {
|
||||
notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
return notes;
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.timeline.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNotes
|
||||
@ -28,28 +26,18 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
makePromise: cursor => this.$root.api('notes/search-by-tag', {
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
withFiles: this.mediaOnly,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
|
||||
query: this.tagTl.query
|
||||
}).then(notes => {
|
||||
if (notes.length == fetchLimit + 1) {
|
||||
notes.pop();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
})
|
||||
pagination: {
|
||||
endpoint: 'notes/search-by-tag',
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
|
||||
withFiles: this.mediaOnly,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
|
||||
query: this.tagTl.query
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNotes
|
||||
@ -28,28 +26,18 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
makePromise: cursor => this.$root.api('notes/user-list-timeline', {
|
||||
listId: this.list.id,
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
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();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
})
|
||||
pagination: {
|
||||
endpoint: 'notes/user-list-timeline',
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
listId: this.list.id,
|
||||
untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
|
||||
withFiles: this.mediaOnly,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<x-column :name="name" :column="column" :is-stacked="isStacked">
|
||||
<template #header><fa icon="at"/>{{ name }}</template>
|
||||
|
||||
<x-mentions ref="tl"/>
|
||||
<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
|
||||
</x-column>
|
||||
</template>
|
||||
|
||||
@ -10,13 +10,14 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XColumn from './deck.column.vue';
|
||||
import XMentions from './deck.mentions.vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XColumn,
|
||||
XMentions
|
||||
XNotes
|
||||
},
|
||||
|
||||
props: {
|
||||
@ -30,6 +31,22 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
pagination: {
|
||||
endpoint: 'notes/mentions',
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
|
||||
})
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
name(): string {
|
||||
if (this.column.name) return this.column.name;
|
||||
@ -37,9 +54,22 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', this.onNote);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onNote(note) {
|
||||
(this.$refs.timeline as any).prepend(note);
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$refs.tl.focus();
|
||||
this.$refs.timeline.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XNotes
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
connection: null,
|
||||
makePromise: cursor => this.$root.api('notes/mentions', {
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
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();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
this.connection.on('mention', this.onNote);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onNote(note) {
|
||||
(this.$refs.timeline as any).prepend(note);
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$refs.timeline.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -8,7 +8,7 @@
|
||||
<div class="is-remote" v-if="note.user.host != null">
|
||||
<details>
|
||||
<summary><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-post') }}</summary>
|
||||
<a :href="note.url || note.uri" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
||||
<a :href="note.url || note.uri" rel="nofollow noopener" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
||||
</details>
|
||||
</div>
|
||||
<mk-note :note="note" :detail="true" :key="note.id"/>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
|
||||
<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
|
||||
<div class="empty" v-if="empty">{{ $t('@.no-notes') }}</div>
|
||||
|
||||
<mk-error v-if="!fetching && !inited" @retry="init()"/>
|
||||
<mk-error v-if="error" @retry="init()"/>
|
||||
|
||||
<div class="placeholder" v-if="fetching">
|
||||
<template v-for="i in 10">
|
||||
@ -16,7 +16,6 @@
|
||||
<mk-note
|
||||
:note="note"
|
||||
:key="note.id"
|
||||
@update:note="onNoteUpdated(i, $event)"
|
||||
:compact="true"
|
||||
/>
|
||||
<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
|
||||
@ -39,33 +38,47 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import shouldMuteNote from '../../../common/scripts/should-mute-note';
|
||||
|
||||
const displayLimit = 20;
|
||||
import paging from '../../../common/scripts/paging';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
inject: ['column', 'isScrollTop', 'count'],
|
||||
|
||||
mixins: [
|
||||
paging({
|
||||
limit: 20,
|
||||
|
||||
onQueueChanged: (self, q) => {
|
||||
self.count(q.length);
|
||||
},
|
||||
|
||||
onPrepend: (self, note, silent) => {
|
||||
// 弾く
|
||||
if (shouldMuteNote(self.$store.state.i, self.$store.state.settings, note)) return false;
|
||||
|
||||
// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
|
||||
if (document.hidden || !self.isScrollTop()) {
|
||||
self.$store.commit('pushBehindNote', note);
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
|
||||
props: {
|
||||
makePromise: {
|
||||
pagination: {
|
||||
required: true
|
||||
},
|
||||
extract: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
rootEl: null,
|
||||
notes: [],
|
||||
queue: [],
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
inited: false,
|
||||
more: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
notes() {
|
||||
return this.extract ? this.extract(this.items) : this.items;
|
||||
},
|
||||
|
||||
_notes(): any[] {
|
||||
return (this.notes as any).map(note => {
|
||||
const date = new Date(note.createdAt).getDate();
|
||||
@ -77,15 +90,6 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
queue(q) {
|
||||
this.count(q.length);
|
||||
},
|
||||
makePromise() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.column.$on('top', this.onTop);
|
||||
this.column.$on('bottom', this.onBottom);
|
||||
@ -101,87 +105,6 @@ export default Vue.extend({
|
||||
focus() {
|
||||
(this.$refs.notes as any).children[0].focus ? (this.$refs.notes as any).children[0].focus() : (this.$refs.notes as any).$el.children[0].focus();
|
||||
},
|
||||
|
||||
onNoteUpdated(i, note) {
|
||||
Vue.set((this as any).notes, i, note);
|
||||
},
|
||||
|
||||
reload() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
async init() {
|
||||
this.queue = [];
|
||||
this.notes = [];
|
||||
this.fetching = true;
|
||||
await (this.makePromise()).then(x => {
|
||||
if (Array.isArray(x)) {
|
||||
this.notes = x;
|
||||
} else {
|
||||
this.notes = x.notes;
|
||||
this.more = x.more;
|
||||
}
|
||||
this.inited = true;
|
||||
this.fetching = false;
|
||||
this.$emit('inited');
|
||||
}, e => {
|
||||
this.fetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
async fetchMore() {
|
||||
if (!this.more || this.moreFetching) return;
|
||||
this.moreFetching = true;
|
||||
await (this.makePromise(this.notes[this.notes.length - 1].id)).then(x => {
|
||||
this.notes = this.notes.concat(x.notes);
|
||||
this.more = x.more;
|
||||
this.moreFetching = false;
|
||||
}, e => {
|
||||
this.moreFetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
prepend(note, silent = false) {
|
||||
// 弾く
|
||||
if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
|
||||
|
||||
// タブが非表示ならタイトルで通知
|
||||
if (document.hidden) {
|
||||
this.$store.commit('pushBehindNote', note);
|
||||
}
|
||||
|
||||
if (this.isScrollTop()) {
|
||||
// Prepend the note
|
||||
this.notes.unshift(note);
|
||||
|
||||
// オーバーフローしたら古い投稿は捨てる
|
||||
if (this.notes.length >= displayLimit) {
|
||||
this.notes = this.notes.slice(0, displayLimit);
|
||||
this.more = true;
|
||||
}
|
||||
} else {
|
||||
this.queue.push(note);
|
||||
}
|
||||
},
|
||||
|
||||
append(note) {
|
||||
this.notes.push(note);
|
||||
},
|
||||
|
||||
releaseQueue() {
|
||||
for (const n of this.queue) {
|
||||
this.prepend(n, true);
|
||||
}
|
||||
this.queue = [];
|
||||
},
|
||||
|
||||
onTop() {
|
||||
this.releaseQueue();
|
||||
},
|
||||
|
||||
onBottom() {
|
||||
this.fetchMore();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -81,15 +81,15 @@
|
||||
</div>
|
||||
|
||||
<template v-if="notification.type == 'quote'">
|
||||
<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||
<mk-note :note="notification.note"/>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type == 'reply'">
|
||||
<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||
<mk-note :note="notification.note"/>
|
||||
</template>
|
||||
|
||||
<template v-if="notification.type == 'mention'">
|
||||
<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
|
||||
<mk-note :note="notification.note"/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@ -105,17 +105,6 @@ export default Vue.extend({
|
||||
getNoteSummary
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onNoteUpdated(note) {
|
||||
switch (this.notification.type) {
|
||||
case 'quote':
|
||||
case 'reply':
|
||||
case 'mention':
|
||||
Vue.set(this.notification, 'note', note);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -10,16 +10,17 @@
|
||||
<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div">
|
||||
<template v-for="(notification, i) in _notifications">
|
||||
<x-notification class="notification" :notification="notification" :key="notification.id"/>
|
||||
<p class="date" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date" :key="notification.id + '-time'">
|
||||
<p class="date" v-if="i != items.length - 1 && notification._date != _notifications[i + 1]._date" :key="notification.id + '-time'">
|
||||
<span><fa icon="angle-up"/>{{ notification._datetext }}</span>
|
||||
<span><fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}</span>
|
||||
</p>
|
||||
</template>
|
||||
</component>
|
||||
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
||||
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? this.$t('@.loading') : this.$t('@.load-more') }}
|
||||
<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMore" :disabled="moreFetching">
|
||||
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? this.$t('@.loading') : this.$t('@.load-more') }}
|
||||
</button>
|
||||
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
|
||||
<p class="empty" v-if="empty">{{ $t('empty') }}</p>
|
||||
<mk-error v-if="error" @retry="init()"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -27,31 +28,38 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import XNotification from './deck.notification.vue';
|
||||
|
||||
const displayLimit = 20;
|
||||
import paging from '../../../common/scripts/paging';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
components: {
|
||||
XNotification
|
||||
},
|
||||
|
||||
inject: ['column', 'isScrollTop', 'count'],
|
||||
|
||||
mixins: [
|
||||
paging({
|
||||
onQueueChanged: (self, q) => {
|
||||
self.count(q.length);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
fetchingMoreNotifications: false,
|
||||
notifications: [],
|
||||
queue: [],
|
||||
moreNotifications: false,
|
||||
connection: null
|
||||
connection: null,
|
||||
pagination: {
|
||||
endpoint: 'i/notifications',
|
||||
limit: 20,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
_notifications(): any[] {
|
||||
return (this.notifications as any).map(notification => {
|
||||
return (this.items as any).map(notification => {
|
||||
const date = new Date(notification.createdAt).getDate();
|
||||
const month = new Date(notification.createdAt).getMonth() + 1;
|
||||
notification._date = date;
|
||||
@ -61,33 +69,12 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
queue(q) {
|
||||
this.count(q.length);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('notification', this.onNotification);
|
||||
|
||||
this.column.$on('top', this.onTop);
|
||||
this.column.$on('bottom', this.onBottom);
|
||||
|
||||
const max = 10;
|
||||
|
||||
this.$root.api('i/notifications', {
|
||||
limit: max + 1
|
||||
}).then(notifications => {
|
||||
if (notifications.length == max + 1) {
|
||||
this.moreNotifications = true;
|
||||
notifications.pop();
|
||||
}
|
||||
|
||||
this.notifications = notifications;
|
||||
this.fetching = false;
|
||||
});
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
@ -98,26 +85,6 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchMoreNotifications() {
|
||||
this.fetchingMoreNotifications = true;
|
||||
|
||||
const max = 20;
|
||||
|
||||
this.$root.api('i/notifications', {
|
||||
limit: max + 1,
|
||||
untilId: this.notifications[this.notifications.length - 1].id
|
||||
}).then(notifications => {
|
||||
if (notifications.length == max + 1) {
|
||||
this.moreNotifications = true;
|
||||
notifications.pop();
|
||||
} else {
|
||||
this.moreNotifications = false;
|
||||
}
|
||||
this.notifications = this.notifications.concat(notifications);
|
||||
this.fetchingMoreNotifications = false;
|
||||
});
|
||||
},
|
||||
|
||||
onNotification(notification) {
|
||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
||||
this.$root.stream.send('readNotification', {
|
||||
@ -126,35 +93,6 @@ export default Vue.extend({
|
||||
|
||||
this.prepend(notification);
|
||||
},
|
||||
|
||||
prepend(notification) {
|
||||
if (this.isScrollTop()) {
|
||||
// Prepend the notification
|
||||
this.notifications.unshift(notification);
|
||||
|
||||
// オーバーフローしたら古い通知は捨てる
|
||||
if (this.notifications.length >= displayLimit) {
|
||||
this.notifications = this.notifications.slice(0, displayLimit);
|
||||
}
|
||||
} else {
|
||||
this.queue.push(notification);
|
||||
}
|
||||
},
|
||||
|
||||
releaseQueue() {
|
||||
for (const n of this.queue) {
|
||||
this.prepend(n);
|
||||
}
|
||||
this.queue = [];
|
||||
},
|
||||
|
||||
onTop() {
|
||||
this.releaseQueue();
|
||||
},
|
||||
|
||||
onBottom() {
|
||||
this.fetchMoreNotifications();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
|
||||
</div>
|
||||
</x-column>
|
||||
</template>
|
||||
@ -16,8 +16,6 @@ import XColumn from './deck.column.vue';
|
||||
import XNotes from './deck.notes.vue';
|
||||
import { genSearchQuery } from '../../../common/scripts/gen-search-query';
|
||||
|
||||
const limit = 20;
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XColumn,
|
||||
@ -26,24 +24,11 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
makePromise: async cursor => this.$root.api('notes/search', {
|
||||
limit: limit + 1,
|
||||
offset: cursor ? cursor : undefined,
|
||||
...(await genSearchQuery(this, this.q))
|
||||
}).then(notes => {
|
||||
if (notes.length == limit + 1) {
|
||||
notes.pop();
|
||||
return {
|
||||
notes: notes,
|
||||
cursor: cursor ? cursor + limit : limit
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
})
|
||||
pagination: {
|
||||
endpoint: 'notes/search',
|
||||
limit: 20,
|
||||
params: () => genSearchQuery(this, this.q)
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
</p>
|
||||
<p class="desc">{{ $t('disabled-timeline.description') }}</p>
|
||||
</div>
|
||||
<x-notes v-else ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
<x-notes v-else ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -15,8 +15,6 @@ import XNotes from './deck.notes.vue';
|
||||
import { faMinusCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('deck'),
|
||||
|
||||
@ -42,7 +40,7 @@ export default Vue.extend({
|
||||
connection: null,
|
||||
disabled: false,
|
||||
faMinusCircle,
|
||||
makePromise: null
|
||||
pagination: null
|
||||
};
|
||||
},
|
||||
|
||||
@ -73,27 +71,17 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
this.makePromise = cursor => this.$root.api(this.endpoint, {
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
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();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
});
|
||||
this.pagination = {
|
||||
endpoint: this.endpoint,
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
|
||||
withFiles: this.mediaOnly,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -30,7 +30,7 @@
|
||||
<ui-container>
|
||||
<template #header><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</template>
|
||||
<div>
|
||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
|
||||
<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')" :key="user.id"/>
|
||||
</div>
|
||||
</ui-container>
|
||||
</div>
|
||||
@ -43,8 +43,6 @@ import XNotes from './deck.notes.vue';
|
||||
import { concat } from '../../../../../prelude/array';
|
||||
import ApexCharts from 'apexcharts';
|
||||
|
||||
const fetchLimit = 10;
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('deck/deck.user-column.vue'),
|
||||
|
||||
@ -63,49 +61,38 @@ export default Vue.extend({
|
||||
return {
|
||||
withFiles: false,
|
||||
images: [],
|
||||
makePromise: null,
|
||||
chart: null as ApexCharts
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
pagination() {
|
||||
return {
|
||||
endpoint: 'users/notes',
|
||||
limit: 10,
|
||||
params: init => ({
|
||||
userId: this.user.id,
|
||||
untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
|
||||
withFiles: this.withFiles,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
includeLocalRenotes: this.$store.state.settings.showLocalRenotes
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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,
|
||||
withFiles: this.withFiles,
|
||||
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();
|
||||
return {
|
||||
notes: notes,
|
||||
more: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
notes: notes,
|
||||
more: false
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetch() {
|
||||
const image = [
|
||||
'image/jpeg',
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="is-remote" v-if="user.host != null">
|
||||
<details>
|
||||
<summary><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}</summary>
|
||||
<a :href="user.url" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
||||
<a :href="user.url" rel="nofollow noopener" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
||||
</details>
|
||||
</div>
|
||||
<header :style="bannerStyle">
|
||||
|
@ -18,24 +18,24 @@
|
||||
</div>
|
||||
</ui-container>
|
||||
|
||||
<mk-user-list v-if="tag != null" :make-promise="tagUsers" :key="`${tag}-local`">
|
||||
<mk-user-list v-if="tag != null" :pagination="tagUsers" :key="`${tag}-local`">
|
||||
<fa :icon="faHashtag" fixed-width/>{{ tag }}
|
||||
</mk-user-list>
|
||||
<mk-user-list v-if="tag != null" :make-promise="tagRemoteUsers" :key="`${tag}-remote`">
|
||||
<mk-user-list v-if="tag != null" :pagination="tagRemoteUsers" :key="`${tag}-remote`">
|
||||
<fa :icon="faHashtag" fixed-width/>{{ tag }} ({{ $t('federated') }})
|
||||
</mk-user-list>
|
||||
|
||||
<template v-if="tag == null">
|
||||
<mk-user-list :make-promise="pinnedUsers">
|
||||
<mk-user-list :pagination="pinnedUsers">
|
||||
<fa :icon="faBookmark" fixed-width/>{{ $t('pinned-users') }}
|
||||
</mk-user-list>
|
||||
<mk-user-list :make-promise="popularUsers">
|
||||
<mk-user-list :pagination="popularUsers">
|
||||
<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
|
||||
</mk-user-list>
|
||||
<mk-user-list :make-promise="recentlyUpdatedUsers">
|
||||
<mk-user-list :pagination="recentlyUpdatedUsers">
|
||||
<fa :icon="faCommentAlt" fixed-width/>{{ $t('recently-updated-users') }}
|
||||
</mk-user-list>
|
||||
<mk-user-list :make-promise="recentlyRegisteredUsers">
|
||||
<mk-user-list :pagination="recentlyRegisteredUsers">
|
||||
<fa :icon="faPlus" fixed-width/>{{ $t('recently-registered-users') }}
|
||||
</mk-user-list>
|
||||
</template>
|
||||
@ -60,24 +60,21 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
pinnedUsers: () => this.$root.api('pinned-users'),
|
||||
popularUsers: () => this.$root.api('users', {
|
||||
pinnedUsers: { endpoint: 'pinned-users' },
|
||||
popularUsers: { endpoint: 'users', limit: 10, params: {
|
||||
state: 'alive',
|
||||
origin: 'local',
|
||||
sort: '+follower',
|
||||
limit: 10
|
||||
}),
|
||||
recentlyUpdatedUsers: () => this.$root.api('users', {
|
||||
} },
|
||||
recentlyUpdatedUsers: { endpoint: 'users', limit: 10, params: {
|
||||
origin: 'local',
|
||||
sort: '+updatedAt',
|
||||
limit: 10
|
||||
}),
|
||||
recentlyRegisteredUsers: () => this.$root.api('users', {
|
||||
} },
|
||||
recentlyRegisteredUsers: { endpoint: 'users', limit: 10, params: {
|
||||
origin: 'local',
|
||||
state: 'alive',
|
||||
sort: '+createdAt',
|
||||
limit: 10
|
||||
}),
|
||||
} },
|
||||
tagsLocal: [],
|
||||
tagsRemote: [],
|
||||
stats: null,
|
||||
@ -88,24 +85,30 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
computed: {
|
||||
tagUsers(): () => Promise<any> {
|
||||
return () => this.$root.api('hashtags/users', {
|
||||
tag: this.tag,
|
||||
state: 'alive',
|
||||
origin: 'local',
|
||||
sort: '+follower',
|
||||
limit: 30
|
||||
});
|
||||
tagUsers(): any {
|
||||
return {
|
||||
endpoint: 'hashtags/users',
|
||||
limit: 30,
|
||||
params: {
|
||||
tag: this.tag,
|
||||
state: 'alive',
|
||||
origin: 'local',
|
||||
sort: '+follower',
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
tagRemoteUsers(): () => Promise<any> {
|
||||
return () => this.$root.api('hashtags/users', {
|
||||
tag: this.tag,
|
||||
state: 'alive',
|
||||
origin: 'remote',
|
||||
sort: '+follower',
|
||||
limit: 30
|
||||
});
|
||||
tagRemoteUsers(): any {
|
||||
return {
|
||||
endpoint: 'hashtags/users',
|
||||
limit: 30,
|
||||
params: {
|
||||
tag: this.tag,
|
||||
state: 'alive',
|
||||
origin: 'remote',
|
||||
sort: '+follower',
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
@ -116,6 +119,10 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$emit('init', {
|
||||
title: this.$t('@.explore'),
|
||||
icon: faHashtag
|
||||
});
|
||||
this.$root.api('hashtags/list', {
|
||||
sort: '+attachedLocalUsers',
|
||||
attachedToLocalUserOnly: true,
|
||||
|
44
src/client/app/common/views/pages/favorites.vue
Normal file
44
src/client/app/common/views/pages/favorites.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<component :is="notesComponent" :pagination="pagination" :extract="items => items.map(item => item.note)"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faStar } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../i18n';
|
||||
//import Progress from '../../../common/scripts/loading';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
props: {
|
||||
platform: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
pagination: {
|
||||
endpoint: 'i/favorites',
|
||||
limit: 10,
|
||||
},
|
||||
|
||||
notesComponent:
|
||||
this.platform === 'desktop' ? () => import('../../../desktop/views/components/detail-notes.vue').then(m => m.default) :
|
||||
this.platform === 'mobile' ? () => import('../../../mobile/views/components/detail-notes.vue').then(m => m.default) :
|
||||
this.platform === 'deck' ? () => import('../deck/deck.notes.vue').then(m => m.default) : null
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$emit('init', {
|
||||
title: this.$t('@.favorites'),
|
||||
icon: faStar
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
44
src/client/app/common/views/pages/featured.vue
Normal file
44
src/client/app/common/views/pages/featured.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<component :is="notesComponent" :pagination="pagination"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faNewspaper } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../i18n';
|
||||
//import Progress from '../../../common/scripts/loading';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
props: {
|
||||
platform: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
pagination: {
|
||||
endpoint: 'notes/featured',
|
||||
limit: 29,
|
||||
},
|
||||
|
||||
notesComponent:
|
||||
this.platform === 'desktop' ? () => import('../../../desktop/views/components/detail-notes.vue').then(m => m.default) :
|
||||
this.platform === 'mobile' ? () => import('../../../mobile/views/components/detail-notes.vue').then(m => m.default) :
|
||||
this.platform === 'deck' ? () => import('../deck/deck.notes.vue').then(m => m.default) : null
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$emit('init', {
|
||||
title: this.$t('@.featured-notes'),
|
||||
icon: faNewspaper
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
74
src/client/app/common/views/pages/follow-requests.vue
Normal file
74
src/client/app/common/views/pages/follow-requests.vue
Normal file
@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-container :body-togglable="true">
|
||||
<template #header>{{ $t('received-follow-requests') }}</template>
|
||||
<div v-if="!fetching">
|
||||
<sequential-entrance animation="entranceFromTop" delay="25" tag="div">
|
||||
<div v-for="req in requests" class="mcbzkkaw">
|
||||
<router-link :key="req.id" :to="req.follower | userPage">
|
||||
<mk-user-name :user="req.follower"/>
|
||||
</router-link>
|
||||
<span>
|
||||
<a @click="accept(req.follower)">{{ $t('accept') }}</a>|<a @click="reject(req.follower)">{{ $t('reject') }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</sequential-entrance>
|
||||
</div>
|
||||
</ui-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import Progress from '../../scripts/loading';
|
||||
import { faUserClock } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/pages/follow-requests.vue'),
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
requests: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$emit('init', {
|
||||
title: this.$t('received-follow-requests'),
|
||||
icon: faUserClock
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
Progress.start();
|
||||
this.$root.api('following/requests/list').then(requests => {
|
||||
this.fetching = false;
|
||||
this.requests = requests;
|
||||
Progress.done();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
accept(user) {
|
||||
this.$root.api('following/requests/accept', { userId: user.id }).then(() => {
|
||||
this.requests = this.requests.filter(r => r.follower.id != user.id);
|
||||
});
|
||||
},
|
||||
reject(user) {
|
||||
this.$root.api('following/requests/reject', { userId: user.id }).then(() => {
|
||||
this.requests = this.requests.filter(r => r.follower.id != user.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mcbzkkaw
|
||||
display flex
|
||||
padding 16px
|
||||
border solid 1px var(--faceDivider)
|
||||
border-radius 4px
|
||||
|
||||
> span
|
||||
margin 0 0 0 auto
|
||||
|
||||
</style>
|
@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<mk-user-list :make-promise="makePromise">{{ $t('@.followers') }}</mk-user-list>
|
||||
</div>
|
||||
<mk-user-list :pagination="pagination" :extract="items => items.map(item => item.follower)">{{ $t('@.followers') }}</mk-user-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -9,31 +7,18 @@ import Vue from 'vue';
|
||||
import parseAcct from '../../../../../misc/acct/parse';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
const fetchLimit = 30;
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
|
||||
data() {
|
||||
return {
|
||||
makePromise: cursor => this.$root.api('users/followers', {
|
||||
...parseAcct(this.$route.params.user),
|
||||
limit: fetchLimit + 1,
|
||||
untilId: cursor ? cursor : undefined,
|
||||
}).then(followings => {
|
||||
if (followings.length == fetchLimit + 1) {
|
||||
followings.pop();
|
||||
return {
|
||||
users: followings.map(following => following.follower),
|
||||
cursor: followings[followings.length - 1].id
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
users: followings.map(following => following.follower),
|
||||
more: false
|
||||
};
|
||||
pagination: {
|
||||
endpoint: 'users/followers',
|
||||
limit: 30,
|
||||
params: {
|
||||
...parseAcct(this.$route.params.user),
|
||||
}
|
||||
}),
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user