Compare commits
87 Commits
Author | SHA1 | Date | |
---|---|---|---|
f6b2f76bbf | |||
1235bef038 | |||
2e11f3a843 | |||
84b7e0bb7d | |||
9f5dc2c0df | |||
e640dbc501 | |||
85db090d9f | |||
9f2d8e1d51 | |||
0c98a90b75 | |||
0047920c1a | |||
e4bb534f20 | |||
3fc04fcdc5 | |||
e542dcac30 | |||
a0b13505a0 | |||
389f9bfea2 | |||
630a534cee | |||
5744c391e6 | |||
b9b05a7401 | |||
359470a263 | |||
3fe934ee62 | |||
3abe632f06 | |||
65961bc15b | |||
12f932d48a | |||
54e9147782 | |||
31b7626d01 | |||
200ebefe92 | |||
9d29a2e85a | |||
c62a225542 | |||
d5d995a3e6 | |||
b7f10fdc10 | |||
cbba03b376 | |||
f84e9c7dc8 | |||
a22ddb1fb9 | |||
0d23ce3d45 | |||
9719387bee | |||
dca110ebaa | |||
136f23c7ad | |||
0963e6d6e1 | |||
712802e682 | |||
abe99c3c73 | |||
d7a3b71028 | |||
10c434f24a | |||
fe46c53ea6 | |||
cdd123dfd3 | |||
a1a3ee44b5 | |||
4e7fbd8967 | |||
a86c419f95 | |||
e3ec0ad97e | |||
75791981ce | |||
e813fe16b9 | |||
42ac7b954d | |||
c1bbf5dab6 | |||
e16dc2a910 | |||
e236c05d79 | |||
454c1e3faf | |||
43daf814df | |||
c40b630530 | |||
7fc0698ecf | |||
4f3c8b940e | |||
1855ab60f1 | |||
af4f1a7bd6 | |||
8646a9c49c | |||
8d7c033cf5 | |||
b8900e32de | |||
d48c25d2c9 | |||
a87c5899c5 | |||
147ad69864 | |||
c146006476 | |||
a0f10d7ca1 | |||
299b91edc4 | |||
95c89ca6db | |||
7fe0d71e7f | |||
fbbb506e86 | |||
ec80b06a45 | |||
41e1619f1f | |||
ba6a9c6a93 | |||
18571c52fb | |||
5d5dfeaa83 | |||
3669d8c0f3 | |||
69d72819c6 | |||
54dcc10250 | |||
1edfce8f73 | |||
675e573a8c | |||
1080fa63a9 | |||
8047086988 | |||
449b9f7fa0 | |||
b7a15bf6ca |
@ -23,6 +23,10 @@ jobs:
|
||||
executor: default
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Ensure package-lock.json
|
||||
command: |
|
||||
[ ! -e package-lock.json ] && echo '{}' > package-lock.json
|
||||
- restore_cache:
|
||||
name: Restore npm package caches
|
||||
keys:
|
||||
@ -35,6 +39,7 @@ jobs:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
npm install
|
||||
npm prune
|
||||
- run:
|
||||
name: Configure
|
||||
command: |
|
||||
@ -50,8 +55,8 @@ jobs:
|
||||
key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- store_artifacts:
|
||||
path: built
|
||||
# - store_artifacts:
|
||||
# path: built
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
@ -98,7 +103,6 @@ jobs:
|
||||
name: Build
|
||||
command: |
|
||||
docker build . | tee docker.log
|
||||
tail -n 1 docker.log | read __Successfully __built tag
|
||||
- when:
|
||||
condition: <<parameters.with_deploy>>
|
||||
steps:
|
||||
@ -107,6 +111,7 @@ jobs:
|
||||
command: |
|
||||
if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
|
||||
then
|
||||
tail -n 1 docker.log | read __Successfully __built tag
|
||||
docker tag $tag misskey/misskey
|
||||
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
|
||||
docker push misskey/misskey
|
||||
@ -126,10 +131,13 @@ workflows:
|
||||
without_redis: "true"
|
||||
requires:
|
||||
- build
|
||||
- docker:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
only: master
|
||||
# - docker:
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore: master
|
||||
- docker:
|
||||
with_deploy: "true"
|
||||
filters:
|
||||
|
@ -1,6 +1,3 @@
|
||||
name: example-instance-name # Name of your instance
|
||||
description: example-description # Description of your instance
|
||||
|
||||
maintainer:
|
||||
name: example-maitainer-name # Your name
|
||||
url: http://example.com/ # Your contact (http or mailto)
|
||||
@ -25,7 +22,7 @@ url: https://example.tld/
|
||||
# +------+ |+-------------+ +----------------+|
|
||||
# +---------------------------------------+
|
||||
#
|
||||
# You need to setup reverse proxy. (eg. Nginx)
|
||||
# You need to setup reverse proxy. (eg. nginx)
|
||||
# You do not define 'https' section.
|
||||
|
||||
# Option 2: Standalone
|
||||
@ -148,6 +145,12 @@ drive:
|
||||
# consumer_key: example-twitter-consumer-key
|
||||
# consumer_secret: example-twitter-consumer-secret-key
|
||||
|
||||
# GitHub integration
|
||||
# You need to set the oauth callback url as : https://<your-misskey-instance>/api/gh/cb
|
||||
#github:
|
||||
# client_id: example-github-client-id
|
||||
# client_secret: example-github-client-secret
|
||||
|
||||
# Ghost
|
||||
# Ghost account is an account used for the purpose of delegating
|
||||
# followers when putting users in the list.
|
||||
@ -164,6 +167,3 @@ drive:
|
||||
# external: true
|
||||
# engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}
|
||||
# timeout: 300000
|
||||
|
||||
# Max allowed note text length in charactors
|
||||
maxNoteTextLength: 1000
|
||||
|
41
.travis.yml
41
.travis.yml
@ -1,41 +0,0 @@
|
||||
# travis file
|
||||
# https://docs.travis-ci.com/user/customizing-the-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
branches:
|
||||
except:
|
||||
- l10n_master
|
||||
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 11.0.0
|
||||
|
||||
env:
|
||||
- CXX=g++-4.8 NODE_ENV=production
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
services:
|
||||
- mongodb
|
||||
- redis-server
|
||||
|
||||
before_script:
|
||||
- npm install
|
||||
|
||||
# 設定ファイルを配置
|
||||
- cp ./.ci/default.yml ./.config
|
||||
- cp ./.ci/test.yml ./.config
|
||||
|
||||
- travis_wait npm run build
|
@ -23,5 +23,5 @@ Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
|
||||
* Test codes are located in `/test`.
|
||||
|
||||
## Continuous integration
|
||||
Misskey uses Travis for automated test.
|
||||
Configuration files are located in `/.travis`.
|
||||
Misskey uses CircleCI for automated test.
|
||||
Configuration files are located in `/.circleci`.
|
||||
|
@ -4,7 +4,6 @@
|
||||
================================================================
|
||||
|
||||
[](https://circleci.com/gh/syuilo/misskey)
|
||||
[![][travis-badge]][travis-link]
|
||||
[![][dependencies-badge]][dependencies-link]
|
||||
[](http://makeapullrequest.com)
|
||||
|
||||
@ -44,7 +43,7 @@ Easiest way to tell your emotions. Misskey allows you to add various type of rea
|
||||
|
||||
<h3 align="left">Interface</h3>
|
||||
<p align="left">
|
||||
No UI fits for everyone. Therefore, Misskey has a highly customizable UI for your taste. You can edit layouts of your timeline, place selectable widgets you can easily move and create your unique home as this place will be your home.
|
||||
Highly customizable UI for your taste. We understand no UI fits for everyone. You can edit layouts of your timeline, place selectable widgets you can easily move and create your unique home as this place will be your home.
|
||||
</p>
|
||||
|
||||
---
|
||||
@ -124,8 +123,6 @@ Misskey is an open-source software licensed under the [GNU AGPLv3](LICENSE).
|
||||
|
||||
[agpl-3.0]: https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
[agpl-3.0-badge]: https://img.shields.io/badge/license-AGPL--3.0-444444.svg?style=flat-square
|
||||
[travis-link]: https://travis-ci.org/syuilo/misskey
|
||||
[travis-badge]: http://img.shields.io/travis/syuilo/misskey/master.svg?style=flat-square
|
||||
[dependencies-link]: https://david-dm.org/syuilo/misskey
|
||||
[dependencies-badge]: https://img.shields.io/david/syuilo/misskey.svg?style=flat-square
|
||||
|
||||
|
@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
|
||||
Please install and setup these softwares:
|
||||
|
||||
#### Dependencies :package:
|
||||
* **[Node.js](https://nodejs.org/en/)**
|
||||
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
|
||||
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
|
||||
|
||||
##### Optional
|
||||
|
@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
|
||||
これらのソフトウェアをインストール・設定してください:
|
||||
|
||||
#### 依存関係 :package:
|
||||
* **[Node.js](https://nodejs.org/en/)**
|
||||
* **[Node.js](https://nodejs.org/en/)** (10.0.0以上)
|
||||
* **[MongoDB](https://www.mongodb.com/)** (3.6以上)
|
||||
|
||||
##### オプション
|
||||
|
@ -21,7 +21,6 @@ import * as htmlmin from 'gulp-htmlmin';
|
||||
const uglifyes = require('uglify-es');
|
||||
|
||||
const locales = require('./locales');
|
||||
import { fa } from './src/misc/fa';
|
||||
|
||||
const uglify = uglifyComposer(uglifyes, console);
|
||||
|
||||
@ -164,8 +163,7 @@ gulp.task('build:client:pug', [
|
||||
gulp.src('./src/client/app/base.pug')
|
||||
.pipe(pug({
|
||||
locals: {
|
||||
themeColor: constants.themeColor,
|
||||
facss: fa.dom.css()
|
||||
themeColor: constants.themeColor
|
||||
}
|
||||
}))
|
||||
.pipe(htmlmin({
|
||||
|
@ -131,6 +131,7 @@ common:
|
||||
show-full-acct: "ユーザー名のホストを省略しない"
|
||||
reduce-motion: "UIの動きを減らす"
|
||||
this-setting-is-this-device-only: "このデバイスのみ"
|
||||
use-os-default-emojis: "OS標準の絵文字を使用"
|
||||
|
||||
do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
|
||||
|
||||
@ -417,6 +418,7 @@ common/views/components/signin.vue:
|
||||
signin: "サインイン"
|
||||
or: "または"
|
||||
signin-with-twitter: "Twitterでログイン"
|
||||
signin-with-github: "GitHubでログイン"
|
||||
login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
|
||||
|
||||
common/views/components/signup.vue:
|
||||
@ -460,6 +462,14 @@ common/views/components/twitter-setting.vue:
|
||||
connect: "Twitterと接続する"
|
||||
disconnect: "切断する"
|
||||
|
||||
common/views/components/github-setting.vue:
|
||||
description: "お使いのGitHubアカウントをお使いのMisskeyアカウントに接続しておくと、プロフィールでGitHubアカウント情報が表示されるようになったり、GitHubを用いた便利なサインインを利用できるようになります。"
|
||||
connected-to: "次のGitHubアカウントに接続されています"
|
||||
detail: "詳細..."
|
||||
reconnect: "再接続する"
|
||||
connect: "GitHubと接続する"
|
||||
disconnect: "切断する"
|
||||
|
||||
common/views/components/uploader.vue:
|
||||
waiting: "待機中"
|
||||
|
||||
@ -599,32 +609,6 @@ desktop/views/components/calendar.vue:
|
||||
next: "次の月"
|
||||
go: "クリックして時間遡行"
|
||||
|
||||
desktop/views/components/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
per-hour: "1時間ごと"
|
||||
federation: "フェデレーション"
|
||||
notes: "投稿"
|
||||
users: "ユーザー"
|
||||
drive: "ドライブ"
|
||||
network: "ネットワーク"
|
||||
charts:
|
||||
federation-instances: "インスタンスの増減"
|
||||
federation-instances-total: "インスタンスの積算"
|
||||
notes: "投稿の増減 (統合)"
|
||||
local-notes: "投稿の増減 (ローカル)"
|
||||
remote-notes: "投稿の増減 (リモート)"
|
||||
notes-total: "投稿の積算"
|
||||
users: "ユーザーの増減"
|
||||
users-total: "ユーザーの積算"
|
||||
drive: "ドライブ使用量の増減"
|
||||
drive-total: "ドライブ使用量の積算"
|
||||
drive-files: "ドライブのファイル数の増減"
|
||||
drive-files-total: "ドライブのファイル数の積算"
|
||||
network-requests: "リクエスト"
|
||||
network-time: "応答時間"
|
||||
network-usage: "通信量"
|
||||
|
||||
desktop/views/components/choose-file-from-drive-window.vue:
|
||||
choose-file: "ファイル選択中"
|
||||
upload: "PCからドライブにファイルをアップロード"
|
||||
@ -1088,10 +1072,18 @@ admin/views/dashboard.vue:
|
||||
instances: "インスタンス"
|
||||
this-instance: "このインスタンス"
|
||||
federated: "連合"
|
||||
|
||||
admin/views/instance.vue:
|
||||
instance: "インスタンス"
|
||||
instance-name: "インスタンス名"
|
||||
instance-description: "インスタンスの紹介"
|
||||
banner-url: "バナー画像URL"
|
||||
max-note-text-length: "投稿の最大文字数"
|
||||
disable-registration: "ユーザー登録の受付を停止する"
|
||||
disable-local-timeline: "ローカルタイムラインを無効にする"
|
||||
invite: "招待"
|
||||
banner-url: "Banner URL"
|
||||
disableRegistration: "Disable new user registration"
|
||||
disableLocalTimeline: "Disable the local timeline"
|
||||
save: "保存"
|
||||
saved: "保存しました"
|
||||
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
@ -1142,10 +1134,16 @@ admin/views/emoji.vue:
|
||||
aliases-desc: "スペースで区切って複数設定できます。"
|
||||
url: "絵文字画像URL"
|
||||
add: "追加"
|
||||
info: "50KB以下のPNG画像をおすすめします。"
|
||||
added: "絵文字を登録しました"
|
||||
emojis:
|
||||
title: "絵文字一覧"
|
||||
update: "更新"
|
||||
remove: "削除"
|
||||
updated: "更新しました"
|
||||
remove-emoji:
|
||||
are-you-sure: "「$1」を削除しますか?"
|
||||
removed: "削除しました"
|
||||
|
||||
admin/views/announcements.vue:
|
||||
announcements: "お知らせ"
|
||||
@ -1154,6 +1152,10 @@ admin/views/announcements.vue:
|
||||
add: "追加"
|
||||
title: "タイトル"
|
||||
text: "内容"
|
||||
saved: "保存しました"
|
||||
_remove:
|
||||
are-you-sure: "「$1」を削除しますか?"
|
||||
removed: "削除しました"
|
||||
|
||||
admin/views/hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
@ -1173,12 +1175,6 @@ desktop/views/pages/deck/deck.user-column.vue:
|
||||
pinned-notes: "ピン留めされた投稿"
|
||||
push-to-a-list: "リストに追加"
|
||||
|
||||
desktop/views/pages/stats/stats.vue:
|
||||
all-users: "全てのユーザー"
|
||||
original-users: "このインスタンスのユーザー"
|
||||
all-notes: "全ての投稿"
|
||||
original-notes: "このインスタンスの投稿"
|
||||
|
||||
desktop/views/pages/welcome.vue:
|
||||
about: "詳しく..."
|
||||
gotit: "わかった"
|
||||
@ -1560,6 +1556,10 @@ mobile/views/pages/settings.vue:
|
||||
twitter-connect: "Twitterアカウントに接続する"
|
||||
twitter-reconnect: "再接続する"
|
||||
twitter-disconnect: "切断する"
|
||||
github: "GitHub連携"
|
||||
github-connect: "GitHubアカウントに接続する"
|
||||
github-reconnect: "再接続する"
|
||||
github-disconnect: "切断する"
|
||||
update: "Misskey Update"
|
||||
version: "バージョン:"
|
||||
latest-version: "最新のバージョン:"
|
||||
|
@ -186,7 +186,7 @@ common:
|
||||
stack-left: "左に重ねんで!"
|
||||
pop-right: "右に出すで!"
|
||||
dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
|
||||
ai-chan-kawaii: "藍ちゃかわいい"
|
||||
ai-chan-kawaii: "藍ちゃめっさべっぴんさんや"
|
||||
auth/views/form.vue:
|
||||
share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?"
|
||||
permission-ask: "このアプリは次の権限を要求してんで:"
|
||||
@ -744,7 +744,7 @@ desktop/views/components/settings.vue:
|
||||
apps: "アプリ"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "守護神セキュリティ"
|
||||
security: "セキュリティ"
|
||||
signin: "こんな感じでサインインしたらしいで"
|
||||
password: "パスワード"
|
||||
2fa: "二段階認証"
|
||||
@ -873,15 +873,15 @@ common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
no-muted-users: "ミュートしとるユーザーはおらんで"
|
||||
no-blocked-users: "ブロックしとるユーザーはおらんで"
|
||||
common/views/components/password-settings.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
enter-new-password: "新しいパスワードを入力してください"
|
||||
enter-new-password-again: "もう一度新しいパスワードを入力してください"
|
||||
not-match: "新しいパスワードが一致しません"
|
||||
changed: "パスワードを変更しました"
|
||||
reset: "パスワード変える"
|
||||
enter-current-password: "今のパスワードを入れてや"
|
||||
enter-new-password: "こんどのパスワード入れてや"
|
||||
enter-new-password-again: "もっぺん入れてや"
|
||||
not-match: "パスワードがおうとらん"
|
||||
changed: "パスワード変えたわ"
|
||||
desktop/views/components/sub-note-content.vue:
|
||||
private: "この投稿は見せられへんわ"
|
||||
deleted: "この投稿なんか無くなってもうたわ"
|
||||
@ -953,7 +953,7 @@ admin/views/index.vue:
|
||||
emoji: "カスタム絵文字"
|
||||
users: "ユーザー"
|
||||
update: "更新"
|
||||
announcements: "お知らせ"
|
||||
announcements: "知っといてや"
|
||||
hashtags: "ハッシュタグ"
|
||||
back-to-misskey: "Misskeyに戻る"
|
||||
admin/views/dashboard.vue:
|
||||
@ -962,9 +962,9 @@ admin/views/dashboard.vue:
|
||||
notes: "投稿"
|
||||
drive: "ドライブ"
|
||||
instances: "インスタンス"
|
||||
this-instance: "このインスタンス"
|
||||
this-instance: "ワイのインスタンス"
|
||||
federated: "連合"
|
||||
invite: "招待"
|
||||
invite: "来てや"
|
||||
banner-url: "Banner URL"
|
||||
disableRegistration: "Disable new user registration"
|
||||
disableLocalTimeline: "Disable the local timeline"
|
||||
@ -980,7 +980,7 @@ admin/views/charts.vue:
|
||||
charts:
|
||||
federation-instances: "インスタンスの増減"
|
||||
federation-instances-total: "インスタンスの積算"
|
||||
notes: "投稿の増減 (統合)"
|
||||
notes: "投稿の増減(統合)"
|
||||
local-notes: "投稿の増減 (ローカル)"
|
||||
remote-notes: "投稿の増減 (リモート)"
|
||||
notes-total: "投稿の積算"
|
||||
@ -1387,7 +1387,7 @@ mobile/views/pages/user.vue:
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
unblock: "ブロックやめたる"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近儲かりまっか?"
|
||||
images: "画像"
|
||||
|
17362
package-lock.json
generated
17362
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.38.1",
|
||||
"clientVersion": "1.0.11482",
|
||||
"version": "10.39.1",
|
||||
"clientVersion": "1.0.11569",
|
||||
"codename": "nighthike",
|
||||
"main": "./built/index.js",
|
||||
"private": true,
|
||||
@ -20,10 +20,11 @@
|
||||
"format": "gulp format"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.6",
|
||||
"@fortawesome/free-brands-svg-icons": "5.4.1",
|
||||
"@fortawesome/free-regular-svg-icons": "5.4.1",
|
||||
"@fortawesome/free-solid-svg-icons": "5.4.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.8",
|
||||
"@fortawesome/free-brands-svg-icons": "5.5.0",
|
||||
"@fortawesome/free-regular-svg-icons": "5.5.0",
|
||||
"@fortawesome/free-solid-svg-icons": "5.5.0",
|
||||
"@fortawesome/vue-fontawesome": "0.1.2",
|
||||
"@koa/cors": "2.2.2",
|
||||
"@prezzemolo/rap": "0.1.2",
|
||||
"@prezzemolo/zip": "0.0.3",
|
||||
@ -62,6 +63,7 @@
|
||||
"@types/mongodb": "3.1.12",
|
||||
"@types/ms": "0.7.30",
|
||||
"@types/node": "10.12.2",
|
||||
"@types/oauth": "0.9.1",
|
||||
"@types/portscanner": "2.1.0",
|
||||
"@types/pug": "2.0.4",
|
||||
"@types/qrcode": "1.3.0",
|
||||
@ -95,7 +97,6 @@
|
||||
"chai": "4.2.0",
|
||||
"chai-http": "4.2.0",
|
||||
"chalk": "2.4.1",
|
||||
"chart.js": "2.7.3",
|
||||
"commander": "2.19.0",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "1.0.1",
|
||||
@ -211,12 +212,10 @@
|
||||
"uuid": "3.3.2",
|
||||
"v-animate-css": "0.0.2",
|
||||
"vue": "2.5.17",
|
||||
"vue-chartjs": "3.4.0",
|
||||
"vue-color": "2.7.0",
|
||||
"vue-content-loading": "1.5.3",
|
||||
"vue-cropperjs": "2.2.2",
|
||||
"vue-js-modal": "1.3.26",
|
||||
"vue-json-tree-view": "2.1.4",
|
||||
"vue-loader": "15.4.2",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-style-loader": "4.1.2",
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="cdeuzmsthagexbkpofbmatmugjuvogfb">
|
||||
<ui-card>
|
||||
<div slot="title">%fa:broadcast-tower% %i18n:@announcements%</div>
|
||||
<div slot="title"><fa icon="broadcast-tower"/> %i18n:@announcements%</div>
|
||||
<section v-for="(announcement, i) in announcements" class="fit-top">
|
||||
<ui-input v-model="announcement.title" @change="save">
|
||||
<span>%i18n:@title%</span>
|
||||
@ -9,13 +9,13 @@
|
||||
<ui-textarea v-model="announcement.text">
|
||||
<span>%i18n:@text%</span>
|
||||
</ui-textarea>
|
||||
<ui-button-group>
|
||||
<ui-button inline @click="save">%fa:save R% %i18n:@save%</ui-button>
|
||||
<ui-button inline @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
|
||||
</ui-button-group>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="save()">%fa:save R% %i18n:@save%</ui-button>
|
||||
<ui-button @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
|
||||
</ui-horizon-group>
|
||||
</section>
|
||||
<section>
|
||||
<ui-button @click="add">%fa:plus% %i18n:@add%</ui-button>
|
||||
<ui-button @click="add"><fa icon="plus"/> %i18n:@add%</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
@ -46,19 +46,45 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
remove(i) {
|
||||
this.announcements = this.announcements.filter((_, j) => j !== i);
|
||||
this.save();
|
||||
this.$swal({
|
||||
type: 'warning',
|
||||
text: '%i18n:@_remove.are-you-sure%'.replace('$1', this.announcements.find((_, j) => j == i).title),
|
||||
showCancelButton: true
|
||||
}).then(res => {
|
||||
if (!res.value) return;
|
||||
this.announcements = this.announcements.filter((_, j) => j !== i);
|
||||
this.save(true);
|
||||
this.$swal({
|
||||
type: 'success',
|
||||
text: '%i18n:@_remove.removed%'
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
save() {
|
||||
save(silent) {
|
||||
(this as any).api('admin/update-meta', {
|
||||
broadcasts: this.announcements
|
||||
}).then(() => {
|
||||
(this as any).os.apis.dialog({ text: `Saved` });
|
||||
if (!silent) {
|
||||
this.$swal({
|
||||
type: 'success',
|
||||
text: '%i18n:@saved%'
|
||||
});
|
||||
}
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
this.$swal({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.cdeuzmsthagexbkpofbmatmugjuvogfb
|
||||
@media (min-width 500px)
|
||||
padding 16px
|
||||
|
||||
</style>
|
||||
|
@ -3,10 +3,10 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>%fa:exchange-alt% In/Out</th>
|
||||
<th>%fa:server% Host</th>
|
||||
<th>%fa:bolt% Activity</th>
|
||||
<th>%fa:user% Actor</th>
|
||||
<th><fa icon="exchange-alt"/> In/Out</th>
|
||||
<th><fa icon="server"/> Host</th>
|
||||
<th><fa icon="bolt"/> Activity</th>
|
||||
<th><fa icon="user"/> Actor</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -63,11 +63,11 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
.hyhctythnmwihguaaapnbrbszsjqxpio
|
||||
display block
|
||||
padding 16px
|
||||
padding 12px 16px 16px 16px
|
||||
height 250px
|
||||
overflow auto
|
||||
overflow hidden
|
||||
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
|
||||
background var(--face)
|
||||
background var(--adminDashboardCardBg)
|
||||
border-radius 8px
|
||||
|
||||
> table
|
||||
@ -76,10 +76,11 @@ export default Vue.extend({
|
||||
overflow auto
|
||||
border-spacing 0
|
||||
border-collapse collapse
|
||||
color #555
|
||||
color var(--adminDashboardCardFg)
|
||||
font-size 14px
|
||||
|
||||
thead
|
||||
border-bottom solid 2px #eee
|
||||
border-bottom solid 1px var(--adminDashboardCardDivider)
|
||||
|
||||
tr
|
||||
th
|
||||
@ -89,7 +90,7 @@ export default Vue.extend({
|
||||
tbody
|
||||
tr
|
||||
&:nth-child(odd)
|
||||
background #fbfbfb
|
||||
background rgba(0, 0, 0, 0.025)
|
||||
|
||||
th, td
|
||||
padding 8px 16px
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="qvgidhudpqhjttdhxubzuyrhyzgslujw">
|
||||
<header>
|
||||
<b>%fa:chart-bar R% %i18n:@title%:</b>
|
||||
<b><fa :icon="['far', 'chart-bar']"/> %i18n:@title%:</b>
|
||||
<select v-model="src">
|
||||
<optgroup label="%i18n:@federation%">
|
||||
<option value="federation-instances">%i18n:@charts.federation-instances%</option>
|
||||
@ -39,6 +39,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import * as ApexCharts from 'apexcharts';
|
||||
|
||||
const limit = 90;
|
||||
@ -147,7 +148,7 @@ export default Vue.extend({
|
||||
this.chartInstance.destroy();
|
||||
}
|
||||
|
||||
this.chartInstance = new ApexCharts(this.$refs.chart, Object.assign({
|
||||
this.chartInstance = new ApexCharts(this.$refs.chart, {
|
||||
chart: {
|
||||
type: 'area',
|
||||
height: 300,
|
||||
@ -168,17 +169,41 @@ export default Vue.extend({
|
||||
},
|
||||
grid: {
|
||||
clipMarkers: false,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
width: 2
|
||||
},
|
||||
legend: {
|
||||
labels: {
|
||||
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime'
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
style: {
|
||||
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
}
|
||||
},
|
||||
axisBorder: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
axisTicks: {
|
||||
color: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
}
|
||||
}, this.data));
|
||||
labels: {
|
||||
formatter: this.data.bytes ? v => Vue.filter('bytes')(v, 0) : v => Vue.filter('number')(v),
|
||||
style: {
|
||||
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
|
||||
}
|
||||
}
|
||||
},
|
||||
series: this.data.series
|
||||
});
|
||||
|
||||
this.chartInstance.render();
|
||||
},
|
||||
@ -286,6 +311,7 @@ export default Vue.extend({
|
||||
|
||||
driveChart(): any {
|
||||
return {
|
||||
bytes: true,
|
||||
series: [{
|
||||
name: 'All',
|
||||
data: this.format(
|
||||
@ -314,6 +340,7 @@ export default Vue.extend({
|
||||
|
||||
driveTotalChart(): any {
|
||||
return {
|
||||
bytes: true,
|
||||
series: [{
|
||||
name: 'Combined',
|
||||
data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
|
||||
@ -396,6 +423,7 @@ export default Vue.extend({
|
||||
|
||||
networkUsageChart(): any {
|
||||
return {
|
||||
bytes: true,
|
||||
series: [{
|
||||
name: 'Incoming',
|
||||
data: this.format(this.stats.network.incomingBytes)
|
||||
@ -424,8 +452,8 @@ export default Vue.extend({
|
||||
margin 0 8px
|
||||
padding 0 0 8px 0
|
||||
font-size 1em
|
||||
color #555
|
||||
border-bottom solid 1px #eee
|
||||
color var(--adminDashboardCardFg)
|
||||
border-bottom solid 1px var(--adminDashboardCardDivider)
|
||||
|
||||
> b
|
||||
margin-right 8px
|
||||
|
@ -2,14 +2,14 @@
|
||||
<div class="zyknedwtlthezamcjlolyusmipqmjgxz">
|
||||
<div>
|
||||
<header>
|
||||
<span>%fa:microchip% CPU <span>{{ cpuP }}%</span></span>
|
||||
<span><fa icon="microchip"/> CPU <span>{{ cpuP }}%</span></span>
|
||||
<span v-if="meta">{{ meta.cpu.model }}</span>
|
||||
</header>
|
||||
<div ref="cpu"></div>
|
||||
</div>
|
||||
<div>
|
||||
<header>
|
||||
<span>%fa:memory% MEM <span>{{ memP }}%</span></span>
|
||||
<span><fa icon="memory"/> MEM <span>{{ memP }}%</span></span>
|
||||
<span v-if="meta"></span>
|
||||
</header>
|
||||
<div ref="mem"></div>
|
||||
@ -79,6 +79,7 @@ export default Vue.extend({
|
||||
},
|
||||
grid: {
|
||||
clipMarkers: false,
|
||||
borderColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
@ -153,7 +154,7 @@ export default Vue.extend({
|
||||
display flex
|
||||
padding 0 8px
|
||||
margin-bottom -16px
|
||||
color #555
|
||||
color var(--adminDashboardCardFg)
|
||||
font-size 14px
|
||||
|
||||
> span
|
||||
@ -167,4 +168,13 @@ export default Vue.extend({
|
||||
> div
|
||||
margin-bottom -10px
|
||||
|
||||
@media (max-width 1000px)
|
||||
display block
|
||||
margin-bottom 26px
|
||||
|
||||
> div
|
||||
&:first-child
|
||||
margin-right 0
|
||||
margin-bottom 26px
|
||||
|
||||
</style>
|
||||
|
@ -11,54 +11,54 @@
|
||||
<div v-if="stats" class="stats">
|
||||
<div>
|
||||
<div>
|
||||
<div>%fa:user%</div>
|
||||
<div><fa icon="user"/></div>
|
||||
<div>
|
||||
<span>%i18n:@accounts%</span>
|
||||
<b class="primary">{{ stats.originalUsersCount | number }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>%fa:home% %i18n:@this-instance%</span>
|
||||
<span @click="setChartSrc('users')">%fa:chart-bar R%</span>
|
||||
<span><fa icon="home"/> %i18n:@this-instance%</span>
|
||||
<span @click="setChartSrc('users')"><fa :icon="['far', 'chart-bar']"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div>%fa:pencil-alt%</div>
|
||||
<div><fa icon="pencil-alt"/></div>
|
||||
<div>
|
||||
<span>%i18n:@notes%</span>
|
||||
<b class="primary">{{ stats.originalNotesCount | number }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>%fa:home% %i18n:@this-instance%</span>
|
||||
<span @click="setChartSrc('notes')">%fa:chart-bar R%</span>
|
||||
<span><fa icon="home"/> %i18n:@this-instance%</span>
|
||||
<span @click="setChartSrc('notes')"><fa :icon="['far', 'chart-bar']"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div>%fa:database%</div>
|
||||
<div><fa icon="database"/></div>
|
||||
<div>
|
||||
<span>%i18n:@drive%</span>
|
||||
<b>{{ stats.driveUsageLocal | bytes }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>%fa:home% %i18n:@this-instance%</span>
|
||||
<span @click="setChartSrc('drive')">%fa:chart-bar R%</span>
|
||||
<span><fa icon="home"/> %i18n:@this-instance%</span>
|
||||
<span @click="setChartSrc('drive')"><fa :icon="['far', 'chart-bar']"/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div>%fa:hdd R%</div>
|
||||
<div><fa :icon="['far', 'hdd']"/></div>
|
||||
<div>
|
||||
<span>%i18n:@instances%</span>
|
||||
<b>{{ stats.instances | number }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>%fa:globe% %i18n:@federated%</span>
|
||||
<span @click="setChartSrc('federation-instances-total')">%fa:chart-bar R%</span>
|
||||
<span><fa icon="globe"/> %i18n:@federated%</span>
|
||||
<span @click="setChartSrc('federation-instances-total')"><fa :icon="['far', 'chart-bar']"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -124,17 +124,28 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.obdskegsannmntldydackcpzezagxqfy
|
||||
padding 16px
|
||||
|
||||
@media (min-width 500px)
|
||||
padding 32px
|
||||
|
||||
> header
|
||||
display flex
|
||||
margin-bottom 16px
|
||||
padding-bottom 16px
|
||||
border-bottom solid 1px #ccc
|
||||
color #777
|
||||
border-bottom solid 1px var(--adminDashboardHeaderBorder)
|
||||
color var(--adminDashboardHeaderFg)
|
||||
font-size 14px
|
||||
white-space nowrap
|
||||
|
||||
@media (max-width 1000px)
|
||||
display none
|
||||
|
||||
> p
|
||||
display inline
|
||||
display block
|
||||
margin 0 32px 0 0
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
|
||||
> b
|
||||
&:after
|
||||
@ -152,11 +163,10 @@ export default Vue.extend({
|
||||
|
||||
> div
|
||||
flex 1
|
||||
max-width 300px
|
||||
margin-right 16px
|
||||
color var(--text)
|
||||
color var(--adminDashboardCardFg)
|
||||
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
|
||||
background var(--face)
|
||||
background var(--adminDashboardCardBg)
|
||||
border-radius 8px
|
||||
|
||||
&:last-child
|
||||
@ -192,7 +202,7 @@ export default Vue.extend({
|
||||
> div:last-child
|
||||
display flex
|
||||
padding 6px 16px
|
||||
border-top solid 1px #eee
|
||||
border-top solid 1px var(--adminDashboardCardDivider)
|
||||
|
||||
> span
|
||||
font-size 70%
|
||||
@ -202,6 +212,21 @@ export default Vue.extend({
|
||||
margin-left auto
|
||||
cursor pointer
|
||||
|
||||
@media (max-width 900px)
|
||||
display grid
|
||||
grid-template-columns 1fr 1fr
|
||||
grid-template-rows 1fr 1fr
|
||||
gap 16px
|
||||
|
||||
> div
|
||||
margin-right 0
|
||||
|
||||
@media (max-width 500px)
|
||||
display block
|
||||
|
||||
> div:not(:last-child)
|
||||
margin-bottom 16px
|
||||
|
||||
> .charts
|
||||
margin-bottom 16px
|
||||
|
||||
|
@ -1,42 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="tumhkfkmgtvzljezfvmgkeurkfncshbe">
|
||||
<ui-card>
|
||||
<div slot="title">%fa:plus% %i18n:@add-emoji.title%</div>
|
||||
<div slot="title"><fa icon="plus"/> %i18n:@add-emoji.title%</div>
|
||||
<section class="fit-top">
|
||||
<ui-input v-model="name">
|
||||
<span>%i18n:@add-emoji.name%</span>
|
||||
<span slot="text">%i18n:@add-emoji.name-desc%</span>
|
||||
</ui-input>
|
||||
<ui-input v-model="aliases">
|
||||
<span>%i18n:@add-emoji.aliases%</span>
|
||||
<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
|
||||
</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="name">
|
||||
<span>%i18n:@add-emoji.name%</span>
|
||||
<span slot="text">%i18n:@add-emoji.name-desc%</span>
|
||||
</ui-input>
|
||||
<ui-input v-model="aliases">
|
||||
<span>%i18n:@add-emoji.aliases%</span>
|
||||
<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-input v-model="url">
|
||||
<span>%i18n:@add-emoji.url%</span>
|
||||
</ui-input>
|
||||
<ui-info>%i18n:@add-emoji.info%</ui-info>
|
||||
<ui-button @click="add">%i18n:@add-emoji.add%</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<div slot="title">%fa:grin R% %i18n:@emojis.title%</div>
|
||||
<div slot="title"><fa :icon="['far', 'grin']"/> %i18n:@emojis.title%</div>
|
||||
<section v-for="emoji in emojis">
|
||||
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
|
||||
<ui-input v-model="emoji.name">
|
||||
<span>%i18n:@add-emoji.name%</span>
|
||||
<span slot="text">%i18n:@add-emoji.name-desc%</span>
|
||||
</ui-input>
|
||||
<ui-input v-model="emoji.aliases">
|
||||
<span>%i18n:@add-emoji.aliases%</span>
|
||||
<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
|
||||
</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="emoji.name">
|
||||
<span>%i18n:@add-emoji.name%</span>
|
||||
</ui-input>
|
||||
<ui-input v-model="emoji.aliases">
|
||||
<span>%i18n:@add-emoji.aliases%</span>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-input v-model="emoji.url">
|
||||
<span>%i18n:@add-emoji.url%</span>
|
||||
</ui-input>
|
||||
<ui-button-group>
|
||||
<ui-button inline @click="updateEmoji(emoji)">%fa:save R% %i18n:@emojis.update%</ui-button>
|
||||
<ui-button inline @click="removeEmoji(emoji)">%fa:trash-alt R% %i18n:@emojis.remove%</ui-button>
|
||||
</ui-button-group>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="updateEmoji(emoji)"><fa :icon="['far', 'save']"/> %i18n:@emojis.update%</ui-button>
|
||||
<ui-button @click="removeEmoji(emoji)"><fa :icon="['far', 'trash-alt']"/> %i18n:@emojis.remove%</ui-button>
|
||||
</ui-horizon-group>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
@ -64,17 +67,24 @@ export default Vue.extend({
|
||||
(this as any).api('admin/emoji/add', {
|
||||
name: this.name,
|
||||
url: this.url,
|
||||
aliases: this.aliases.split(' ')
|
||||
aliases: this.aliases.split(' ').filter(x => x.length > 0)
|
||||
}).then(() => {
|
||||
(this as any).os.apis.dialog({ text: `Added` });
|
||||
this.$swal({
|
||||
type: 'success',
|
||||
text: '%i18n:@add-emoji.added%'
|
||||
});
|
||||
this.fetchEmojis();
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
this.$swal({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
fetchEmojis() {
|
||||
(this as any).api('admin/emoji/list').then(emojis => {
|
||||
emojis.reverse();
|
||||
emojis.forEach(e => e.aliases = (e.aliases || []).join(' '));
|
||||
this.emojis = emojis;
|
||||
});
|
||||
@ -85,24 +95,51 @@ export default Vue.extend({
|
||||
id: emoji.id,
|
||||
name: emoji.name,
|
||||
url: emoji.url,
|
||||
aliases: emoji.aliases.split(' ')
|
||||
aliases: emoji.aliases.split(' ').filter(x => x.length > 0)
|
||||
}).then(() => {
|
||||
(this as any).os.apis.dialog({ text: `Updated` });
|
||||
this.$swal({
|
||||
type: 'success',
|
||||
text: '%i18n:@updated%'
|
||||
});
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
this.$swal({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
removeEmoji(emoji) {
|
||||
(this as any).api('admin/emoji/remove', {
|
||||
id: emoji.id
|
||||
}).then(() => {
|
||||
(this as any).os.apis.dialog({ text: `Removed` });
|
||||
this.fetchEmojis();
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
this.$swal({
|
||||
type: 'warning',
|
||||
text: '%i18n:@remove-emoji.are-you-sure%'.replace('$1', emoji.name),
|
||||
showCancelButton: true
|
||||
}).then(res => {
|
||||
if (!res.value) return;
|
||||
|
||||
(this as any).api('admin/emoji/remove', {
|
||||
id: emoji.id
|
||||
}).then(() => {
|
||||
this.$swal({
|
||||
type: 'success',
|
||||
text: '%i18n:@remove-emoji.removed%'
|
||||
});
|
||||
this.fetchEmojis();
|
||||
}).catch(e => {
|
||||
this.$swal({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.tumhkfkmgtvzljezfvmgkeurkfncshbe
|
||||
@media (min-width 500px)
|
||||
padding 16px
|
||||
|
||||
</style>
|
||||
|
@ -29,9 +29,9 @@ export default Vue.extend({
|
||||
(this as any).api('admin/update-meta', {
|
||||
hidedTags: this.hidedTags.split('\n')
|
||||
}).then(() => {
|
||||
(this as any).os.apis.dialog({ text: `Saved` });
|
||||
//(this as any).os.apis.dialog({ text: `Saved` });
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
//(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,15 @@
|
||||
<template>
|
||||
<div class="mk-admin">
|
||||
<nav>
|
||||
<div class="mk-admin" :class="{ isMobile }">
|
||||
<header v-show="isMobile">
|
||||
<button class="nav" @click="navOpend = true"><fa icon="bars"/></button>
|
||||
<span>MisskeyMyAdmin</span>
|
||||
</header>
|
||||
<div class="nav-backdrop"
|
||||
v-if="navOpend && isMobile"
|
||||
@click="navOpend = false"
|
||||
@touchstart="navOpend = false"
|
||||
></div>
|
||||
<nav v-show="navOpend">
|
||||
<div class="mi">
|
||||
<img svg-inline src="../assets/header-icon.svg"/>
|
||||
</div>
|
||||
@ -9,18 +18,18 @@
|
||||
<p class="name">{{ $store.state.i | userName }}</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:home .fw%%i18n:@dashboard%</li>
|
||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }">%fa:cog .fw%%i18n:@instance%</li>
|
||||
<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
|
||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }">%fa:grin R .fw%%i18n:@emoji%</li>
|
||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
|
||||
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }">%fa:hashtag .fw%%i18n:@hashtags%</li>
|
||||
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>%i18n:@dashboard%</li>
|
||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>%i18n:@instance%</li>
|
||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>%i18n:@users%</li>
|
||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa icon="grin R" fixed-width/>%i18n:@emoji%</li>
|
||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>%i18n:@announcements%</li>
|
||||
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>%i18n:@hashtags%</li>
|
||||
|
||||
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:common.drive%</li> -->
|
||||
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>%i18n:common.drive%</li> -->
|
||||
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
|
||||
</ul>
|
||||
<div class="back-to-misskey">
|
||||
<a href="/">%fa:arrow-left% %i18n:@back-to-misskey%</a>
|
||||
<a href="/"><fa icon="arrow-left"/> %i18n:@back-to-misskey%</a>
|
||||
</div>
|
||||
<div class="version">
|
||||
<small>Misskey {{ version }}</small>
|
||||
@ -49,6 +58,10 @@ import XAnnouncements from "./announcements.vue";
|
||||
import XHashtags from "./hashtags.vue";
|
||||
import XUsers from "./users.vue";
|
||||
|
||||
// Detect the user agent
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XDashboard,
|
||||
@ -58,10 +71,15 @@ export default Vue.extend({
|
||||
XHashtags,
|
||||
XUsers
|
||||
},
|
||||
provide: {
|
||||
isMobile
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
page: 'dashboard',
|
||||
version
|
||||
version,
|
||||
isMobile,
|
||||
navOpend: !isMobile
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -74,12 +92,46 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus">
|
||||
.mk-admin
|
||||
$headerHeight = 48px
|
||||
|
||||
display flex
|
||||
height 100%
|
||||
|
||||
> header
|
||||
position fixed
|
||||
top 0
|
||||
z-index 10000
|
||||
width 100%
|
||||
color var(--mobileHeaderFg)
|
||||
background-color var(--mobileHeaderBg)
|
||||
box-shadow 0 1px 0 rgba(#000, 0.075)
|
||||
|
||||
&, *
|
||||
user-select none
|
||||
|
||||
> span
|
||||
display block
|
||||
line-height $headerHeight
|
||||
text-align center
|
||||
|
||||
> .nav
|
||||
display block
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
z-index 10001
|
||||
padding 0
|
||||
width $headerHeight
|
||||
font-size 1.4em
|
||||
line-height $headerHeight
|
||||
border-right solid 1px rgba(#000, 0.1)
|
||||
|
||||
> [data-icon]
|
||||
transition all 0.2s ease
|
||||
|
||||
> nav
|
||||
position fixed
|
||||
z-index 10000
|
||||
z-index 20001
|
||||
top 0
|
||||
left 0
|
||||
width 250px
|
||||
@ -136,7 +188,7 @@ export default Vue.extend({
|
||||
&:hover
|
||||
color #fff
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 6px
|
||||
|
||||
> .version
|
||||
@ -166,7 +218,7 @@ export default Vue.extend({
|
||||
&:hover
|
||||
color #fff
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 6px
|
||||
|
||||
&.active
|
||||
@ -187,9 +239,22 @@ export default Vue.extend({
|
||||
border-bottom solid 16px transparent
|
||||
border-left solid 16px transparent
|
||||
|
||||
> .nav-backdrop
|
||||
position fixed
|
||||
top 0
|
||||
left 0
|
||||
z-index 20000
|
||||
width 100%
|
||||
height 100%
|
||||
background var(--mobileNavBackdrop)
|
||||
|
||||
> main
|
||||
width 100%
|
||||
padding 32px 32px 32px calc(32px + 250px)
|
||||
padding 0 0 0 250px
|
||||
max-width 1300px
|
||||
|
||||
&.isMobile
|
||||
> main
|
||||
padding $headerHeight 0 0 0
|
||||
|
||||
</style>
|
||||
|
@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="axbwjelsbymowqjyywpirzhdlszoncqs">
|
||||
<ui-card>
|
||||
<div slot="title">%i18n:@banner-url%</div>
|
||||
<div slot="title">%fa:cog% %i18n:@instance%</div>
|
||||
<section class="fit-top">
|
||||
<ui-input v-model="bannerUrl"/>
|
||||
<ui-input v-model="name">%i18n:@instance-name%</ui-input>
|
||||
<ui-textarea v-model="description">%i18n:@instance-description%</ui-textarea>
|
||||
<ui-input v-model="bannerUrl">%i18n:@banner-url%</ui-input>
|
||||
<ui-input v-model="maxNoteTextLength">%i18n:@max-note-text-length%</ui-input>
|
||||
<ui-button @click="updateMeta">%i18n:@save%</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
@ -35,28 +38,61 @@ export default Vue.extend({
|
||||
disableRegistration: false,
|
||||
disableLocalTimeline: false,
|
||||
bannerUrl: null,
|
||||
name: null,
|
||||
description: null,
|
||||
maxNoteTextLength: null,
|
||||
inviteCode: null,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
(this as any).os.getMeta().then(meta => {
|
||||
this.bannerUrl = meta.bannerUrl;
|
||||
this.name = meta.name;
|
||||
this.description = meta.description;
|
||||
this.maxNoteTextLength = meta.maxNoteTextLength;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
invite() {
|
||||
(this as any).api('admin/invite').then(x => {
|
||||
this.inviteCode = x.code;
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
this.$swal({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updateMeta() {
|
||||
(this as any).api('admin/update-meta', {
|
||||
disableRegistration: this.disableRegistration,
|
||||
disableLocalTimeline: this.disableLocalTimeline,
|
||||
bannerUrl: this.bannerUrl
|
||||
bannerUrl: this.bannerUrl,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
maxNoteTextLength: parseInt(this.maxNoteTextLength, 10)
|
||||
}).then(() => {
|
||||
(this as any).os.apis.dialog({ text: `Saved` });
|
||||
this.$swal({
|
||||
type: 'success',
|
||||
text: '%i18n:@saved%'
|
||||
});
|
||||
}).catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed ${e}` });
|
||||
this.$swal({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.axbwjelsbymowqjyywpirzhdlszoncqs
|
||||
@media (min-width 500px)
|
||||
padding 16px
|
||||
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="ucnffhbtogqgscfmqcymwmmupoknpfsw">
|
||||
<ui-card>
|
||||
<div slot="title">%i18n:@verify-user%</div>
|
||||
<section class="fit-top">
|
||||
@ -67,11 +67,11 @@ export default Vue.extend({
|
||||
const process = async () => {
|
||||
const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername));
|
||||
await (this as any).os.api('admin/verify-user', { userId: user.id });
|
||||
(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
|
||||
//(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
});
|
||||
|
||||
this.verifying = false;
|
||||
@ -83,11 +83,11 @@ export default Vue.extend({
|
||||
const process = async () => {
|
||||
const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername));
|
||||
await (this as any).os.api('admin/unverify-user', { userId: user.id });
|
||||
(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
|
||||
//(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
});
|
||||
|
||||
this.unverifying = false;
|
||||
@ -99,11 +99,11 @@ export default Vue.extend({
|
||||
const process = async () => {
|
||||
const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername));
|
||||
await (this as any).os.api('admin/suspend-user', { userId: user.id });
|
||||
(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
|
||||
//(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
});
|
||||
|
||||
this.suspending = false;
|
||||
@ -115,11 +115,11 @@ export default Vue.extend({
|
||||
const process = async () => {
|
||||
const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername));
|
||||
await (this as any).os.api('admin/unsuspend-user', { userId: user.id });
|
||||
(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
|
||||
//(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
|
||||
});
|
||||
|
||||
this.unsuspending = false;
|
||||
@ -127,3 +127,10 @@ export default Vue.extend({
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.ucnffhbtogqgscfmqcymwmmupoknpfsw
|
||||
@media (min-width 500px)
|
||||
padding 16px
|
||||
|
||||
</style>
|
||||
|
@ -128,7 +128,7 @@ pre
|
||||
overflow auto
|
||||
tab-size 2
|
||||
|
||||
[data-fa]
|
||||
[data-icon]
|
||||
display inline-block
|
||||
|
||||
.swal2-container
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:key% API</div>
|
||||
<div slot="title"><fa icon="key"/> API</div>
|
||||
|
||||
<section class="fit-top">
|
||||
<ui-input :value="$store.state.i.token" readonly>
|
||||
@ -9,11 +9,11 @@
|
||||
<p>%i18n:@intro%</p>
|
||||
<ui-info warn>%i18n:@caution%</ui-info>
|
||||
<p>%i18n:@regeneration-of-token%</p>
|
||||
<ui-button @click="regenerateToken">%fa:sync-alt% %i18n:@regenerate-token%</ui-button>
|
||||
<ui-button @click="regenerateToken"><fa icon="sync-alt"/> %i18n:@regenerate-token%</ui-button>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>%fa:terminal% %i18n:@console.title%</header>
|
||||
<header><fa icon="terminal"/> %i18n:@console.title%</header>
|
||||
<ui-input v-model="endpoint">
|
||||
<span>%i18n:@console.endpoint%</span>
|
||||
</ui-input>
|
||||
@ -22,7 +22,7 @@
|
||||
</ui-textarea>
|
||||
<ui-button @click="send" :disabled="sending">
|
||||
<template v-if="sending">%i18n:@console.sending%</template>
|
||||
<template v-else>%fa:paper-plane% %i18n:@console.send%</template>
|
||||
<template v-else><fa icon="paper-plane"/> %i18n:@console.send%</template>
|
||||
</ui-button>
|
||||
<ui-textarea v-if="res" v-model="res" readonly tall>
|
||||
<span>%i18n:@console.response%</span>
|
||||
|
@ -14,7 +14,8 @@
|
||||
</ol>
|
||||
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
|
||||
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
|
||||
<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else-if="!useOsDefaultEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else>{{ emoji.emoji }}</span>
|
||||
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
|
||||
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
|
||||
@ -33,6 +34,7 @@ type EmojiDef = {
|
||||
name: string;
|
||||
aliasOf?: string;
|
||||
url?: string;
|
||||
isCustomEmoji?: boolean;
|
||||
};
|
||||
|
||||
const lib = Object.entries(emojilib.lib).filter((x: any) => {
|
||||
@ -42,7 +44,8 @@ const lib = Object.entries(emojilib.lib).filter((x: any) => {
|
||||
const emjdb: EmojiDef[] = lib.map((x: any) => ({
|
||||
emoji: x[1].char,
|
||||
name: x[0],
|
||||
aliasOf: null
|
||||
aliasOf: null,
|
||||
url: `https://twemoji.maxcdn.com/2/svg/${x[1].char.codePointAt(0).toString(16)}.svg`
|
||||
}));
|
||||
|
||||
lib.forEach((x: any) => {
|
||||
@ -51,7 +54,8 @@ lib.forEach((x: any) => {
|
||||
emjdb.push({
|
||||
emoji: x[1].char,
|
||||
name: k,
|
||||
aliasOf: x[0]
|
||||
aliasOf: x[0],
|
||||
url: `https://twemoji.maxcdn.com/2/svg/${x[1].char.codePointAt(0).toString(16)}.svg`
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -77,6 +81,10 @@ export default Vue.extend({
|
||||
computed: {
|
||||
items(): HTMLCollection {
|
||||
return (this.$refs.suggests as Element).children;
|
||||
},
|
||||
|
||||
useOsDefaultEmojis(): boolean {
|
||||
return this.$store.state.device.useOsDefaultEmojis;
|
||||
}
|
||||
},
|
||||
|
||||
@ -107,7 +115,8 @@ export default Vue.extend({
|
||||
emojiDefinitions.push({
|
||||
name: x.name,
|
||||
emoji: `:${x.name}:`,
|
||||
url: x.url
|
||||
url: x.url,
|
||||
isCustomEmoji: true
|
||||
});
|
||||
|
||||
if (x.aliases) {
|
||||
@ -116,7 +125,8 @@ export default Vue.extend({
|
||||
name: alias,
|
||||
aliasOf: x.name,
|
||||
emoji: `:${x.name}:`,
|
||||
url: x.url
|
||||
url: x.url,
|
||||
isCustomEmoji: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,35 +1,35 @@
|
||||
<template>
|
||||
<div class="troubleshooter">
|
||||
<div class="body">
|
||||
<h1>%fa:wrench%%i18n:@title%</h1>
|
||||
<h1><fa icon="wrench"/>%i18n:@title%</h1>
|
||||
<div>
|
||||
<p :data-wip="network == null">
|
||||
<template v-if="network != null">
|
||||
<template v-if="network">%fa:check%</template>
|
||||
<template v-if="!network">%fa:times%</template>
|
||||
<template v-if="network"><fa icon="check"/></template>
|
||||
<template v-if="!network"><fa icon="times"/></template>
|
||||
</template>
|
||||
{{ network == null ? '%i18n:@checking-network%' : '%i18n:@network%' }}<mk-ellipsis v-if="network == null"/>
|
||||
</p>
|
||||
<p v-if="network == true" :data-wip="internet == null">
|
||||
<template v-if="internet != null">
|
||||
<template v-if="internet">%fa:check%</template>
|
||||
<template v-if="!internet">%fa:times%</template>
|
||||
<template v-if="internet"><fa icon="check"/></template>
|
||||
<template v-if="!internet"><fa icon="times"/></template>
|
||||
</template>
|
||||
{{ internet == null ? '%i18n:@checking-internet%' : '%i18n:@internet%' }}<mk-ellipsis v-if="internet == null"/>
|
||||
</p>
|
||||
<p v-if="internet == true" :data-wip="server == null">
|
||||
<template v-if="server != null">
|
||||
<template v-if="server">%fa:check%</template>
|
||||
<template v-if="!server">%fa:times%</template>
|
||||
<template v-if="server"><fa icon="check"/></template>
|
||||
<template v-if="!server"><fa icon="times"/></template>
|
||||
</template>
|
||||
{{ server == null ? '%i18n:@checking-server%' : '%i18n:@server%' }}<mk-ellipsis v-if="server == null"/>
|
||||
</p>
|
||||
</div>
|
||||
<p v-if="!end">%i18n:@finding%<mk-ellipsis/></p>
|
||||
<p v-if="network === false"><b>%fa:exclamation-triangle%%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p>
|
||||
<p v-if="internet === false"><b>%fa:exclamation-triangle%%i18n:@no-internet%</b><br>%i18n:@no-internet-desc%</p>
|
||||
<p v-if="server === false"><b>%fa:exclamation-triangle%%i18n:@no-server%</b><br>%i18n:@no-server-desc%</p>
|
||||
<p v-if="server === true" class="success"><b>%fa:info-circle%%i18n:@success%</b><br>%i18n:@success-desc%</p>
|
||||
<p v-if="network === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p>
|
||||
<p v-if="internet === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-internet%</b><br>%i18n:@no-internet-desc%</p>
|
||||
<p v-if="server === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-server%</b><br>%i18n:@no-server-desc%</p>
|
||||
<p v-if="server === true" class="success"><b><fa icon="info-circle"/>%i18n:@success%</b><br>%i18n:@success-desc%</p>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="/assets/flush.html">%i18n:@flush%</a> | <a href="/assets/version.html">%i18n:@set-version%</a>
|
||||
@ -100,7 +100,7 @@ export default Vue.extend({
|
||||
color #444
|
||||
border-bottom solid 1px #eee
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 0.25em
|
||||
|
||||
> div
|
||||
@ -115,7 +115,7 @@ export default Vue.extend({
|
||||
&[data-wip]
|
||||
color #888
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 0.25em
|
||||
|
||||
&.times
|
||||
@ -132,7 +132,7 @@ export default Vue.extend({
|
||||
border-top solid 1px #eee
|
||||
|
||||
> b
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 0.25em
|
||||
|
||||
&.success
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mk-connect-failed">
|
||||
<img src="data:image/jpeg;base64,%base64:/assets/error.jpg%" alt=""/>
|
||||
<img src="https://raw.githubusercontent.com/syuilo/misskey/develop/src/client/assets/error.jpg" alt=""/>
|
||||
<h1>%i18n:@title%</h1>
|
||||
<p class="text">
|
||||
<span>{{ '%i18n:@description%'.substr(0, '%i18n:@description%'.indexOf('{')) }}</span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:cloud% %i18n:common.drive%</div>
|
||||
<div slot="title"><fa icon="cloud"/> %i18n:common.drive%</div>
|
||||
|
||||
<section v-if="!fetching" class="juakhbxthdewydyreaphkepoxgxvfogn">
|
||||
<div class="meter"><div :style="meterStyle"></div></div>
|
||||
|
82
src/client/app/common/views/components/emoji.vue
Normal file
82
src/client/app/common/views/components/emoji.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<img v-if="customEmoji" class="fvgwvorwhxigeolkkrcderjzcawqrscl custom" :src="url" :alt="alt" :title="alt"/>
|
||||
<img v-else-if="char && !useOsDefaultEmojis" class="fvgwvorwhxigeolkkrcderjzcawqrscl" :src="url" :alt="alt" :title="alt"/>
|
||||
<span v-else-if="char && useOsDefaultEmojis">{{ char }}</span>
|
||||
<span v-else>:{{ name }}:</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { lib } from 'emojilib';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
emoji: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
customEmojis: {
|
||||
required: false,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
url: null,
|
||||
char: null,
|
||||
customEmoji: null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
alt(): string {
|
||||
return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
|
||||
},
|
||||
|
||||
useOsDefaultEmojis(): boolean {
|
||||
return this.$store.state.device.useOsDefaultEmojis;
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.name) {
|
||||
const customEmoji = this.customEmojis.find(x => x.name == this.name);
|
||||
if (customEmoji) {
|
||||
this.customEmoji = customEmoji;
|
||||
this.url = customEmoji.url;
|
||||
} else {
|
||||
const emoji = lib[this.name];
|
||||
if (emoji) {
|
||||
this.char = emoji.char;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.char = this.emoji;
|
||||
}
|
||||
|
||||
if (this.char) {
|
||||
this.url = `https://twemoji.maxcdn.com/2/svg/${this.char.codePointAt(0).toString(16)}.svg`;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.fvgwvorwhxigeolkkrcderjzcawqrscl
|
||||
height 1.25em
|
||||
vertical-align -0.25em
|
||||
|
||||
&.custom
|
||||
height 2.5em
|
||||
vertical-align middle
|
||||
transition transform 0.2s ease
|
||||
|
||||
&:hover
|
||||
transform scale(1.2)
|
||||
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj">
|
||||
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
|
||||
<p><fa icon="exclamation-triangle"/> %i18n:common.error.title%</p>
|
||||
<ui-button @click="() => $emit('retry')">%i18n:common.error.retry%</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<span class="mk-file-type-icon">
|
||||
<template v-if="kind == 'image'">%fa:file-image%</template>
|
||||
<template v-if="kind == 'image'"><fa icon="file-image"/></template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
|
||||
<button class="go-index" v-if="selfNav" @click="goIndex">%fa:arrow-left%</button>
|
||||
<button class="go-index" v-if="selfNav" @click="goIndex"><fa icon="arrow-left"/></button>
|
||||
<header><b><router-link :to="blackUser | userPage">{{ blackUser | userName }}</router-link></b>(%i18n:common.reversi.black%) vs <b><router-link :to="whiteUser | userPage">{{ whiteUser | userName }}</router-link></b>(%i18n:common.reversi.white%)</header>
|
||||
|
||||
<div style="overflow: hidden; line-height: 28px;">
|
||||
@ -51,13 +51,13 @@
|
||||
|
||||
<div class="player" v-if="game.isEnded">
|
||||
<div>
|
||||
<button @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</button>
|
||||
<button @click="logPos--" :disabled="logPos == 0">%fa:angle-left%</button>
|
||||
<button @click="logPos = 0" :disabled="logPos == 0"><fa icon="angle-double-left"/></button>
|
||||
<button @click="logPos--" :disabled="logPos == 0"><fa icon="angle-left"/></button>
|
||||
</div>
|
||||
<span>{{ logPos }} / {{ logs.length }}</span>
|
||||
<div>
|
||||
<button @click="logPos++" :disabled="logPos == logs.length">%fa:angle-right%</button>
|
||||
<button @click="logPos = logs.length" :disabled="logPos == logs.length">%fa:angle-double-right%</button>
|
||||
<button @click="logPos++" :disabled="logPos == logs.length"><fa icon="angle-right"/></button>
|
||||
<button @click="logPos = logs.length" :disabled="logPos == logs.length"><fa icon="angle-double-right"/></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -17,13 +17,13 @@
|
||||
</header>
|
||||
|
||||
<div>
|
||||
<div class="random" v-if="game.settings.map == null">%fa:dice%</div>
|
||||
<div class="random" v-if="game.settings.map == null"><fa icon="dice"/></div>
|
||||
<div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
|
||||
<div v-for="(x, i) in game.settings.map.join('')"
|
||||
:data-none="x == ' '"
|
||||
@click="onPixelClick(i, x)">
|
||||
<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode">%fa:circle R%</template><template v-else>%fa:circle%</template></template>
|
||||
<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode">%fa:circle%</template><template v-else>%fa:circle R%</template></template>
|
||||
<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode"><fa :icon="['far', 'circle']"/></template><template v-else><fa icon="circle"/></template></template>
|
||||
<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode"><fa :icon="['far', 'circle']"/></template><template v-else><fa icon="circle"/></template></template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
63
src/client/app/common/views/components/github-setting.vue
Normal file
63
src/client/app/common/views/components/github-setting.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="mk-github-setting">
|
||||
<p>%i18n:@description%<a :href="`${docsUrl}/link-to-github`" target="_blank">%i18n:@detail%</a></p>
|
||||
<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">%i18n:@connected-to%: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
|
||||
<p>
|
||||
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a>
|
||||
<span v-if="$store.state.i.github"> or </span>
|
||||
<a :href="`${apiUrl}/disconnect/github`" target="_blank" v-if="$store.state.i.github" @click.prevent="disconnect">%i18n:@disconnect%</a>
|
||||
</p>
|
||||
<p class="id" v-if="$store.state.i.github">GitHub ID: {{ $store.state.i.github.id }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { apiUrl, docsUrl } from '../../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
form: null,
|
||||
apiUrl,
|
||||
docsUrl
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$watch('$store.state.i', () => {
|
||||
if (this.$store.state.i.github && this.form)
|
||||
this.form.close();
|
||||
}, {
|
||||
deep: true
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
connect() {
|
||||
this.form = window.open(apiUrl + '/connect/github',
|
||||
'github_connect_window',
|
||||
'height=570, width=520');
|
||||
},
|
||||
|
||||
disconnect() {
|
||||
window.open(apiUrl + '/disconnect/github',
|
||||
'github_disconnect_window',
|
||||
'height=570, width=520');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mk-github-setting
|
||||
.account
|
||||
border solid 1px #e1e8ed
|
||||
border-radius 4px
|
||||
padding 16px
|
||||
|
||||
a
|
||||
font-weight bold
|
||||
color inherit
|
||||
|
||||
.id
|
||||
color #8899a6
|
||||
</style>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mk-google">
|
||||
<input type="search" v-model="query" :placeholder="q">
|
||||
<button @click="search">%fa:search% %i18n:common.search%</button>
|
||||
<button @click="search"><fa icon="search"/> %i18n:common.search%</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -37,12 +37,14 @@ import messaging from './messaging.vue';
|
||||
import messagingRoom from './messaging-room.vue';
|
||||
import urlPreview from './url-preview.vue';
|
||||
import twitterSetting from './twitter-setting.vue';
|
||||
import githubSetting from './github-setting.vue';
|
||||
import fileTypeIcon from './file-type-icon.vue';
|
||||
import emoji from './emoji.vue';
|
||||
import Reversi from './games/reversi/reversi.vue';
|
||||
import welcomeTimeline from './welcome-timeline.vue';
|
||||
import uiInput from './ui/input.vue';
|
||||
import uiButton from './ui/button.vue';
|
||||
import uiButtonGroup from './ui/button-group.vue';
|
||||
import uiHorizonGroup from './ui/horizon-group.vue';
|
||||
import uiCard from './ui/card.vue';
|
||||
import uiForm from './ui/form.vue';
|
||||
import uiTextarea from './ui/textarea.vue';
|
||||
@ -90,12 +92,14 @@ Vue.component('mk-messaging', messaging);
|
||||
Vue.component('mk-messaging-room', messagingRoom);
|
||||
Vue.component('mk-url-preview', urlPreview);
|
||||
Vue.component('mk-twitter-setting', twitterSetting);
|
||||
Vue.component('mk-github-setting', githubSetting);
|
||||
Vue.component('mk-file-type-icon', fileTypeIcon);
|
||||
Vue.component('mk-emoji', emoji);
|
||||
Vue.component('mk-reversi', Reversi);
|
||||
Vue.component('mk-welcome-timeline', welcomeTimeline);
|
||||
Vue.component('ui-input', uiInput);
|
||||
Vue.component('ui-button', uiButton);
|
||||
Vue.component('ui-button-group', uiButtonGroup);
|
||||
Vue.component('ui-horizon-group', uiHorizonGroup);
|
||||
Vue.component('ui-card', uiCard);
|
||||
Vue.component('ui-form', uiForm);
|
||||
Vue.component('ui-textarea', uiTextarea);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mk-media-banner">
|
||||
<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
|
||||
<span class="icon">%fa:exclamation-triangle%</span>
|
||||
<span class="icon"><fa icon="exclamation-triangle"/></span>
|
||||
<b>%i18n:@sensitive%</b>
|
||||
<span>%i18n:@click-to-show%</span>
|
||||
</div>
|
||||
@ -18,7 +18,7 @@
|
||||
:title="media.name"
|
||||
:download="media.name"
|
||||
>
|
||||
<span class="icon">%fa:download%</span>
|
||||
<span class="icon"><fa icon="download"/></span>
|
||||
<b>{{ media.name }}</b>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -4,7 +4,9 @@
|
||||
<div class="popover" :class="{ hukidasi }" ref="popover">
|
||||
<template v-for="item, i in items">
|
||||
<div v-if="item === null"></div>
|
||||
<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text" :tabindex="i"></button>
|
||||
<button v-if="item" @click="clicked(item.action)" :tabindex="i">
|
||||
<fa v-if="item.icon" :icon="item.icon"/>{{ item.text }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@ -188,6 +190,9 @@ export default Vue.extend({
|
||||
color var(--primaryForeground)
|
||||
background var(--primaryDarken10)
|
||||
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> div
|
||||
margin 8px 0
|
||||
height 1px
|
||||
|
@ -14,13 +14,13 @@
|
||||
<div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
|
||||
<mk-uploader ref="uploader" @uploaded="onUploaded"/>
|
||||
<button class="send" @click="send" :disabled="!canSend || sending" title="%i18n:@send%">
|
||||
<template v-if="!sending">%fa:paper-plane%</template><template v-if="sending">%fa:spinner .spin%</template>
|
||||
<template v-if="!sending"><fa icon="paper-plane"/></template><template v-if="sending"><fa icon="spinner .spin"/></template>
|
||||
</button>
|
||||
<button class="attach-from-local" @click="chooseFile" title="%i18n:@attach-from-local%">
|
||||
%fa:upload%
|
||||
<fa icon="upload"/>
|
||||
</button>
|
||||
<button class="attach-from-drive" @click="chooseFileFromDrive" title="%i18n:@attach-from-drive%">
|
||||
%fa:R folder-open%
|
||||
<fa :icon="['far', 'folder-open']"/>
|
||||
</button>
|
||||
<input ref="file" type="file" @change="onChangeFile"/>
|
||||
</div>
|
||||
|
@ -24,7 +24,7 @@
|
||||
<footer>
|
||||
<span class="read" v-if="isMe && message.isRead">%i18n:@is-read%</span>
|
||||
<mk-time :time="message.createdAt"/>
|
||||
<template v-if="message.is_edited">%fa:pencil-alt%</template>
|
||||
<template v-if="message.is_edited"><fa icon="pencil-alt"/></template>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
@ -179,7 +179,7 @@ export default Vue.extend({
|
||||
font-size 10px
|
||||
color var(--messagingRoomMessageInfo)
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-left 4px
|
||||
|
||||
&:not([data-is-me])
|
||||
|
@ -4,11 +4,11 @@
|
||||
@drop.prevent.stop="onDrop"
|
||||
>
|
||||
<div class="body">
|
||||
<p class="init" v-if="init">%fa:spinner .spin%%i18n:common.loading%</p>
|
||||
<p class="empty" v-if="!init && messages.length == 0">%fa:info-circle%%i18n:@empty%</p>
|
||||
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages">%fa:flag%%i18n:@no-history%</p>
|
||||
<p class="init" v-if="init"><fa icon="spinner .spin"/>%i18n:common.loading%</p>
|
||||
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>%i18n:@empty%</p>
|
||||
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>%i18n:@no-history%</p>
|
||||
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
|
||||
<template v-if="fetchingMoreMessages">%fa:spinner .pulse .fw%</template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
|
||||
<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
|
||||
</button>
|
||||
<template v-for="(message, i) in _messages">
|
||||
<x-message :message="message" :key="message.id"/>
|
||||
@ -20,7 +20,7 @@
|
||||
<footer>
|
||||
<transition name="fade">
|
||||
<div class="new-message" v-show="showIndicator">
|
||||
<button @click="onIndicatorClick">%fa:arrow-circle-down%%i18n:@new-message%</button>
|
||||
<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>%i18n:@new-message%</button>
|
||||
</div>
|
||||
</transition>
|
||||
<x-form :user="user" ref="form"/>
|
||||
@ -280,7 +280,7 @@ export default Vue.extend({
|
||||
color var(--messagingRoomInfo)
|
||||
opacity 0.5
|
||||
|
||||
[data-fa]
|
||||
[data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> .no-history
|
||||
@ -292,7 +292,7 @@ export default Vue.extend({
|
||||
color var(--messagingRoomInfo)
|
||||
opacity 0.5
|
||||
|
||||
[data-fa]
|
||||
[data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> .more
|
||||
@ -313,7 +313,7 @@ export default Vue.extend({
|
||||
&.fetching
|
||||
cursor wait
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> .message
|
||||
@ -381,7 +381,7 @@ export default Vue.extend({
|
||||
&:active
|
||||
background var(--primaryDarken10)
|
||||
|
||||
> [data-fa]
|
||||
> i
|
||||
position absolute
|
||||
top 0
|
||||
left 10px
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="mk-messaging" :data-compact="compact">
|
||||
<div class="search" v-if="!compact" :style="{ top: headerTop + 'px' }">
|
||||
<div class="form">
|
||||
<label for="search-input">%fa:search%</label>
|
||||
<label for="search-input"><i><fa icon="search"/></i></label>
|
||||
<input v-model="q" type="search" @input="search" @keydown="onSearchKeydown" placeholder="%i18n:@search-user%"/>
|
||||
</div>
|
||||
<div class="result">
|
||||
@ -45,7 +45,7 @@
|
||||
</template>
|
||||
</div>
|
||||
<p class="no-history" v-if="!fetching && messages.length == 0">%i18n:@no-history%</p>
|
||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -213,7 +213,7 @@ export default Vue.extend({
|
||||
width 38px
|
||||
pointer-events none
|
||||
|
||||
> [data-fa]
|
||||
> i
|
||||
display block
|
||||
position absolute
|
||||
top 0
|
||||
@ -418,7 +418,7 @@ export default Vue.extend({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
// TODO: element base media query
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Vue, { VNode } from 'vue';
|
||||
import * as emojilib from 'emojilib';
|
||||
import { length } from 'stringz';
|
||||
import parse from '../../../../../mfm/parse';
|
||||
import getAcct from '../../../../../misc/acct/render';
|
||||
@ -188,24 +187,15 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
}
|
||||
|
||||
case 'emoji': {
|
||||
//#region カスタム絵文字
|
||||
if (this.customEmojis != null) {
|
||||
const customEmoji = this.customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
|
||||
if (customEmoji) {
|
||||
return [createElement('img', {
|
||||
attrs: {
|
||||
src: customEmoji.url,
|
||||
alt: token.emoji,
|
||||
title: token.emoji,
|
||||
style: 'height: 2.5em; vertical-align: middle;'
|
||||
}
|
||||
})];
|
||||
return [createElement('mk-emoji', {
|
||||
attrs: {
|
||||
emoji: token.emoji,
|
||||
name: token.name
|
||||
},
|
||||
props: {
|
||||
customEmojis: this.customEmojis
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const emoji = emojilib.lib[token.emoji];
|
||||
return [createElement('span', emoji ? emoji.char : token.content)];
|
||||
})];
|
||||
}
|
||||
|
||||
case 'search': {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:ban% %i18n:@mute-and-block%</div>
|
||||
<div slot="title"><fa icon="ban"/> %i18n:@mute-and-block%</div>
|
||||
|
||||
<section>
|
||||
<header>%i18n:@mute%</header>
|
||||
|
@ -2,15 +2,11 @@
|
||||
<span class="mk-nav">
|
||||
<a :href="aboutUrl">%i18n:@about%</a>
|
||||
<i>・</i>
|
||||
<a href="/stats">%i18n:@stats%</a>
|
||||
<i>・</i>
|
||||
<a :href="repositoryUrl">%i18n:@repository%</a>
|
||||
<i>・</i>
|
||||
<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a>
|
||||
<i>・</i>
|
||||
<a href="/dev">%i18n:@develop%</a>
|
||||
<i>・</i>
|
||||
<a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
@ -6,18 +6,18 @@
|
||||
<span class="is-bot" v-if="note.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
|
||||
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%"><fa icon="star"/></span>
|
||||
<div class="info">
|
||||
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
|
||||
<router-link class="created-at" :to="note | notePage">
|
||||
<mk-time :time="note.createdAt"/>
|
||||
</router-link>
|
||||
<span class="visibility" v-if="note.visibility != 'public'">
|
||||
<template v-if="note.visibility == 'home'">%fa:home%</template>
|
||||
<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
|
||||
<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
|
||||
<template v-if="note.visibility == 'private'">%fa:lock%</template>
|
||||
<template v-if="note.visibility == 'home'"><fa icon="home"/></template>
|
||||
<template v-if="note.visibility == 'followers'"><fa icon="unlock"/></template>
|
||||
<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
|
||||
<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -15,18 +15,18 @@ export default Vue.extend({
|
||||
computed: {
|
||||
items() {
|
||||
const items = [{
|
||||
icon: '%fa:info-circle%',
|
||||
icon: 'info-circle',
|
||||
text: '%i18n:@detail%',
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: '%fa:link%',
|
||||
icon: 'link',
|
||||
text: '%i18n:@copy-link%',
|
||||
action: this.copyLink
|
||||
}];
|
||||
|
||||
if (this.note.uri) {
|
||||
items.push({
|
||||
icon: '%fa:external-link-square-alt%',
|
||||
icon: 'external-link-square-alt',
|
||||
text: '%i18n:@remote%',
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
@ -38,13 +38,13 @@ export default Vue.extend({
|
||||
|
||||
if (this.note.isFavorited) {
|
||||
items.push({
|
||||
icon: '%fa:star%',
|
||||
icon: 'star',
|
||||
text: '%i18n:@unfavorite%',
|
||||
action: this.unfavorite
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
icon: '%fa:star%',
|
||||
icon: 'star',
|
||||
text: '%i18n:@favorite%',
|
||||
action: this.favorite
|
||||
});
|
||||
@ -53,13 +53,13 @@ export default Vue.extend({
|
||||
if (this.note.userId == this.$store.state.i.id) {
|
||||
if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) {
|
||||
items.push({
|
||||
icon: '%fa:thumbtack%',
|
||||
icon: 'thumbtack',
|
||||
text: '%i18n:@unpin%',
|
||||
action: this.unpin
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
icon: '%fa:thumbtack%',
|
||||
icon: 'thumbtack',
|
||||
text: '%i18n:@pin%',
|
||||
action: this.pin
|
||||
});
|
||||
@ -69,7 +69,7 @@ export default Vue.extend({
|
||||
if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
|
||||
items.push(null);
|
||||
items.push({
|
||||
icon: '%fa:trash-alt R%',
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: '%i18n:@delete%',
|
||||
action: this.del
|
||||
});
|
||||
|
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div class="mk-poll-editor">
|
||||
<p class="caution" v-if="choices.length < 2">
|
||||
%fa:exclamation-triangle%%i18n:@no-only-one-choice%
|
||||
<fa icon="exclamation-triangle"/>%i18n:@no-only-one-choice%
|
||||
</p>
|
||||
<ul ref="choices">
|
||||
<li v-for="(choice, i) in choices">
|
||||
<input :value="choice" @input="onInput(i, $event)" :placeholder="'%i18n:@choice-n%'.replace('{}', i + 1)">
|
||||
<button @click="remove(i)" title="%i18n:@remove%">
|
||||
%fa:times%
|
||||
<fa icon="times"/>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="add" v-if="choices.length < 10" @click="add">%i18n:@add%</button>
|
||||
<button class="destroy" @click="destroy" title="%i18n:@destroy%">
|
||||
%fa:times%
|
||||
<fa icon="times"/>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -76,7 +76,7 @@ export default Vue.extend({
|
||||
font-size 0.8em
|
||||
color #f00
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> ul
|
||||
|
@ -4,7 +4,7 @@
|
||||
<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? '%i18n:@vote-to%'.replace('{}', choice.text) : ''">
|
||||
<div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div>
|
||||
<span>
|
||||
<template v-if="choice.isVoted">%fa:check%</template>
|
||||
<template v-if="choice.isVoted"><fa icon="check"/></template>
|
||||
<span>{{ choice.text }}</span>
|
||||
<span class="votes" v-if="showResult">({{ '%i18n:@vote-count%'.replace('{}', choice.votes) }})</span>
|
||||
</span>
|
||||
@ -100,7 +100,7 @@ export default Vue.extend({
|
||||
transition width 1s ease
|
||||
|
||||
> span
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> .votes
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:user% %i18n:@title%</div>
|
||||
<div slot="title"><fa icon="user"/> %i18n:@title%</div>
|
||||
|
||||
<section class="fit-top">
|
||||
<ui-form :disabled="saving">
|
||||
@ -16,12 +16,12 @@
|
||||
|
||||
<ui-input v-model="location">
|
||||
<span>%i18n:@location%</span>
|
||||
<span slot="prefix">%fa:map-marker-alt%</span>
|
||||
<span slot="prefix"><fa icon="map-marker-alt"/></span>
|
||||
</ui-input>
|
||||
|
||||
<ui-input v-model="birthday" type="date">
|
||||
<span>%i18n:@birthday%</span>
|
||||
<span slot="prefix">%fa:birthday-cake%</span>
|
||||
<span slot="prefix"><fa icon="birthday-cake"/></span>
|
||||
</ui-input>
|
||||
|
||||
<ui-textarea v-model="description" :max="500">
|
||||
@ -30,13 +30,13 @@
|
||||
|
||||
<ui-input type="file" @change="onAvatarChange">
|
||||
<span>%i18n:@avatar%</span>
|
||||
<span slot="icon">%fa:image%</span>
|
||||
<span slot="icon"><fa icon="image"/></span>
|
||||
<span slot="text" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span>
|
||||
</ui-input>
|
||||
|
||||
<ui-input type="file" @change="onBannerChange">
|
||||
<span>%i18n:@banner%</span>
|
||||
<span slot="icon">%fa:image%</span>
|
||||
<span slot="icon"><fa icon="image"/></span>
|
||||
<span slot="text" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span>
|
||||
</ui-input>
|
||||
|
||||
|
@ -8,11 +8,12 @@
|
||||
</ui-input>
|
||||
<ui-input v-model="password" type="password" required styl="fill">
|
||||
<span>%i18n:@password%</span>
|
||||
<span slot="prefix">%fa:lock%</span>
|
||||
<span slot="prefix"><fa icon="lock"/></span>
|
||||
</ui-input>
|
||||
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill"/>
|
||||
<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
|
||||
<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
|
||||
<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/github`">%i18n:@signin-with-github%</a></p>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
@ -3,36 +3,36 @@
|
||||
<template v-if="meta">
|
||||
<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill">
|
||||
<span>%i18n:@invitation-code%</span>
|
||||
<span slot="prefix">%fa:id-card-alt%</span>
|
||||
<span slot="prefix"><fa icon="id-card-alt"/></span>
|
||||
<p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p>
|
||||
</ui-input>
|
||||
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill">
|
||||
<span>%i18n:@username%</span>
|
||||
<span slot="prefix">@</span>
|
||||
<span slot="suffix">@{{ host }}</span>
|
||||
<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
|
||||
<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
|
||||
<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
|
||||
<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
|
||||
<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
|
||||
<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
|
||||
<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
|
||||
<p slot="text" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> %i18n:@checking%</p>
|
||||
<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@available%</p>
|
||||
<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@unavailable%</p>
|
||||
<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@error%</p>
|
||||
<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@invalid-format%</p>
|
||||
<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-short%</p>
|
||||
<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-long%</p>
|
||||
</ui-input>
|
||||
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill">
|
||||
<span>%i18n:@password%</span>
|
||||
<span slot="prefix">%fa:lock%</span>
|
||||
<span slot="prefix"><fa icon="lock"/></span>
|
||||
<div slot="text">
|
||||
<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@weak-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@normal-password%</p>
|
||||
<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@strong-password%</p>
|
||||
</div>
|
||||
</ui-input>
|
||||
<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill">
|
||||
<span>%i18n:@password% (%i18n:@retype%)</span>
|
||||
<span slot="prefix">%fa:lock%</span>
|
||||
<span slot="prefix"><fa icon="lock"/></span>
|
||||
<div slot="text">
|
||||
<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
|
||||
<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
|
||||
<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@password-matched%</p>
|
||||
<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@password-not-matched%</p>
|
||||
</div>
|
||||
</ui-input>
|
||||
<div v-if="meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div>
|
||||
|
@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<div class="mk-stream-indicator">
|
||||
<p v-if="stream.state == 'initializing'">
|
||||
%fa:spinner .pulse%
|
||||
<fa icon="spinner .pulse"/>
|
||||
<span>%i18n:@connecting%<mk-ellipsis/></span>
|
||||
</p>
|
||||
<p v-if="stream.state == 'reconnecting'">
|
||||
%fa:spinner .pulse%
|
||||
<fa icon="spinner .pulse"/>
|
||||
<span>%i18n:@reconnecting%<mk-ellipsis/></span>
|
||||
</p>
|
||||
<p v-if="stream.state == 'connected'">
|
||||
%fa:check%
|
||||
<fa icon="check"/>
|
||||
<span>%i18n:@connected%</span>
|
||||
</p>
|
||||
</div>
|
||||
@ -80,7 +80,7 @@ export default Vue.extend({
|
||||
display block
|
||||
margin 0
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 0.25em
|
||||
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
|
||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="empty" v-else-if="tags.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
|
||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p>
|
||||
<div v-else>
|
||||
<vue-word-cloud
|
||||
:words="tags.slice(0, 20).map(x => [x.name, x.count])"
|
||||
@ -74,7 +74,7 @@ export default Vue.extend({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> div
|
||||
|
@ -25,7 +25,7 @@
|
||||
</label>
|
||||
|
||||
<details class="creator">
|
||||
<summary>%fa:palette% %i18n:@create-a-theme%</summary>
|
||||
<summary><fa icon="palette"/> %i18n:@create-a-theme%</summary>
|
||||
<div>
|
||||
<span>%i18n:@base-theme%:</span>
|
||||
<ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio>
|
||||
@ -51,23 +51,23 @@
|
||||
<div style="padding-bottom:8px;">%i18n:@text-color%:</div>
|
||||
<color-picker v-model="myThemeText"/>
|
||||
</div>
|
||||
<ui-button @click="preview()">%fa:eye% %i18n:@preview-created-theme%</ui-button>
|
||||
<ui-button primary @click="gen()">%fa:save R% %i18n:@save-created-theme%</ui-button>
|
||||
<ui-button @click="preview()"><fa icon="eye"/> %i18n:@preview-created-theme%</ui-button>
|
||||
<ui-button primary @click="gen()"><fa :icon="['far', 'save']"/> %i18n:@save-created-theme%</ui-button>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>%fa:download% %i18n:@install-a-theme%</summary>
|
||||
<ui-button @click="import_()">%fa:file-import% %i18n:@import%</ui-button>
|
||||
<summary><fa icon="download"/> %i18n:@install-a-theme%</summary>
|
||||
<ui-button @click="import_()"><fa icon="file-import"/> %i18n:@import%</ui-button>
|
||||
<input ref="file" type="file" accept=".misskeytheme" style="display:none;" @change="onUpdateImportFile"/>
|
||||
<p>%i18n:@import-by-code%:</p>
|
||||
<ui-textarea v-model="installThemeCode">
|
||||
<span>%i18n:@theme-code%</span>
|
||||
</ui-textarea>
|
||||
<ui-button @click="() => install(this.installThemeCode)">%fa:check% %i18n:@install%</ui-button>
|
||||
<ui-button @click="() => install(this.installThemeCode)"><fa icon="check"/> %i18n:@install%</ui-button>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>%fa:folder-open% %i18n:@manage-themes%</summary>
|
||||
<summary><fa icon="folder-open"/> %i18n:@manage-themes%</summary>
|
||||
<ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%">
|
||||
<optgroup label="%i18n:@builtin-themes%">
|
||||
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
@ -89,8 +89,8 @@
|
||||
<ui-textarea readonly :value="selectedThemeCode">
|
||||
<span>%i18n:@theme-code%</span>
|
||||
</ui-textarea>
|
||||
<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button>
|
||||
<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)">%fa:trash-alt R% %i18n:@uninstall%</ui-button>
|
||||
<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export"><fa icon="box"/> %i18n:@export%</ui-button>
|
||||
<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><fa :icon="['far', 'trash-alt']"/> %i18n:@uninstall%</ui-button>
|
||||
</template>
|
||||
</details>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
|
||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
|
||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p>
|
||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||
<transition-group v-else tag="div" name="chart">
|
||||
<div v-for="stat in stats" :key="stat.tag">
|
||||
@ -58,7 +58,7 @@ export default Vue.extend({
|
||||
color var(--text)
|
||||
opacity 0.7
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> div
|
||||
|
@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
|
||||
display flex
|
||||
|
||||
> *
|
||||
flex 1
|
||||
|
||||
&:not(:last-child)
|
||||
margin-right 16px
|
||||
</style>
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<component class="dmtdnykelhudezerjlfpbhgovrgnqqgr" :is="link ? 'a' : 'button'" :class="[styl, { inline, primary }]" :type="type" @click="$emit('click')">
|
||||
<component class="dmtdnykelhudezerjlfpbhgovrgnqqgr"
|
||||
:is="link ? 'a' : 'button'"
|
||||
:class="[styl, { inline, primary }]"
|
||||
:type="type"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
@ -7,6 +12,11 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
inject: {
|
||||
horizonGrouped: {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
@ -20,7 +30,9 @@ export default Vue.extend({
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
default(): boolean {
|
||||
return this.horizonGrouped;
|
||||
}
|
||||
},
|
||||
link: {
|
||||
type: Boolean,
|
||||
|
35
src/client/app/common/views/components/ui/horizon-group.vue
Normal file
35
src/client/app/common/views/components/ui/horizon-group.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze" :class="{ inputs }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
provide: {
|
||||
horizonGrouped: true
|
||||
},
|
||||
props: {
|
||||
inputs: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
|
||||
display flex
|
||||
|
||||
&.inputs
|
||||
margin 32px 0
|
||||
|
||||
> *
|
||||
flex 1
|
||||
|
||||
&:not(:last-child)
|
||||
margin-right 16px
|
||||
</style>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
|
||||
<i v-if="warn">%fa:exclamation-triangle%</i>
|
||||
<i v-else>%fa:info-circle%</i>
|
||||
<i v-if="warn"><fa icon="exclamation-triangle"/></i>
|
||||
<i v-else><fa icon="info-circle"/></i>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="ui-input" :class="[{ focused, filled }, styl]">
|
||||
<div class="ui-input" :class="[{ focused, filled, inline }, styl]">
|
||||
<div class="icon" ref="icon"><slot name="icon"></slot></div>
|
||||
<div class="input">
|
||||
<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
|
||||
@ -41,6 +41,11 @@ import Vue from 'vue';
|
||||
const getPasswordStrength = require('syuilo-password-strength');
|
||||
|
||||
export default Vue.extend({
|
||||
inject: {
|
||||
horizonGrouped: {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
required: false
|
||||
@ -72,6 +77,13 @@ export default Vue.extend({
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default(): boolean {
|
||||
return this.horizonGrouped;
|
||||
}
|
||||
},
|
||||
styl: {
|
||||
type: String,
|
||||
required: false,
|
||||
@ -337,4 +349,8 @@ root(fill)
|
||||
&:not(.fill)
|
||||
root(false)
|
||||
|
||||
&.inline
|
||||
display inline-block
|
||||
margin 0
|
||||
|
||||
</style>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ol v-if="uploads.length > 0">
|
||||
<li v-for="ctx in uploads" :key="ctx.id">
|
||||
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
|
||||
<p class="name">%fa:spinner .pulse%{{ ctx.name }}</p>
|
||||
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
|
||||
<p class="status">
|
||||
<span class="initing" v-if="ctx.progress == undefined">%i18n:@waiting%<mk-ellipsis/></span>
|
||||
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
|
||||
@ -155,7 +155,7 @@ export default Vue.extend({
|
||||
text-overflow ellipsis
|
||||
overflow hidden
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> .status
|
||||
|
@ -6,7 +6,7 @@
|
||||
<span class="pathname" v-if="pathname != ''">{{ pathname }}</span>
|
||||
<span class="query">{{ query }}</span>
|
||||
<span class="hash">{{ hash }}</span>
|
||||
%fa:external-link-square-alt%
|
||||
<fa icon="external-link-square-alt"/>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
@ -40,7 +40,7 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
.mk-url
|
||||
word-break break-all
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
padding-left 2px
|
||||
font-size .9em
|
||||
font-weight 400
|
||||
|
@ -3,34 +3,34 @@
|
||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||
<div class="popover" :class="{ compact }" ref="popover">
|
||||
<div @click="choose('public')" :class="{ active: v == 'public' }">
|
||||
<div>%fa:globe%</div>
|
||||
<div><fa icon="globe"/></div>
|
||||
<div>
|
||||
<span>%i18n:@public%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('home')" :class="{ active: v == 'home' }">
|
||||
<div>%fa:home%</div>
|
||||
<div><fa icon="home"/></div>
|
||||
<div>
|
||||
<span>%i18n:@home%</span>
|
||||
<span>%i18n:@home-desc%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('followers')" :class="{ active: v == 'followers' }">
|
||||
<div>%fa:unlock%</div>
|
||||
<div><fa icon="unlock"/></div>
|
||||
<div>
|
||||
<span>%i18n:@followers%</span>
|
||||
<span>%i18n:@followers-desc%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('specified')" :class="{ active: v == 'specified' }">
|
||||
<div>%fa:envelope%</div>
|
||||
<div><fa icon="envelope"/></div>
|
||||
<div>
|
||||
<span>%i18n:@specified%</span>
|
||||
<span>%i18n:@specified-desc%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="choose('private')" :class="{ active: v == 'private' }">
|
||||
<div>%fa:lock%</div>
|
||||
<div><fa icon="lock"/></div>
|
||||
<div>
|
||||
<span>%i18n:@private%</span>
|
||||
</div>
|
||||
|
@ -145,6 +145,7 @@ class Autocomplete {
|
||||
} else {
|
||||
// サジェスト要素作成
|
||||
this.suggestion = new MkAutocomplete({
|
||||
parent: this.vm,
|
||||
propsData: {
|
||||
textarea: this.textarea,
|
||||
complete: this.complete,
|
||||
@ -222,8 +223,6 @@ class Autocomplete {
|
||||
const trimmedBefore = before.substring(0, before.lastIndexOf(':'));
|
||||
const after = source.substr(caret);
|
||||
|
||||
if (value.startsWith(':')) value = value + ' ';
|
||||
|
||||
// 挿入
|
||||
this.text = trimmedBefore + value + after;
|
||||
|
||||
|
@ -19,13 +19,13 @@
|
||||
@click="onClick"
|
||||
:disabled="followWait">
|
||||
<template v-if="!followWait">
|
||||
<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked">%fa:hourglass-half% %i18n:@request-pending%</template>
|
||||
<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked">%fa:hourglass-start% %i18n:@follow-processing%</template>
|
||||
<template v-else-if="user.isFollowing">%fa:minus% %i18n:@following%</template>
|
||||
<template v-else-if="!user.isFollowing && user.isLocked">%fa:plus% %i18n:@follow-request%</template>
|
||||
<template v-else-if="!user.isFollowing && !user.isLocked">%fa:plus% %i18n:@follow%</template>
|
||||
<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked"><fa icon="hourglass-half"/> %i18n:@request-pending%</template>
|
||||
<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked"><fa icon="hourglass-start"/> %i18n:@follow-processing%</template>
|
||||
<template v-else-if="user.isFollowing"><fa icon="minus"/> %i18n:@following%</template>
|
||||
<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> %i18n:@follow-request%</template>
|
||||
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> %i18n:@follow%</template>
|
||||
</template>
|
||||
<template v-else>%fa:spinner .pulse .fw%</template>
|
||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<mk-widget-container :show-header="false">
|
||||
<article class="dolfvtibguprpxxhfndqaosjitixjohx">
|
||||
<h1>%fa:heart%%i18n:@title%</h1>
|
||||
<h1><fa icon="heart"/>%i18n:@title%</h1>
|
||||
<p v-if="meta">
|
||||
{{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }}
|
||||
<a :href="meta.maintainer.url">{{ meta.maintainer.name }}</a>
|
||||
@ -41,7 +41,7 @@ export default define({
|
||||
margin 0 0 5px 0
|
||||
font-size 1em
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 0.25em
|
||||
|
||||
> p
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mkw-hashtags">
|
||||
<mk-widget-container :show-header="!props.compact">
|
||||
<template slot="header">%fa:hashtag%%i18n:@title%</template>
|
||||
<template slot="header"><fa icon="hashtag"/>%i18n:@title%</template>
|
||||
|
||||
<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
|
||||
<mk-trends/>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mkw-memo">
|
||||
<mk-widget-container :show-header="!props.compact">
|
||||
<template slot="header">%fa:R sticky-note%%i18n:@title%</template>
|
||||
<template slot="header"><fa :icon="['far', 'sticky-note']"/>%i18n:@title%</template>
|
||||
|
||||
<div class="mkw-memo--body">
|
||||
<textarea v-model="text" placeholder="%i18n:@memo%" @input="onChange"></textarea>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="mkw-photo-stream" :class="$style.root" :data-melt="props.design == 2">
|
||||
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
|
||||
<template slot="header">%fa:camera%%i18n:@title%</template>
|
||||
<template slot="header"><fa icon="camera"/>%i18n:@title%</template>
|
||||
|
||||
<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<div :class="$style.stream" v-if="!fetching && images.length > 0">
|
||||
<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
|
||||
</div>
|
||||
@ -94,7 +94,7 @@ export default define({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
</style>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="mkw-posts-monitor">
|
||||
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
|
||||
<template slot="header">%fa:chart-line%%i18n:@title%</template>
|
||||
<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
|
||||
<template slot="header"><fa icon="chart-line"/>%i18n:@title%</template>
|
||||
<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button>
|
||||
|
||||
<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }">
|
||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="mkw-rss">
|
||||
<mk-widget-container :show-header="!props.compact">
|
||||
<template slot="header">%fa:rss-square%RSS</template>
|
||||
<button slot="func" title="設定" @click="setting">%fa:cog%</button>
|
||||
<template slot="header"><fa icon="rss-square"/>RSS</template>
|
||||
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
|
||||
|
||||
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
|
||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<div class="feed" v-else>
|
||||
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
|
||||
</div>
|
||||
@ -85,7 +85,7 @@ export default define({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
&[data-mobile]
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="cpu">
|
||||
<x-pie class="pie" :value="usage"/>
|
||||
<div>
|
||||
<p>%fa:microchip%CPU</p>
|
||||
<p><fa icon="microchip"/>CPU</p>
|
||||
<p>{{ meta.cpu.cores }} Cores</p>
|
||||
<p>{{ meta.cpu.model }}</p>
|
||||
</div>
|
||||
@ -57,7 +57,7 @@ export default Vue.extend({
|
||||
&:first-child
|
||||
font-weight bold
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
&:after
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="disk">
|
||||
<x-pie class="pie" :value="usage"/>
|
||||
<div>
|
||||
<p>%fa:R hdd%Storage</p>
|
||||
<p><fa :icon="['far', 'hdd']"/>Storage</p>
|
||||
<p>Total: {{ total | bytes(1) }}</p>
|
||||
<p>Free: {{ available | bytes(1) }}</p>
|
||||
<p>Used: {{ used | bytes(1) }}</p>
|
||||
@ -65,7 +65,7 @@ export default Vue.extend({
|
||||
&:first-child
|
||||
font-weight bold
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
&:after
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="memory">
|
||||
<x-pie class="pie" :value="usage"/>
|
||||
<div>
|
||||
<p>%fa:flask%Memory</p>
|
||||
<p><fa icon="flask"/>Memory</p>
|
||||
<p>Total: {{ total | bytes(1) }}</p>
|
||||
<p>Used: {{ used | bytes(1) }}</p>
|
||||
<p>Free: {{ free | bytes(1) }}</p>
|
||||
@ -65,7 +65,7 @@ export default Vue.extend({
|
||||
&:first-child
|
||||
font-weight bold
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
&:after
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="mkw-server">
|
||||
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
|
||||
<template slot="header">%fa:server%%i18n:@title%</template>
|
||||
<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
|
||||
<template slot="header"><fa icon="server"/>%i18n:@title%</template>
|
||||
<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button>
|
||||
|
||||
<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<template v-if="!fetching">
|
||||
<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
|
||||
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
|
||||
@ -87,7 +87,7 @@ export default define({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mkw-tips">
|
||||
<p ref="tip">%fa:R lightbulb%<span v-html="tip"></span></p>
|
||||
<p ref="tip"><fa :icon="['far', 'lightbulb']"/><span v-html="tip"></span></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -88,7 +88,7 @@ export default define({
|
||||
font-size 0.7em
|
||||
color #999
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
kbd
|
||||
|
@ -23,7 +23,6 @@ import updateBanner from './api/update-banner';
|
||||
import MkIndex from './views/pages/index.vue';
|
||||
import MkHome from './views/pages/home.vue';
|
||||
import MkDeck from './views/pages/deck/deck.vue';
|
||||
import MkStats from './views/pages/stats/stats.vue';
|
||||
import MkUser from './views/pages/user/user.vue';
|
||||
import MkFavorites from './views/pages/favorites.vue';
|
||||
import MkSelectDrive from './views/pages/selectdrive.vue';
|
||||
@ -56,7 +55,6 @@ init(async (launch) => {
|
||||
{ path: '/', name: 'index', component: MkIndex },
|
||||
{ path: '/home', name: 'home', component: MkHome },
|
||||
{ path: '/deck', name: 'deck', component: MkDeck },
|
||||
{ path: '/stats', name: 'stats', component: MkStats },
|
||||
{ path: '/i/customize-home', component: MkHomeCustomize },
|
||||
{ path: '/i/favorites', component: MkFavorites },
|
||||
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="mk-activity">
|
||||
<mk-widget-container :show-header="design == 0" :naked="design == 2">
|
||||
<template slot="header">%fa:chart-bar%%i18n:@title%</template>
|
||||
<button slot="func" title="%i18n:@toggle%" @click="toggle">%fa:sort%</button>
|
||||
<template slot="header"><fa icon="chart-bar"/>%i18n:@title%</template>
|
||||
<button slot="func" title="%i18n:@toggle%" @click="toggle"><fa icon="sort"/></button>
|
||||
|
||||
<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
|
||||
<template v-else>
|
||||
<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
|
||||
<x-chart v-show="view == 1" :data="[].concat(activity)"/>
|
||||
@ -78,7 +78,7 @@ export default Vue.extend({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
</style>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="mk-calendar" :data-melt="design == 4 || design == 5">
|
||||
<template v-if="design == 0 || design == 1">
|
||||
<button @click="prev" title="%i18n:@prev%">%fa:chevron-circle-left%</button>
|
||||
<button @click="prev" title="%i18n:@prev%"><fa icon="chevron-circle-left"/></button>
|
||||
<p class="title">{{ '%i18n:@title%'.replace('{1}', year).replace('{2}', month) }}</p>
|
||||
<button @click="next" title="%i18n:@next%">%fa:chevron-circle-right%</button>
|
||||
<button @click="next" title="%i18n:@next%"><fa icon="chevron-circle-right"/></button>
|
||||
</template>
|
||||
|
||||
<div class="calendar">
|
||||
@ -151,7 +151,7 @@ export default Vue.extend({
|
||||
background var(--faceHeader)
|
||||
box-shadow 0 1px rgba(#000, 0.07)
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> button
|
||||
|
@ -1,42 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
import { Line } from 'vue-chartjs';
|
||||
import * as mergeOptions from 'merge-options';
|
||||
|
||||
export default Vue.extend({
|
||||
extends: Line,
|
||||
props: {
|
||||
data: {
|
||||
required: true
|
||||
},
|
||||
opts: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.render();
|
||||
},
|
||||
methods: {
|
||||
render() {
|
||||
this.renderChart(this.data, mergeOptions({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
distribution: 'series'
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
intersect: false,
|
||||
mode: 'index',
|
||||
position: 'nearest'
|
||||
}
|
||||
}, this.opts || {}));
|
||||
}
|
||||
}
|
||||
});
|
@ -1,723 +0,0 @@
|
||||
<template>
|
||||
<div class="gkgckalzgidaygcxnugepioremxvxvpt">
|
||||
<header>
|
||||
<b>%i18n:@title%:</b>
|
||||
<select v-model="chartType">
|
||||
<optgroup label="%i18n:@federation%">
|
||||
<option value="federation-instances">%i18n:@charts.federation-instances%</option>
|
||||
<option value="federation-instances-total">%i18n:@charts.federation-instances-total%</option>
|
||||
</optgroup>
|
||||
<optgroup label="%i18n:@users%">
|
||||
<option value="users">%i18n:@charts.users%</option>
|
||||
<option value="users-total">%i18n:@charts.users-total%</option>
|
||||
</optgroup>
|
||||
<optgroup label="%i18n:@notes%">
|
||||
<option value="notes">%i18n:@charts.notes%</option>
|
||||
<option value="local-notes">%i18n:@charts.local-notes%</option>
|
||||
<option value="remote-notes">%i18n:@charts.remote-notes%</option>
|
||||
<option value="notes-total">%i18n:@charts.notes-total%</option>
|
||||
</optgroup>
|
||||
<optgroup label="%i18n:@drive%">
|
||||
<option value="drive-files">%i18n:@charts.drive-files%</option>
|
||||
<option value="drive-files-total">%i18n:@charts.drive-files-total%</option>
|
||||
<option value="drive">%i18n:@charts.drive%</option>
|
||||
<option value="drive-total">%i18n:@charts.drive-total%</option>
|
||||
</optgroup>
|
||||
<optgroup label="%i18n:@network%">
|
||||
<option value="network-requests">%i18n:@charts.network-requests%</option>
|
||||
<option value="network-time">%i18n:@charts.network-time%</option>
|
||||
<option value="network-usage">%i18n:@charts.network-usage%</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<div>
|
||||
<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
|
||||
</div>
|
||||
</header>
|
||||
<div>
|
||||
<x-chart v-if="chart" :data="data[0]" :opts="data[1]"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XChart from './charts.chart.ts';
|
||||
|
||||
const colors = {
|
||||
local: 'rgb(246, 88, 79)',
|
||||
remote: 'rgb(65, 221, 222)',
|
||||
|
||||
localPlus: 'rgb(52, 178, 118)',
|
||||
remotePlus: 'rgb(158, 255, 209)',
|
||||
localMinus: 'rgb(255, 97, 74)',
|
||||
remoteMinus: 'rgb(255, 149, 134)',
|
||||
|
||||
incoming: 'rgb(52, 178, 118)',
|
||||
outgoing: 'rgb(255, 97, 74)',
|
||||
};
|
||||
|
||||
const rgba = (color: string): string => {
|
||||
return color.replace('rgb', 'rgba').replace(')', ', 0.1)');
|
||||
};
|
||||
|
||||
const limit = 35;
|
||||
|
||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||
const negate = arr => arr.map(x => -x);
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XChart
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
now: null,
|
||||
chart: null,
|
||||
chartType: 'notes',
|
||||
span: 'hour'
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
data(): any {
|
||||
if (this.chart == null) return null;
|
||||
switch (this.chartType) {
|
||||
case 'federation-instances': return this.federationInstancesChart(false);
|
||||
case 'federation-instances-total': return this.federationInstancesChart(true);
|
||||
case 'users': return this.usersChart(false);
|
||||
case 'users-total': return this.usersChart(true);
|
||||
case 'notes': return this.notesChart('combined');
|
||||
case 'local-notes': return this.notesChart('local');
|
||||
case 'remote-notes': return this.notesChart('remote');
|
||||
case 'notes-total': return this.notesTotalChart();
|
||||
case 'drive': return this.driveChart();
|
||||
case 'drive-total': return this.driveTotalChart();
|
||||
case 'drive-files': return this.driveFilesChart();
|
||||
case 'drive-files-total': return this.driveFilesTotalChart();
|
||||
case 'network-requests': return this.networkRequestsChart();
|
||||
case 'network-time': return this.networkTimeChart();
|
||||
case 'network-usage': return this.networkUsageChart();
|
||||
}
|
||||
},
|
||||
|
||||
stats(): any[] {
|
||||
const stats =
|
||||
this.span == 'day' ? this.chart.perDay :
|
||||
this.span == 'hour' ? this.chart.perHour :
|
||||
null;
|
||||
|
||||
return stats;
|
||||
}
|
||||
},
|
||||
|
||||
async created() {
|
||||
this.now = new Date();
|
||||
|
||||
const [perHour, perDay] = await Promise.all([Promise.all([
|
||||
(this as any).api('charts/federation', { limit: limit, span: 'hour' }),
|
||||
(this as any).api('charts/users', { limit: limit, span: 'hour' }),
|
||||
(this as any).api('charts/notes', { limit: limit, span: 'hour' }),
|
||||
(this as any).api('charts/drive', { limit: limit, span: 'hour' }),
|
||||
(this as any).api('charts/network', { limit: limit, span: 'hour' })
|
||||
]), Promise.all([
|
||||
(this as any).api('charts/federation', { limit: limit, span: 'day' }),
|
||||
(this as any).api('charts/users', { limit: limit, span: 'day' }),
|
||||
(this as any).api('charts/notes', { limit: limit, span: 'day' }),
|
||||
(this as any).api('charts/drive', { limit: limit, span: 'day' }),
|
||||
(this as any).api('charts/network', { limit: limit, span: 'day' })
|
||||
])]);
|
||||
|
||||
const chart = {
|
||||
perHour: {
|
||||
federation: perHour[0],
|
||||
users: perHour[1],
|
||||
notes: perHour[2],
|
||||
drive: perHour[3],
|
||||
network: perHour[4]
|
||||
},
|
||||
perDay: {
|
||||
federation: perDay[0],
|
||||
users: perDay[1],
|
||||
notes: perDay[2],
|
||||
drive: perDay[3],
|
||||
network: perDay[4]
|
||||
}
|
||||
};
|
||||
|
||||
this.chart = chart;
|
||||
},
|
||||
|
||||
methods: {
|
||||
getDate(i: number) {
|
||||
const y = this.now.getFullYear();
|
||||
const m = this.now.getMonth();
|
||||
const d = this.now.getDate();
|
||||
const h = this.now.getHours();
|
||||
|
||||
return (
|
||||
this.span == 'day' ? new Date(y, m, d - i) :
|
||||
this.span == 'hour' ? new Date(y, m, d, h - i) :
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
format(arr) {
|
||||
return arr.map((v, i) => ({ t: this.getDate(i).getTime(), y: v }));
|
||||
},
|
||||
|
||||
federationInstancesChart(total: boolean): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Instances',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localPlus),
|
||||
borderColor: colors.localPlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(total
|
||||
? this.stats.federation.instance.total
|
||||
: sum(this.stats.federation.instance.inc, negate(this.stats.federation.instance.dec)))
|
||||
}]
|
||||
}];
|
||||
},
|
||||
|
||||
notesChart(type: string): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'All',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(type == 'combined'
|
||||
? sum(this.stats.notes.local.inc, negate(this.stats.notes.local.dec), this.stats.notes.remote.inc, negate(this.stats.notes.remote.dec))
|
||||
: sum(this.stats.notes[type].inc, negate(this.stats.notes[type].dec))
|
||||
)
|
||||
}, {
|
||||
label: 'Renotes',
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(161, 222, 65, 0.1)',
|
||||
borderColor: '#a1de41',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(type == 'combined'
|
||||
? sum(this.stats.notes.local.diffs.renote, this.stats.notes.remote.diffs.renote)
|
||||
: this.stats.notes[type].diffs.renote
|
||||
)
|
||||
}, {
|
||||
label: 'Replies',
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(247, 121, 108, 0.1)',
|
||||
borderColor: '#f7796c',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(type == 'combined'
|
||||
? sum(this.stats.notes.local.diffs.reply, this.stats.notes.remote.diffs.reply)
|
||||
: this.stats.notes[type].diffs.reply
|
||||
)
|
||||
}, {
|
||||
label: 'Normal',
|
||||
fill: true,
|
||||
backgroundColor: 'rgba(65, 221, 222, 0.1)',
|
||||
borderColor: '#41ddde',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(type == 'combined'
|
||||
? sum(this.stats.notes.local.diffs.normal, this.stats.notes.remote.diffs.normal)
|
||||
: this.stats.notes[type].diffs.normal
|
||||
)
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('number')(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
notesTotalChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Combined',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(sum(this.stats.notes.local.total, this.stats.notes.remote.total))
|
||||
}, {
|
||||
label: 'Local',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.local),
|
||||
borderColor: colors.local,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.notes.local.total)
|
||||
}, {
|
||||
label: 'Remote',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remote),
|
||||
borderColor: colors.remote,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.notes.remote.total)
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('number')(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
usersChart(total: boolean): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Combined',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(total
|
||||
? sum(this.stats.users.local.total, this.stats.users.remote.total)
|
||||
: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec), this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
|
||||
)
|
||||
}, {
|
||||
label: 'Local',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.local),
|
||||
borderColor: colors.local,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(total
|
||||
? this.stats.users.local.total
|
||||
: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec))
|
||||
)
|
||||
}, {
|
||||
label: 'Remote',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remote),
|
||||
borderColor: colors.remote,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(total
|
||||
? this.stats.users.remote.total
|
||||
: sum(this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
|
||||
)
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('number')(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
driveChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'All',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(sum(this.stats.drive.local.incSize, negate(this.stats.drive.local.decSize), this.stats.drive.remote.incSize, negate(this.stats.drive.remote.decSize)))
|
||||
}, {
|
||||
label: 'Local +',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localPlus),
|
||||
borderColor: colors.localPlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.local.incSize)
|
||||
}, {
|
||||
label: 'Local -',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localMinus),
|
||||
borderColor: colors.localMinus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(negate(this.stats.drive.local.decSize))
|
||||
}, {
|
||||
label: 'Remote +',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remotePlus),
|
||||
borderColor: colors.remotePlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.remote.incSize)
|
||||
}, {
|
||||
label: 'Remote -',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remoteMinus),
|
||||
borderColor: colors.remoteMinus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(negate(this.stats.drive.remote.decSize))
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('bytes')(value, 1);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
driveTotalChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Combined',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
|
||||
}, {
|
||||
label: 'Local',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.local),
|
||||
borderColor: colors.local,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.local.totalSize)
|
||||
}, {
|
||||
label: 'Remote',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remote),
|
||||
borderColor: colors.remote,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.remote.totalSize)
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('bytes')(value, 1);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
driveFilesChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'All',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(sum(this.stats.drive.local.incCount, negate(this.stats.drive.local.decCount), this.stats.drive.remote.incCount, negate(this.stats.drive.remote.decCount)))
|
||||
}, {
|
||||
label: 'Local +',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localPlus),
|
||||
borderColor: colors.localPlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.local.incCount)
|
||||
}, {
|
||||
label: 'Local -',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localMinus),
|
||||
borderColor: colors.localMinus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(negate(this.stats.drive.local.decCount))
|
||||
}, {
|
||||
label: 'Remote +',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remotePlus),
|
||||
borderColor: colors.remotePlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.remote.incCount)
|
||||
}, {
|
||||
label: 'Remote -',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remoteMinus),
|
||||
borderColor: colors.remoteMinus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(negate(this.stats.drive.remote.decCount))
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('number')(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
driveFilesTotalChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Combined',
|
||||
fill: false,
|
||||
borderColor: '#555',
|
||||
borderWidth: 2,
|
||||
borderDash: [4, 4],
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(sum(this.stats.drive.local.totalCount, this.stats.drive.remote.totalCount))
|
||||
}, {
|
||||
label: 'Local',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.local),
|
||||
borderColor: colors.local,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.local.totalCount)
|
||||
}, {
|
||||
label: 'Remote',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.remote),
|
||||
borderColor: colors.remote,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.drive.remote.totalCount)
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('number')(value);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
|
||||
networkRequestsChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Incoming',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localPlus),
|
||||
borderColor: colors.localPlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.network.incomingRequests)
|
||||
}]
|
||||
}];
|
||||
},
|
||||
|
||||
networkTimeChart(): any {
|
||||
const data = [];
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
data.push(this.stats.network.incomingRequests[i] != 0 ? (this.stats.network.totalTime[i] / this.stats.network.incomingRequests[i]) : 0);
|
||||
}
|
||||
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Avg time (ms)',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.localPlus),
|
||||
borderColor: colors.localPlus,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(data)
|
||||
}]
|
||||
}];
|
||||
},
|
||||
|
||||
networkUsageChart(): any {
|
||||
return [{
|
||||
datasets: [{
|
||||
label: 'Incoming',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.incoming),
|
||||
borderColor: colors.incoming,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.network.incomingBytes)
|
||||
}, {
|
||||
label: 'Outgoing',
|
||||
fill: true,
|
||||
backgroundColor: rgba(colors.outgoing),
|
||||
borderColor: colors.outgoing,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#fff',
|
||||
lineTension: 0,
|
||||
data: this.format(this.stats.network.outgoingBytes)
|
||||
}]
|
||||
}, {
|
||||
scales: {
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
callback: value => {
|
||||
return Vue.filter('bytes')(value, 1);
|
||||
}
|
||||
}
|
||||
}]
|
||||
},
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.gkgckalzgidaygcxnugepioremxvxvpt
|
||||
padding 32px
|
||||
background #fff
|
||||
box-shadow 0 2px 8px rgba(#000, 0.1)
|
||||
|
||||
*
|
||||
user-select none
|
||||
|
||||
> header
|
||||
display flex
|
||||
margin 0 0 1em 0
|
||||
padding 0 0 8px 0
|
||||
font-size 1em
|
||||
color #555
|
||||
border-bottom solid 1px #eee
|
||||
|
||||
> b
|
||||
margin-right 8px
|
||||
|
||||
> *:last-child
|
||||
margin-left auto
|
||||
|
||||
*
|
||||
&:not(.active)
|
||||
color var(--primary)
|
||||
cursor pointer
|
||||
|
||||
> div
|
||||
> *
|
||||
display block
|
||||
height 350px
|
||||
|
||||
</style>
|
@ -13,7 +13,7 @@
|
||||
@change-selection="onChangeSelection"
|
||||
/>
|
||||
<div :class="$style.footer">
|
||||
<button :class="$style.upload" title="%i18n:@upload%" @click="upload">%fa:upload%</button>
|
||||
<button :class="$style.upload" title="%i18n:@upload%" @click="upload"><fa icon="upload"/></button>
|
||||
<button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
|
||||
<button :class="$style.ok" :disabled="multiple && files.length == 0" @click="ok">%i18n:@ok%</button>
|
||||
</div>
|
||||
@ -28,7 +28,7 @@ export default Vue.extend({
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
default: '%fa:R file%%i18n:@choose-prompt%'
|
||||
default: '<fa :icon="['far', 'file']"/>%i18n:@choose-prompt%'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -62,7 +62,7 @@ export default Vue.extend({
|
||||
|
||||
|
||||
.title
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
.count
|
||||
|
@ -21,7 +21,7 @@ import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
title: {
|
||||
default: '%fa:R folder%%i18n:@choose-prompt%'
|
||||
default: '<fa :icon="['far', 'folder']"/>%i18n:@choose-prompt%'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -40,7 +40,7 @@ export default Vue.extend({
|
||||
|
||||
|
||||
.title
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
.browser
|
||||
|
@ -3,13 +3,13 @@
|
||||
<li v-for="(item, i) in menu" :class="item ? item.type : item === null ? 'divider' : null">
|
||||
<template v-if="item">
|
||||
<template v-if="item.type == null || item.type == 'item'">
|
||||
<p @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
|
||||
<p @click="click(item)"><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}</p>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'link'">
|
||||
<a :href="item.href" :target="item.target" @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</a>
|
||||
<a :href="item.href" :target="item.target" @click="click(item)"><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}</a>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'nest'">
|
||||
<p><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
|
||||
<p><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}...<span class="caret"><fa icon="caret-right"/></span></p>
|
||||
<me-nu :menu="item.menu" @x="click"/>
|
||||
</template>
|
||||
</template>
|
||||
@ -113,9 +113,9 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" module>
|
||||
.icon
|
||||
> *
|
||||
width 28px
|
||||
margin-left -28px
|
||||
text-align center
|
||||
display inline-block
|
||||
width 28px
|
||||
margin-left -28px
|
||||
text-align center
|
||||
</style>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<mk-window ref="window" is-modal width="800px" :can-close="false">
|
||||
<span slot="header">%fa:crop%{{ title }}</span>
|
||||
<span slot="header"><fa icon="crop"/>{{ title }}</span>
|
||||
<div class="body">
|
||||
<vue-cropper ref="cropper"
|
||||
:src="image.url"
|
||||
@ -64,7 +64,7 @@ export default Vue.extend({
|
||||
|
||||
|
||||
.header
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
.img
|
||||
|
@ -91,8 +91,6 @@ export default Vue.extend({
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
|
||||
.mk-dialog
|
||||
> .bg
|
||||
display block
|
||||
|
@ -2,7 +2,7 @@
|
||||
<mk-window ref="window" @closed="destroyDom" width="800px" height="500px" :popout-url="popout">
|
||||
<template slot="header">
|
||||
<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:@used%</p>
|
||||
<span :class="$style.title">%fa:cloud%%i18n:common.drive%</span>
|
||||
<span :class="$style.title"><fa icon="cloud"/>%i18n:common.drive%</span>
|
||||
</template>
|
||||
<mk-drive :class="$style.browser" multiple :init-folder="folder" ref="browser"/>
|
||||
</mk-window>
|
||||
@ -39,7 +39,7 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" module>
|
||||
.title
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
.info
|
||||
|
@ -71,27 +71,27 @@ export default Vue.extend({
|
||||
contextmenu((this as any).os)(e, [{
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.rename%',
|
||||
icon: '%fa:i-cursor%',
|
||||
icon: 'i-cursor',
|
||||
action: this.rename
|
||||
}, {
|
||||
type: 'item',
|
||||
text: this.file.isSensitive ? '%i18n:@contextmenu.unmark-as-sensitive%' : '%i18n:@contextmenu.mark-as-sensitive%',
|
||||
icon: this.file.isSensitive ? '%fa:R eye%' : '%fa:R eye-slash%',
|
||||
icon: this.file.isSensitive ? ['far', 'eye'] : ['far', 'eye-slash'],
|
||||
action: this.toggleSensitive
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.copy-url%',
|
||||
icon: '%fa:link%',
|
||||
icon: 'link',
|
||||
action: this.copyUrl
|
||||
}, {
|
||||
type: 'link',
|
||||
href: `${this.file.url}?download`,
|
||||
text: '%i18n:@contextmenu.download%',
|
||||
icon: '%fa:download%',
|
||||
icon: 'download',
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:common.delete%',
|
||||
icon: '%fa:R trash-alt%',
|
||||
icon: ['far', 'trash-alt'],
|
||||
action: this.deleteFile
|
||||
}, null, {
|
||||
type: 'nest',
|
||||
@ -170,7 +170,7 @@ export default Vue.extend({
|
||||
copyUrl() {
|
||||
copyToClipboard(this.file.url);
|
||||
(this as any).apis.dialog({
|
||||
title: '%fa:check%%i18n:@contextmenu.copied%',
|
||||
title: '<fa icon="check"/>%i18n:@contextmenu.copied%',
|
||||
text: '%i18n:@contextmenu.copied-url-to-clipboard%',
|
||||
actions: [{
|
||||
text: '%i18n:common.ok%'
|
||||
|
@ -16,8 +16,8 @@
|
||||
:title="title"
|
||||
>
|
||||
<p class="name">
|
||||
<template v-if="hover">%fa:R folder-open .fw%</template>
|
||||
<template v-if="!hover">%fa:R folder .fw%</template>
|
||||
<template v-if="hover"><fa :icon="['far', 'folder-open']" fixed-width/></template>
|
||||
<template v-if="!hover"><fa :icon="['far', 'folder']" fixed-width/></template>
|
||||
{{ folder.name }}
|
||||
</p>
|
||||
</div>
|
||||
@ -55,22 +55,22 @@ export default Vue.extend({
|
||||
contextmenu((this as any).os)(e, [{
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.move-to-this-folder%',
|
||||
icon: '%fa:arrow-right%',
|
||||
icon: 'arrow-right',
|
||||
action: this.go
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.show-in-new-window%',
|
||||
icon: '%fa:R window-restore%',
|
||||
icon: ['far', 'window-restore'],
|
||||
action: this.newWindow
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.rename%',
|
||||
icon: '%fa:i-cursor%',
|
||||
icon: 'i-cursor',
|
||||
action: this.rename
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:common.delete%',
|
||||
icon: '%fa:R trash-alt%',
|
||||
icon: ['far', 'trash-alt'],
|
||||
action: this.deleteFolder
|
||||
}], {
|
||||
closed: () => {
|
||||
@ -155,7 +155,7 @@ export default Vue.extend({
|
||||
switch (err) {
|
||||
case 'detected-circular-definition':
|
||||
(this as any).apis.dialog({
|
||||
title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
|
||||
title: '<fa icon="exclamation-triangle"/>%i18n:@unable-to-process%',
|
||||
text: '%i18n:@circular-reference-detected%',
|
||||
actions: [{
|
||||
text: '%i18n:common.ok%'
|
||||
@ -255,7 +255,7 @@ export default Vue.extend({
|
||||
font-size 0.9em
|
||||
color var(--desktopDriveFolderFg)
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
margin-left 2px
|
||||
text-align left
|
||||
|
@ -7,7 +7,7 @@
|
||||
@dragleave="onDragleave"
|
||||
@drop.stop="onDrop"
|
||||
>
|
||||
<template v-if="folder == null">%fa:cloud%</template>
|
||||
<i v-if="folder == null" class="cloud"><fa icon="cloud"/></i>
|
||||
<span>{{ folder == null ? '%i18n:common.drive%' : folder.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -110,7 +110,7 @@ export default Vue.extend({
|
||||
&[data-draghover]
|
||||
background #eee
|
||||
|
||||
[data-fa].cloud
|
||||
i.cloud
|
||||
margin-right 4px
|
||||
|
||||
</style>
|
||||
|
@ -4,10 +4,10 @@
|
||||
<div class="path" @contextmenu.prevent.stop="() => {}">
|
||||
<x-nav-folder :class="{ current: folder == null }"/>
|
||||
<template v-for="folder in hierarchyFolders">
|
||||
<span class="separator">%fa:angle-right%</span>
|
||||
<span class="separator"><fa icon="angle-right"/></span>
|
||||
<x-nav-folder :folder="folder" :key="folder.id"/>
|
||||
</template>
|
||||
<span class="separator" v-if="folder != null">%fa:angle-right%</span>
|
||||
<span class="separator" v-if="folder != null"><fa icon="angle-right"/></span>
|
||||
<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
|
||||
</div>
|
||||
<!--
|
||||
@ -138,17 +138,17 @@ export default Vue.extend({
|
||||
contextmenu((this as any).os)(e, [{
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.create-folder%',
|
||||
icon: '%fa:R folder%',
|
||||
icon: ['far', 'folder'],
|
||||
action: this.createFolder
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.upload%',
|
||||
icon: '%fa:upload%',
|
||||
icon: 'upload',
|
||||
action: this.selectLocalFile
|
||||
}, {
|
||||
type: 'item',
|
||||
text: '%i18n:@contextmenu.url-upload%',
|
||||
icon: '%fa:cloud-upload-alt%',
|
||||
icon: 'cloud-upload-alt',
|
||||
action: this.urlUpload
|
||||
}]);
|
||||
},
|
||||
@ -313,7 +313,7 @@ export default Vue.extend({
|
||||
switch (err) {
|
||||
case 'detected-circular-definition':
|
||||
(this as any).apis.dialog({
|
||||
title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
|
||||
title: '<fa icon="exclamation-triangle"/>%i18n:@unable-to-process%',
|
||||
text: '%i18n:@circular-reference-detected%',
|
||||
actions: [{
|
||||
text: '%i18n:common.ok%'
|
||||
@ -343,7 +343,7 @@ export default Vue.extend({
|
||||
});
|
||||
|
||||
(this as any).apis.dialog({
|
||||
title: '%fa:check%%i18n:@url-upload-requested%',
|
||||
title: '<fa icon="check"/>%i18n:@url-upload-requested%',
|
||||
text: '%i18n:@may-take-time%',
|
||||
actions: [{
|
||||
text: '%i18n:common.ok%'
|
||||
@ -613,9 +613,6 @@ export default Vue.extend({
|
||||
line-height 38px
|
||||
cursor pointer
|
||||
|
||||
i
|
||||
margin-right 4px
|
||||
|
||||
*
|
||||
pointer-events none
|
||||
|
||||
@ -635,7 +632,7 @@ export default Vue.extend({
|
||||
opacity 0.5
|
||||
cursor default
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin 0
|
||||
|
||||
> .search
|
||||
|
@ -5,13 +5,13 @@
|
||||
:disabled="wait"
|
||||
>
|
||||
<template v-if="!wait">
|
||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked">%fa:hourglass-half%<template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
|
||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked">%fa:hourglass-start%<template v-if="size == 'big'"> %i18n:@follow-processing%</template></template>
|
||||
<template v-else-if="u.isFollowing">%fa:minus%<template v-if="size == 'big'"> %i18n:@following%</template></template>
|
||||
<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
|
||||
<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow%</template></template>
|
||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
|
||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> %i18n:@follow-processing%</template></template>
|
||||
<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> %i18n:@following%</template></template>
|
||||
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
|
||||
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> %i18n:@follow%</template></template>
|
||||
</template>
|
||||
<template v-else>%fa:spinner .pulse .fw%</template>
|
||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
|
@ -11,9 +11,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>
|
||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
|
||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:@fetching%<mk-ellipsis/></p>
|
||||
<a class="refresh" @click="refresh">%i18n:@refresh%</a>
|
||||
<button class="close" @click="destroyDom()" title="%i18n:@close%">%fa:times%</button>
|
||||
<button class="close" @click="destroyDom()" title="%i18n:@close%"><fa icon="times"/></button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -124,7 +124,7 @@ export default Vue.extend({
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
> .refresh
|
||||
@ -155,7 +155,7 @@ export default Vue.extend({
|
||||
&:active
|
||||
color #222
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
padding 14px
|
||||
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
|
||||
<span slot="header" :class="$style.header">%fa:gamepad%%i18n:@game%</span>
|
||||
<span slot="header" :class="$style.header"><fa icon="gamepad"/>%i18n:@game%</span>
|
||||
<mk-reversi :class="$style.content" @gamed="g => game = g"/>
|
||||
</mk-window>
|
||||
</template>
|
||||
@ -27,7 +27,7 @@ export default Vue.extend({
|
||||
|
||||
<style lang="stylus" module>
|
||||
.header
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
.content
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mk-home" :data-customize="customize">
|
||||
<div class="customize" v-if="customize">
|
||||
<router-link to="/">%fa:check%%i18n:@done%</router-link>
|
||||
<router-link to="/"><fa icon="check"/>%i18n:@done%</router-link>
|
||||
<div>
|
||||
<div class="adder">
|
||||
<p>%i18n:@add-widget%</p>
|
||||
@ -185,7 +185,7 @@ export default Vue.extend({
|
||||
methods: {
|
||||
hint() {
|
||||
(this as any).apis.dialog({
|
||||
title: '%fa:info-circle%%i18n:common.customization-tips.title%',
|
||||
title: '<fa icon="info-circle"/>%i18n:common.customization-tips.title%',
|
||||
text: '<p>%i18n:common.customization-tips.paragraph1%</p>' +
|
||||
'<p>%i18n:common.customization-tips.paragraph2%</p>' +
|
||||
'<p>%i18n:common.customization-tips.paragraph3%</p>' +
|
||||
@ -299,7 +299,7 @@ export default Vue.extend({
|
||||
background var(--primaryDarken10)
|
||||
transition background 0s ease
|
||||
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 8px
|
||||
|
||||
> div
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="destroyDom">
|
||||
<span slot="header" :class="$style.header">
|
||||
%fa:i-cursor%{{ title }}
|
||||
<fa icon="i-cursor"/>{{ title }}
|
||||
</span>
|
||||
|
||||
<div :class="$style.body">
|
||||
@ -76,10 +76,8 @@ export default Vue.extend({
|
||||
|
||||
|
||||
<style lang="stylus" module>
|
||||
|
||||
|
||||
.header
|
||||
> [data-fa]
|
||||
> [data-icon]
|
||||
margin-right 4px
|
||||
|
||||
.body
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="ldwbgwstjsdgcjruamauqdrffetqudry" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
|
||||
<div>
|
||||
<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
|
||||
<b><fa icon="exclamation-triangle"/> %i18n:@sensitive%</b>
|
||||
<span>%i18n:@click-to-show%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="uofhebxjdgksfmltszlxurtjnjjsvioh" v-if="video.isSensitive && hide" @click="hide = false">
|
||||
<div>
|
||||
<b>%fa:exclamation-triangle% %i18n:@sensitive%</b>
|
||||
<b><fa icon="exclamation-triangle"/> %i18n:@sensitive%</b>
|
||||
<span>%i18n:@click-to-show%</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -12,7 +12,7 @@
|
||||
@click.prevent="onClick"
|
||||
:title="video.name"
|
||||
>
|
||||
%fa:R play-circle%
|
||||
<fa :icon="['far', 'play-circle']"/>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user