Compare commits

..

52 Commits

Author SHA1 Message Date
syuilo 8f58e7208d 10.46.0 2018-11-09 21:35:11 +09:00
syuilo cf0b7e26b5 Remove duplicated option 2018-11-09 21:30:12 +09:00
syuilo 3a1c3f9656 [Client] Fix bug 2018-11-09 21:27:27 +09:00
Aya Morisawa 035bdd0279 Add showVia option (#3182)
* Resolve #3160

* Fix bug

* Fix bug
2018-11-09 21:10:21 +09:00
syuilo f7c596beac Delete update-remote-user.js 2018-11-09 19:05:16 +09:00
syuilo ac8817ef34 [Client] Fix bug 2018-11-09 18:38:10 +09:00
syuilo 372db65604 [Client] Fix i18n 2018-11-09 18:34:32 +09:00
syuilo 4a92635eae 🎨 2018-11-09 18:32:09 +09:00
syuilo 5e140d9a11 🎨 2018-11-09 18:25:30 +09:00
syuilo 0b53ef9bae [Client] Fix bug 2018-11-09 18:25:21 +09:00
syuilo 3f79c9ae49 Refactor client (#3178)
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
2018-11-09 16:00:29 +09:00
syuilo 5d882dc3df [Client] Fix bug 2018-11-09 14:19:30 +09:00
syuilo 5b4205bdbc Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-09 14:15:52 +09:00
syuilo 20cf2b3f77 [Client] Fix i18n 2018-11-09 14:15:29 +09:00
Aya Morisawa 3c0d2db3bc Rename: setDifference -> difference (#3177)
* Improve setDifference

* Rename: setDifference -> difference
2018-11-09 14:14:53 +09:00
syuilo 9aa65fb600 [Client] Fix bug 2018-11-09 14:13:40 +09:00
syuilo 4dcb15ef0d [Client] Fix bug 2018-11-09 14:10:50 +09:00
syuilo ae6293cb6b [Client] Improve usability 2018-11-09 14:10:23 +09:00
syuilo 2614771a7c 🎨 2018-11-09 13:54:00 +09:00
syuilo ba2ebfad4f [Client] Fix error 2018-11-09 13:53:44 +09:00
syuilo 51ba738c4b Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-09 13:47:28 +09:00
syuilo c8081ed353 Fix i18n 2018-11-09 13:47:22 +09:00
Aya Morisawa 500fc47618 Add group function (#3175) 2018-11-09 13:03:46 +09:00
Aya Morisawa 276edd7cc2 Use sum function (#3174) 2018-11-09 11:02:23 +09:00
Aya Morisawa a9436306ab Fix #3170 (#3173) 2018-11-09 11:01:55 +09:00
dependabot[bot] 21d9afebc3 Update autobind-decorator requirement from 2.1.0 to 2.2.1 (#3151)
Updates the requirements on [autobind-decorator](https://github.com/andreypopp/autobind-decorator) to permit the latest version.
- [Release notes](https://github.com/andreypopp/autobind-decorator/releases)
- [Changelog](https://github.com/andreypopp/autobind-decorator/blob/master/CHANGELOG.md)
- [Commits](https://github.com/andreypopp/autobind-decorator/commits/v2.2.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-09 08:45:19 +09:00
dependabot[bot] ab66162dbe Update @types/minio requirement from 7.0.0 to 7.0.1 (#3153)
Updates the requirements on [@types/minio](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-09 08:45:06 +09:00
dependabot[bot] 0aacca3e78 Update systeminformation requirement from 3.45.9 to 3.47.0 (#3154)
Updates the requirements on [systeminformation](https://github.com/sebhildebrandt/systeminformation) to permit the latest version.
- [Release notes](https://github.com/sebhildebrandt/systeminformation/releases)
- [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sebhildebrandt/systeminformation/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-09 08:44:58 +09:00
dependabot[bot] bdcaa07cc8 Update vue-sweetalert2 requirement from 1.5.6 to 1.5.7 (#3171)
Updates the requirements on [vue-sweetalert2](https://github.com/avil13/vue-sweetalert2) to permit the latest version.
- [Release notes](https://github.com/avil13/vue-sweetalert2/releases)
- [Commits](https://github.com/avil13/vue-sweetalert2/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-09 08:44:49 +09:00
MeiMei 5b684c6deb On remote notes, not use content for detecting mentions (#3170)
* On remote note, detect mentioned users from to/cc

* fix
2018-11-09 08:44:19 +09:00
syuilo 5ef8a8b5f0 [Client] Fix #3168 2018-11-09 08:41:06 +09:00
syuilo 10fa824f95 [Client] Fix #2759 2018-11-09 08:31:05 +09:00
syuilo fccbecf159 [Client] Fix bug 2018-11-09 08:26:32 +09:00
syuilo 60ef3e3563 Refactor & Clean up 2018-11-09 08:13:34 +09:00
syuilo ba845f5218 Clean up 2018-11-09 04:02:12 +09:00
syuilo 5105981e93 [Doc] Update docs 2018-11-09 03:54:45 +09:00
syuilo f05a688ac2 [API] Fix #3169 2018-11-09 03:52:03 +09:00
syuilo 4bc919a912 [chore] Update dependencies 🚀 2018-11-09 03:46:55 +09:00
syuilo 25a69ec1b6 Refactoring of i18n (#3165)
Refactoring of i18n
2018-11-09 03:44:35 +09:00
Acid Chicken (硫酸鶏) 21303bd06a Update CircleCI configuration (#3163)
* Update config.yml

* Update config.yml
2018-11-08 18:49:45 +09:00
Aya Morisawa 480d1c9f09 Justify margin (#3164) 2018-11-08 15:47:10 +09:00
Aya Morisawa 57f6ce280e Update README.md (#3159) 2018-11-08 15:19:14 +09:00
syuilo f052d8912b wip: フォロー/フォロワーページ (#3157)
フォロー/フォロワーページ
2018-11-08 15:16:39 +09:00
Xeltica 6c95120023 via の表示を改善 (#3161) 2018-11-08 13:54:59 +09:00
syuilo 24766fb79e 🎨 2018-11-08 11:20:50 +09:00
dependabot[bot] 300d3da6ff Update apexcharts requirement from 2.1.9 to 2.2.0 (#3152)
Updates the requirements on [apexcharts](https://github.com/apexcharts/apexcharts.js) to permit the latest version.
- [Release notes](https://github.com/apexcharts/apexcharts.js/releases)
- [Changelog](https://github.com/apexcharts/apexcharts.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/apexcharts/apexcharts.js/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-08 10:18:01 +09:00
syuilo d779e18546 10.45.0 2018-11-08 02:10:36 +09:00
syuilo 3261d54cd3 Resolve #2320 2018-11-08 02:09:15 +09:00
syuilo e0ec56abb5 Fix bug 2018-11-08 01:42:02 +09:00
syuilo 1056a7167d [Client] Improve usabiliy 2018-11-07 23:04:59 +09:00
syuilo b8e1162e2d Fix test 2018-11-07 21:30:28 +09:00
syuilo 4c81e400c4 [MFM] Fix title parsing 2018-11-07 21:17:27 +09:00
282 changed files with 2946 additions and 3037 deletions
+33 -7
View File
@@ -1,11 +1,5 @@
version: 2.1 version: 2.1
general:
branches:
ignore:
- l10n_develop
- imgbot
executors: executors:
default: default:
working_directory: /tmp/workspace working_directory: /tmp/workspace
@@ -17,8 +11,20 @@ executors:
working_directory: /tmp/workspace working_directory: /tmp/workspace
docker: docker:
- image: docker:latest - image: docker:latest
alpine:
working_directory: /tmp/workspace
docker:
- image: alpine:latest
jobs: jobs:
ok:
executor: alpine
steps:
- run:
name: OK
command: |
echo -e '\033[0;32mOK\033[0;39m'
build: build:
executor: default executor: default
steps: steps:
@@ -111,6 +117,9 @@ jobs:
command: | command: |
if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ] if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
then then
curl -LSs 'https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64' > jq
chmod +x jq
docker tag misskey/misskey misskey/misskey:$(cat package.json | jq -r .version)
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
docker push misskey/misskey docker push misskey/misskey
else else
@@ -121,10 +130,27 @@ workflows:
version: 2 version: 2
build-and-test: build-and-test:
jobs: jobs:
- build - ok:
filters:
branches:
only:
- l10n_develop
- imgbot
- build:
filters:
branches:
ignore:
- l10n_develop
- imgbot
- test: - test:
requires: requires:
- build - build
filters:
branches:
ignore:
# - master
- l10n_develop
- imgbot
- test: - test:
without_redis: "true" without_redis: "true"
requires: requires:
-3
View File
@@ -6,9 +6,6 @@ Feature suggestions and bug reports are filed in https://github.com/syuilo/missk
Before creating a new issue, please search existing issues to avoid duplication. Before creating a new issue, please search existing issues to avoid duplication.
If you find the existing issue, please add your reaction or comment to the issue. If you find the existing issue, please add your reaction or comment to the issue.
## Internationalization (i18n)
Please see [Translation guide](./docs/translate.en.md).
## Localization (l10n) ## Localization (l10n)
Please use [Crowdin](https://crowdin.com/project/misskey) for localization. Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
+13 -11
View File
@@ -9,10 +9,12 @@
**Sophisticated microblogging platform, evolving forever.** **Sophisticated microblogging platform, evolving forever.**
[Misskey](https://misskey.xyz) is a decentralized microblogging platform born on Earth. <p align="justify">
<a href="https://misskey.xyz">Misskey</a> is a decentralized microblogging platform born on Earth.
Since it exists within the Fediverse (a universe where various social media platforms are organized), Since it exists within the Fediverse (a universe where various social media platforms are organized),
it is mutually linked with other social media platforms. it is mutually linked with other social media platforms.
Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet? [Find instance!](https://joinmisskey.github.io/) Why don't you take a short break from the hustle and bustle of the city, and dive into a new Internet? <a href="https://joinmisskey.github.io/">Find instance!</a>
</p>
<a href="https://www.patreon.com/syuilo"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="Become a Patron!" width="160" /></a> <a href="https://www.patreon.com/syuilo"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="Become a Patron!" width="160" /></a>
@@ -24,8 +26,8 @@ Why don't you take a short break from the hustle and bustle of the city, and div
<img src="/assets/about/post.png" align="left" height="200px"/> <img src="/assets/about/post.png" align="left" height="200px"/>
<h3 align="left">Posting</h3> <h3 align="left">Posting</h3>
<p align="left"> <p align="justify">
Just post your idea, hot topics and anything you want to share. You may want to decorate your words, attach your favorite pictures, send files including movies and create a poll - those are the things you can do on Misskey! Just post your idea, hot topics and anything you want to share. You may decorate your words, attach your favorite pictures or movies, and create a poll - those are all supported in Misskey!
</p> </p>
--- ---
@@ -33,8 +35,8 @@ Just post your idea, hot topics and anything you want to share. You may want to
<img src="/assets/about/reaction.png" align="right" height="200px"/> <img src="/assets/about/reaction.png" align="right" height="200px"/>
<h3 align="right">Reactions</h3> <h3 align="right">Reactions</h3>
<p align="right"> <p align="justify">
Easiest way to tell your emotions. Misskey allows you to add various type of reactions to others post. The emotional experience on Misskey will never be on other SNSs which only able to push “likes”. The simplest way to tell your emotions to the posts. You can choose the best reaction from various reactions. Reactions on Misskey has much more expressive than other social media which only allows pushing “likes”.
</p> </p>
--- ---
@@ -42,8 +44,8 @@ Easiest way to tell your emotions. Misskey allows you to add various type of rea
<img src="/assets/about/ui.png" align="left" height="200px"/> <img src="/assets/about/ui.png" align="left" height="200px"/>
<h3 align="left">Interface</h3> <h3 align="left">Interface</h3>
<p align="left"> <p align="justify">
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. Highly customizable UI for your taste. We understand no UI fits for everyone. Make your graceful home by editing, adjusting layouts of timeline, and placing widgets.
</p> </p>
--- ---
@@ -51,13 +53,13 @@ Highly customizable UI for your taste. We understand no UI fits for everyone. Yo
<img src="/assets/about/drive.png" align="right" width="300px"/> <img src="/assets/about/drive.png" align="right" width="300px"/>
<h3 align="right">Misskey Drive</h3> <h3 align="right">Misskey Drive</h3>
<p align="right"> <p align="justify">
Wanna post a picture you have already uploaded? Wish to organize, name and create a folder for your uploaded files? Misskey Drive is the best solution for you. Very easy to share your files online. Organized uploaded files. Wanna post a picture you have already uploaded? Wish to create a folder for your files? Misskey Drive is the best solution for you.
</p> </p>
--- ---
and more! You can see it with your own eyes at [misskey.xyz](https://misskey.xyz) or [other instances](https://joinmisskey.github.io/). and more! Now it's time to experience the world with your own eyes at [misskey.xyz](https://misskey.xyz) or [other instances](https://joinmisskey.github.io/).
:package: Create your own instance :package: Create your own instance
---------------------------------------------------------------- ----------------------------------------------------------------
-12
View File
@@ -1,12 +0,0 @@
const updatePerson = require('../built/remote/activitypub/models/person').updatePerson;
const args = process.argv.slice(2);
const user = args[0];
console.log(`Updating ${user}...`);
updatePerson(user).then(() => {
console.log(`Updated ${user}`);
}, e => {
console.error(e);
});
-27
View File
@@ -1,27 +0,0 @@
Misskey's Translation
=====================
If you find an untranslated part on Misskey:
--------------------------------------------
1. Look for untranslated parts in the misskey's source code.
- For instance, if you find an untranslated part in: `src/client/app/mobile/views/pages/home.vue`.
2. Replace the untranslated portion with a character string of the form `%i18n:@foo%`.
- In fact, `foo` should be a word that is appropriate for the situation and is easy to understand in English.
- For example, if the untranslated portion is the following "タイムライン" you must write: `%i18n:@timeline%`.
3. Open the `locales/ja-JP.yml`, check whether the <strong>file name (path)</strong> found in step 1 exists, if not, create it.
- Do not put the beginning of the path `src/client/app/` in the locale file.
- For example, in this case we want to modify untranslated parts of `src/client/app/mobile/views/pages/home.vue`, so the key is `mobile/views/pages/home.vue`.
4. Add the text property using the `foo` keyword below the path that you found or created in step 2. Make sure to type your text in quotation marks. Text should always be inside of quotes.
- For example, in this case we add timeline: `timeline: "タイムライン"` to `locales/ja-JP.yml`.
5. When you add text to the ja-JP file (of syuilo/misskey), it will automatically be applied to all other local language files within 24-48 hours. Translations added in ja-JP file should contain the original Japanese strings (example see step 4).
6. The new strings will automatically appear in the localized language files in the original Japanese text. After that, please go to [CrowdIn](https://crowdin.com/project/misskey) to do the localized translations in your language.
7. And done
For more details, please refer to this [commit](https://github.com/syuilo/misskey/commit/10f6d5980fa7692ccb45fbc5f843458b69b7607c).
-23
View File
@@ -1,23 +0,0 @@
Traduction de Misskey
=====================
Si vous trouvez un segment non-traduit sur Misskey :
----------------------------------------------------
1. Veuillez chercher des parties non-traduites dans le code source de Misskey.
- Par exemple, supposons que vous trouviez un segment non-traduit dans : `src/client/app/mobile/views/pages/home.vue`.
2. Remplacez la portion non-traduite par une chaîne de caractères de type `%i18n:@foo%`.
- En fait, `foo` doit être un mot approprié à la situation et facile à comprendre en français.
- Par exemple, si le segment non-traduit est「タイムライン」on peut écrire : `%i18n:@timeline%`.
3. Ouvrez chaque fichier linguistique dans /locales, vérifiez si le <strong>nom du fichier (chemin)</strong> trouvé dans l'étape 1 existe, sinon créez-le.
- Ne mettez pas le début du chemin `src/client/app/` dans les fichiers /locales.
- Par exemple, dans ce cas de figure, nous voulons modifier le segment non-traduit de : `src/client/app/mobile/views/pages/home.vue`donc il faut juste écrire : `mobile/views/pages/home.vue` dans les fichiers linguistiques.
4. Ajoutez la propriété du texte traduit grâce à la clef `foo`, en-dessous du chemin correspondant à votre modification que vous avez trouvé ou créé dans l'étape 2. À côté, veuillez indiquer entre "guillemets" la valeur de votre traduction.
- Par exemple, dans ce cas de figure, nous ajoutons la propriété et la traduction `timeline: "Timeline"` à `locales/fr.yml`, mais aussi la propriété et la version originale `timeline: "タイムライン"` à `locales/ja-JP.yml`.
5. Vous avez réussi à traduire une portion de misskey
Pour plus de détails, veuillez vous référer à ce [commit](https://github.com/syuilo/misskey/commit/10f6d5980fa7692ccb45fbc5f843458b69b7607c).
-23
View File
@@ -1,23 +0,0 @@
Misskeyの翻訳
============
Misskey内の未翻訳箇所を見つけたら
-------------------------------
1. Misskeyのソースコード内から未翻訳箇所を探してください。
- 例えば`src/client/app/mobile/views/pages/home.vue`で未翻訳箇所を見つけたとします。
2. 未翻訳箇所を`%i18n:@foo%`のような形式の文字列に置換してください。
- `foo`は実際にはその場に適したわかりやすい(英語の)名前にしてください。
- 例えば未翻訳箇所が「タイムライン」というテキストだった場合、`%i18n:@timeline%`のようにします。
3. `locales/ja-JP.yml`を開き、1.で見つけた<strong>ファイル名(パス)</strong>のキーが存在するか確認し、無ければ作成してください。
- パスの`src/client/app/`は省略してください。
- 例えば、今回の例では`src/client/app/mobile/views/pages/home.vue`の未翻訳箇所を修正したいので、キーは`mobile/views/pages/home.vue`になります。
4. そのキーの直下に2.で置換した`foo`の部分をキーとし、テキストを値とするプロパティを追加します。
- 例えば、今回の例で言うと`locales/ja-JP.yml``timeline: "タイムライン"`を追加します。
5. 完了です!
詳しくは、[このコミット](https://github.com/syuilo/misskey/commit/10f6d5980fa7692ccb45fbc5f843458b69b7607c)などを参考にしてください。
+59 -105
View File
@@ -25,22 +25,12 @@ common:
application-authorization: "アプリの連携" application-authorization: "アプリの連携"
close: "閉じる" close: "閉じる"
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。" do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
BSoD: load-more: "もっと読み込む"
fatal-error: ":( 致命的な問題が発生しました。"
update-browser-os: "お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。"
error-code: "エラーコード"
browser-version: "ブラウザ バージョン"
client-version: "クライアント バージョン"
email-support: "問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。"
thanks: "Thank you for using Misskey."
got-it: "わかった" got-it: "わかった"
customization-tips: customization-tips:
title: "カスタマイズのヒント" title: "カスタマイズのヒント"
paragraph1: "ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。" paragraph: "<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p><p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p><p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p><p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>"
paragraph2: "一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。"
paragraph3: "ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。"
paragraph4: "カスタマイズを終了するには、右上の「完了」をクリックします。"
gotit: "Got it!" gotit: "Got it!"
notification: notification:
file-uploaded: "ファイルがアップロードされました" file-uploaded: "ファイルがアップロードされました"
@@ -65,6 +55,7 @@ common:
trash: "ゴミ箱" trash: "ゴミ箱"
drive: "ドライブ" drive: "ドライブ"
messaging: "トーク"
weekday-short: weekday-short:
sunday: "日" sunday: "日"
@@ -129,6 +120,7 @@ common:
always-show-nsfw: "常に閲覧注意のメディアを表示する" always-show-nsfw: "常に閲覧注意のメディアを表示する"
always-mark-nsfw: "常にメディアを閲覧注意として投稿" always-mark-nsfw: "常にメディアを閲覧注意として投稿"
show-full-acct: "ユーザー名のホストを省略しない" show-full-acct: "ユーザー名のホストを省略しない"
show-via: "viaを表示する"
reduce-motion: "UIの動きを減らす" reduce-motion: "UIの動きを減らす"
this-setting-is-this-device-only: "このデバイスのみ" this-setting-is-this-device-only: "このデバイスのみ"
use-os-default-emojis: "OS標準の絵文字を使用" use-os-default-emojis: "OS標準の絵文字を使用"
@@ -147,13 +139,13 @@ common:
drawn: "引き分け" drawn: "引き分け"
my-turn: "あなたのターンです" my-turn: "あなたのターンです"
opponent-turn: "相手のターンです" opponent-turn: "相手のターンです"
turn-of: "{}のターンです" turn-of: "{name}のターンです"
past-turn-of: "{}のターン" past-turn-of: "{name}のターン"
won: "{}の勝ち" won: "{name}の勝ち"
black: "黒" black: "黒"
white: "白" white: "白"
total: "合計" total: "合計"
this-turn: "{}ターン目" this-turn: "{count}ターン目"
widgets: widgets:
analog-clock: "アナログ時計" analog-clock: "アナログ時計"
@@ -173,39 +165,17 @@ common:
users: "おすすめユーザー" users: "おすすめユーザー"
polls: "アンケート" polls: "アンケート"
post-form: "投稿フォーム" post-form: "投稿フォーム"
messaging: "メッセージ"
server: "サーバー情報" server: "サーバー情報"
donation: "寄付のお願い" donation: "寄付のお願い"
nav: "ナビゲーション" nav: "ナビゲーション"
tips: "ヒント" tips: "ヒント"
hashtags: "ハッシュタグ" hashtags: "ハッシュタグ"
deck:
widgets: "ウィジェット"
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
swap-right: "右に移動"
swap-up: "上に移動"
swap-down: "下に移動"
remove: "カラムを削除"
add-column: "カラムを追加"
rename: "名前を変更"
stack-left: "左に重ねる"
pop-right: "右に出す"
dev: "アプリの作成に失敗しました。再度お試しください。" dev: "アプリの作成に失敗しました。再度お試しください。"
ai-chan-kawaii: "藍ちゃかわいい" ai-chan-kawaii: "藍ちゃかわいい"
auth/views/form.vue: auth/views/form.vue:
share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?" share-access: "<i>{{name}}</i>があなたのアカウントにアクセスすることを許可しますか?"
permission-ask: "このアプリは次の権限を要求しています:" permission-ask: "このアプリは次の権限を要求しています:"
account-read: "アカウントの情報を見る。" account-read: "アカウントの情報を見る。"
account-write: "アカウントの情報を操作する。" account-write: "アカウントの情報を操作する。"
@@ -354,7 +324,6 @@ common/views/components/messaging.vue:
common/views/components/messaging-room.vue: common/views/components/messaging-room.vue:
empty: "このユーザーと話したことはありません" empty: "このユーザーと話したことはありません"
more: "もっと読む"
no-history: "これより過去の履歴はありません" no-history: "これより過去の履歴はありません"
resize-form: "ドラッグしてフォームの広さを調整" resize-form: "ドラッグしてフォームの広さを調整"
new-message: "新しいメッセージがあります" new-message: "新しいメッセージがあります"
@@ -604,13 +573,13 @@ desktop/views/components/activity.vue:
toggle: "表示を切り替え" toggle: "表示を切り替え"
desktop/views/components/calendar.vue: desktop/views/components/calendar.vue:
title: "{1}年 {2}月" title: "{year}年 {month}月"
prev: "前の月" prev: "前の月"
next: "次の月" next: "次の月"
go: "クリックして時間遡行" go: "クリックして時間遡行"
desktop/views/components/choose-file-from-drive-window.vue: desktop/views/components/choose-file-from-drive-window.vue:
choose-file: "ファイル選択中" chosen-files: "{count}ファイル選択中"
upload: "PCからドライブにファイルをアップロード" upload: "PCからドライブにファイルをアップロード"
cancel: "キャンセル" cancel: "キャンセル"
ok: "決定" ok: "決定"
@@ -662,7 +631,6 @@ desktop/views/components/drive.folder.vue:
desktop/views/components/drive.vue: desktop/views/components/drive.vue:
search: "検索" search: "検索"
load-more: "もっと読み込む"
empty-draghover: "ドロップですか?いいですよ、ボクはカワイイですからね" empty-draghover: "ドロップですか?いいですよ、ボクはカワイイですからね"
empty-drive: "ドライブには何もありません。" empty-drive: "ドライブには何もありません。"
empty-drive-description: "右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできます。" empty-drive-description: "右クリックして「ファイルをアップロード」を選んだり、ファイルをドラッグ&ドロップすることでもアップロードできます。"
@@ -734,7 +702,6 @@ desktop/views/components/messaging-window.vue:
title: "メッセージ" title: "メッセージ"
desktop/views/components/note-detail.vue: desktop/views/components/note-detail.vue:
more: "会話をもっと読み込む"
private: "この投稿は非公開です" private: "この投稿は非公開です"
deleted: "この投稿は削除されました" deleted: "この投稿は削除されました"
reposted-by: "{}がRenote" reposted-by: "{}がRenote"
@@ -754,10 +721,8 @@ desktop/views/components/note.vue:
desktop/views/components/notes.vue: desktop/views/components/notes.vue:
error: "読み込みに失敗しました。" error: "読み込みに失敗しました。"
retry: "リトライ" retry: "リトライ"
load-more: "もっと読み込む"
desktop/views/components/notifications.vue: desktop/views/components/notifications.vue:
more: "もっと見る"
empty: "ありません!" empty: "ありません!"
desktop/views/components/post-form.vue: desktop/views/components/post-form.vue:
@@ -810,6 +775,10 @@ desktop/views/components/renote-form.vue:
desktop/views/components/renote-form-window.vue: desktop/views/components/renote-form-window.vue:
title: "この投稿をRenoteしますか?" title: "この投稿をRenoteしますか?"
desktop/views/pages/user-following-or-followers.vue:
following: "{user}のフォロー"
followers: "{user}のフォロワー"
desktop/views/components/settings-window.vue: desktop/views/components/settings-window.vue:
settings: "設定" settings: "設定"
@@ -1015,7 +984,6 @@ desktop/views/components/ui.header.account.vue:
desktop/views/components/ui.header.nav.vue: desktop/views/components/ui.header.nav.vue:
home: "ホーム" home: "ホーム"
deck: "デッキ" deck: "デッキ"
messaging: "メッセージ"
game: "ゲーム" game: "ゲーム"
desktop/views/components/ui.header.notifications.vue: desktop/views/components/ui.header.notifications.vue:
@@ -1045,7 +1013,6 @@ desktop/views/components/user-preview.vue:
desktop/views/components/users-list.vue: desktop/views/components/users-list.vue:
all: "すべて" all: "すべて"
iknow: "知り合い" iknow: "知り合い"
load-more: "もっと"
fetching: "読み込んでいます" fetching: "読み込んでいます"
desktop/views/components/users-list-item.vue: desktop/views/components/users-list-item.vue:
@@ -1192,21 +1159,6 @@ admin/views/announcements.vue:
admin/views/hashtags.vue: admin/views/hashtags.vue:
hided-tags: "Hidden Tags" hided-tags: "Hidden Tags"
desktop/views/pages/deck/deck.tl-column.vue:
is-media-only: "メディア投稿のみ"
is-media-view: "メディアビュー"
edit: "オプション"
desktop/views/pages/deck/deck.user-column.vue:
posts: "投稿"
following: "フォロー"
followers: "フォロワー"
images: "画像"
activity: "アクティビティ"
timeline: "タイムライン"
pinned-notes: "ピン留めされた投稿"
push-to-a-list: "リストに追加"
desktop/views/pages/welcome.vue: desktop/views/pages/welcome.vue:
about: "詳しく..." about: "詳しく..."
gotit: "わかった" gotit: "わかった"
@@ -1223,9 +1175,6 @@ desktop/views/pages/welcome.vue:
desktop/views/pages/drive.vue: desktop/views/pages/drive.vue:
title: "Misskey Drive" title: "Misskey Drive"
desktop/views/pages/favorites.vue:
more: "さらに読み込む"
desktop/views/pages/home-customize.vue: desktop/views/pages/home-customize.vue:
title: "ホームのカスタマイズ" title: "ホームのカスタマイズ"
@@ -1241,13 +1190,13 @@ desktop/views/pages/selectdrive.vue:
desktop/views/pages/search.vue: desktop/views/pages/search.vue:
not-available: "検索機能はインスタンスの設定で無効になっています。" not-available: "検索機能はインスタンスの設定で無効になっています。"
not-found: "「{}」に関する投稿は見つかりませんでした。" not-found: "「{q}」に関する投稿は見つかりませんでした。"
desktop/views/pages/share.vue: desktop/views/pages/share.vue:
share-with: "{}で共有" share-with: "{name}で共有"
desktop/views/pages/tag.vue: desktop/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
desktop/views/pages/user-list.users.vue: desktop/views/pages/user-list.users.vue:
users: "ユーザー" users: "ユーザー"
@@ -1264,10 +1213,6 @@ desktop/views/pages/user/user.friends.vue:
loading: "読み込み中" loading: "読み込み中"
no-users: "よく話すユーザーはいません" no-users: "よく話すユーザーはいません"
desktop/views/pages/user/user.vue:
is-suspended: "このユーザーは凍結されています。"
last-used-at: "最終アクセス"
desktop/views/pages/user/user.photos.vue: desktop/views/pages/user/user.photos.vue:
title: "フォト" title: "フォト"
loading: "読み込み中" loading: "読み込み中"
@@ -1338,7 +1283,6 @@ mobile/views/components/drive.vue:
folder-count: "フォルダ" folder-count: "フォルダ"
count-separator: "、" count-separator: "、"
file-count: "ファイル" file-count: "ファイル"
load-more: "もっと読み込む"
nothing-in-drive: "ドライブには何もありません" nothing-in-drive: "ドライブには何もありません"
folder-is-empty: "このフォルダは空です" folder-is-empty: "このフォルダは空です"
prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>" prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>"
@@ -1349,9 +1293,6 @@ mobile/views/components/drive.vue:
url-prompt: "アップロードしたいファイルのURL" url-prompt: "アップロードしたいファイルのURL"
uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。" uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。"
mobile/views/components/drive-file-detail.vue:
rename: "名前を変更"
mobile/views/components/drive-file-chooser.vue: mobile/views/components/drive-file-chooser.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
@@ -1417,12 +1358,7 @@ mobile/views/components/note-sub.vue:
bot: "bot" bot: "bot"
cat: "cat" cat: "cat"
mobile/views/components/notes.vue:
failed: "読み込みに失敗しました。"
retry: "リトライ"
mobile/views/components/notifications.vue: mobile/views/components/notifications.vue:
more: "もっと見る"
empty: "ありません!" empty: "ありません!"
mobile/views/components/post-form.vue: mobile/views/components/post-form.vue:
@@ -1445,7 +1381,6 @@ mobile/views/components/sub-note-content.vue:
mobile/views/components/timeline.vue: mobile/views/components/timeline.vue:
empty: "投稿がありません" empty: "投稿がありません"
load-more: "もっと"
mobile/views/components/ui.header.vue: mobile/views/components/ui.header.vue:
welcome-back: "おかえりなさい、" welcome-back: "おかえりなさい、"
@@ -1454,7 +1389,6 @@ mobile/views/components/ui.header.vue:
mobile/views/components/ui.nav.vue: mobile/views/components/ui.nav.vue:
timeline: "タイムライン" timeline: "タイムライン"
notifications: "通知" notifications: "通知"
messaging: "メッセージ"
follow-requests: "フォロー申請" follow-requests: "フォロー申請"
search: "検索" search: "検索"
favorites: "お気に入り" favorites: "お気に入り"
@@ -1469,12 +1403,10 @@ mobile/views/components/ui.nav.vue:
mobile/views/components/user-timeline.vue: mobile/views/components/user-timeline.vue:
no-notes: "このユーザーは投稿していないようです。" no-notes: "このユーザーは投稿していないようです。"
no-notes-with-media: "メディア付き投稿はありません。" no-notes-with-media: "メディア付き投稿はありません。"
load-more: "もっと"
mobile/views/components/users-list.vue: mobile/views/components/users-list.vue:
all: "すべて" all: "すべて"
known: "知り合い" known: "知り合い"
load-more: "もっと"
mobile/views/pages/favorites.vue: mobile/views/pages/favorites.vue:
title: "お気に入り" title: "お気に入り"
@@ -1483,17 +1415,14 @@ mobile/views/pages/user-lists.vue:
title: "リスト" title: "リスト"
enter-list-name: "リスト名を入力してください" enter-list-name: "リスト名を入力してください"
mobile/views/pages/drive.vue:
more: "もっと見る"
mobile/views/pages/signup.vue: mobile/views/pages/signup.vue:
lets-start: "📦 始めましょう" lets-start: "📦 始めましょう"
mobile/views/pages/followers.vue: mobile/views/pages/followers.vue:
followers-of: "{}のフォロワー" followers-of: "{name}のフォロワー"
mobile/views/pages/following.vue: mobile/views/pages/following.vue:
following-of: "{}のフォロー" following-of: "{name}のフォロー"
mobile/views/pages/home.vue: mobile/views/pages/home.vue:
home: "ホーム" home: "ホーム"
@@ -1504,7 +1433,7 @@ mobile/views/pages/home.vue:
messages: "メッセージ" messages: "メッセージ"
mobile/views/pages/tag.vue: mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
mobile/views/pages/welcome.vue: mobile/views/pages/welcome.vue:
signup: "新規登録" signup: "新規登録"
@@ -1519,13 +1448,7 @@ mobile/views/pages/widgets/activity.vue:
activity: "アクティビティ" activity: "アクティビティ"
mobile/views/pages/share.vue: mobile/views/pages/share.vue:
share-with: "{}で共有" share-with: "{name}で共有"
mobile/views/pages/messaging.vue:
messaging: "メッセージ"
mobile/views/pages/messaging-room.vue:
messaging: "メッセージ"
mobile/views/pages/received-follow-requests.vue: mobile/views/pages/received-follow-requests.vue:
title: "フォロー申請" title: "フォロー申請"
@@ -1546,8 +1469,7 @@ mobile/views/pages/games/reversi.vue:
mobile/views/pages/search.vue: mobile/views/pages/search.vue:
search: "検索" search: "検索"
empty: "「{}」に関する投稿は見つかりませんでした。" not-found: "「{q}」に関する投稿は見つかりませんでした。"
not-found: "「{}」に関する投稿は見つかりませんでした。"
mobile/views/pages/selectdrive.vue: mobile/views/pages/selectdrive.vue:
select-file: "ファイルを選択" select-file: "ファイルを選択"
@@ -1633,21 +1555,53 @@ mobile/views/pages/user/home.vue:
last-used-at: "最終ログイン" last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue: mobile/views/pages/user/home.followers-you-know.vue:
loading: "読み込み中"
no-users: "知り合いのユーザーはいません" no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue: mobile/views/pages/user/home.friends.vue:
loading: "読み込み中"
no-users: "よく会話するユーザーはいません" no-users: "よく会話するユーザーはいません"
mobile/views/pages/user/home.notes.vue: mobile/views/pages/user/home.notes.vue:
loading: "読み込み中"
no-notes: "投稿はありません" no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue: mobile/views/pages/user/home.photos.vue:
loading: "読み込み中"
no-photos: "写真はありません" no-photos: "写真はありません"
deck:
widgets: "ウィジェット"
home: "ホーム"
local: "ローカル"
hybrid: "ソーシャル"
hashtag: "ハッシュタグ"
global: "グローバル"
mentions: "あなた宛て"
direct: "ダイレクト投稿"
notifications: "通知"
list: "リスト"
swap-left: "左に移動"
swap-right: "右に移動"
swap-up: "上に移動"
swap-down: "下に移動"
remove: "カラムを削除"
add-column: "カラムを追加"
rename: "名前を変更"
stack-left: "左に重ねる"
pop-right: "右に出す"
deck/deck.tl-column.vue:
is-media-only: "メディア投稿のみ"
is-media-view: "メディアビュー"
edit: "オプション"
deck/deck.user-column.vue:
posts: "投稿"
following: "フォロー"
followers: "フォロワー"
images: "画像"
activity: "アクティビティ"
timeline: "タイムライン"
pinned-notes: "ピン留めされた投稿"
push-to-a-list: "リストに追加"
docs: docs:
edit-this-page-on-github: "間違いや改善点を見つけましたか?" edit-this-page-on-github: "間違いや改善点を見つけましたか?"
edit-this-page-on-github-link: "このページをGitHubで編集" edit-this-page-on-github-link: "このページをGitHubで編集"
+8 -7
View File
@@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "10.44.2", "version": "10.46.0",
"clientVersion": "1.0.11630", "clientVersion": "1.0.11687",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "main": "./built/index.js",
"private": true, "private": true,
@@ -57,7 +57,7 @@
"@types/koa-send": "4.1.1", "@types/koa-send": "4.1.1",
"@types/koa-views": "2.0.3", "@types/koa-views": "2.0.3",
"@types/koa__cors": "2.2.3", "@types/koa__cors": "2.2.3",
"@types/minio": "7.0.0", "@types/minio": "7.0.1",
"@types/mkdirp": "0.5.2", "@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.5", "@types/mocha": "5.2.5",
"@types/mongodb": "3.1.12", "@types/mongodb": "3.1.12",
@@ -86,8 +86,8 @@
"@types/websocket": "0.0.40", "@types/websocket": "0.0.40",
"@types/ws": "6.0.1", "@types/ws": "6.0.1",
"animejs": "2.2.0", "animejs": "2.2.0",
"apexcharts": "2.1.9", "apexcharts": "2.2.0",
"autobind-decorator": "2.1.0", "autobind-decorator": "2.2.1",
"autosize": "4.0.2", "autosize": "4.0.2",
"autwh": "0.1.0", "autwh": "0.1.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
@@ -197,7 +197,7 @@
"stylus": "0.54.5", "stylus": "0.54.5",
"stylus-loader": "3.0.2", "stylus-loader": "3.0.2",
"summaly": "2.2.0", "summaly": "2.2.0",
"systeminformation": "3.45.9", "systeminformation": "3.47.0",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"tinycolor2": "1.4.1", "tinycolor2": "1.4.1",
@@ -215,12 +215,13 @@
"vue-color": "2.7.0", "vue-color": "2.7.0",
"vue-content-loading": "1.5.3", "vue-content-loading": "1.5.3",
"vue-cropperjs": "2.2.2", "vue-cropperjs": "2.2.2",
"vue-i18n": "8.3.1",
"vue-js-modal": "1.3.26", "vue-js-modal": "1.3.26",
"vue-loader": "15.4.2", "vue-loader": "15.4.2",
"vue-router": "3.0.1", "vue-router": "3.0.1",
"vue-style-loader": "4.1.2", "vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.1", "vue-svg-inline-loader": "1.2.1",
"vue-sweetalert2": "1.5.6", "vue-sweetalert2": "1.5.7",
"vue-template-compiler": "2.5.17", "vue-template-compiler": "2.5.17",
"vuedraggable": "2.16.0", "vuedraggable": "2.16.0",
"vuewordcloud": "18.7.11", "vuewordcloud": "18.7.11",
+14 -12
View File
@@ -1,30 +1,32 @@
<template> <template>
<div class="cdeuzmsthagexbkpofbmatmugjuvogfb"> <div class="cdeuzmsthagexbkpofbmatmugjuvogfb">
<ui-card> <ui-card>
<div slot="title"><fa icon="broadcast-tower"/> %i18n:@announcements%</div> <div slot="title"><fa icon="broadcast-tower"/> {{ $t('announcements') }}</div>
<section v-for="(announcement, i) in announcements" class="fit-top"> <section v-for="(announcement, i) in announcements" class="fit-top">
<ui-input v-model="announcement.title" @change="save"> <ui-input v-model="announcement.title" @change="save">
<span>%i18n:@title%</span> <span>{{ $t('title') }}</span>
</ui-input> </ui-input>
<ui-textarea v-model="announcement.text"> <ui-textarea v-model="announcement.text">
<span>%i18n:@text%</span> <span>{{ $t('text') }}</span>
</ui-textarea> </ui-textarea>
<ui-horizon-group> <ui-horizon-group>
<ui-button @click="save()"><fa :icon="['far', 'save']"/> %i18n:@save%</ui-button> <ui-button @click="save()"><fa :icon="['far', 'save']"/> {{ $t('save') }}</ui-button>
<ui-button @click="remove(i)"><fa :icon="['far', 'trash-alt']"/> %i18n:@remove%</ui-button> <ui-button @click="remove(i)"><fa :icon="['far', 'trash-alt']"/> {{ $t('remove') }}</ui-button>
</ui-horizon-group> </ui-horizon-group>
</section> </section>
<section> <section>
<ui-button @click="add"><fa icon="plus"/> %i18n:@add%</ui-button> <ui-button @click="add"><fa icon="plus"/> {{ $t('add') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/announcements.vue'),
data() { data() {
return { return {
announcements: [], announcements: [],
@@ -32,7 +34,7 @@ export default Vue.extend({
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.announcements = meta.broadcasts; this.announcements = meta.broadcasts;
}); });
}, },
@@ -48,7 +50,7 @@ export default Vue.extend({
remove(i) { remove(i) {
this.$swal({ this.$swal({
type: 'warning', type: 'warning',
text: '%i18n:@_remove.are-you-sure%'.replace('$1', this.announcements.find((_, j) => j == i).title), text: this.$t('_remove.are-you-sure').replace('$1', this.announcements.find((_, j) => j == i).title),
showCancelButton: true showCancelButton: true
}).then(res => { }).then(res => {
if (!res.value) return; if (!res.value) return;
@@ -56,19 +58,19 @@ export default Vue.extend({
this.save(true); this.save(true);
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@_remove.removed%' text: this.$t('_remove.removed')
}); });
}); });
}, },
save(silent) { save(silent) {
(this as any).api('admin/update-meta', { this.$root.api('admin/update-meta', {
broadcasts: this.announcements broadcasts: this.announcements
}).then(() => { }).then(() => {
if (!silent) { if (!silent) {
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@saved%' text: this.$t('saved')
}); });
} }
}).catch(e => { }).catch(e => {
+1 -1
View File
@@ -33,7 +33,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
this.connection = (this as any).os.stream.useSharedConnection('apLog'); this.connection = this.$root.stream.useSharedConnection('apLog');
this.connection.on('log', this.onLog); this.connection.on('log', this.onLog);
this.connection.on('logs', this.onLogs); this.connection.on('logs', this.onLogs);
this.connection.send('requestLog', { this.connection.send('requestLog', {
+38 -32
View File
@@ -1,36 +1,36 @@
<template> <template>
<div class="qvgidhudpqhjttdhxubzuyrhyzgslujw"> <div class="qvgidhudpqhjttdhxubzuyrhyzgslujw">
<header> <header>
<b><fa :icon="['far', 'chart-bar']"/> %i18n:@title%:</b> <b><fa :icon="['far', 'chart-bar']"/> {{ $t('title') }}:</b>
<select v-model="src"> <select v-model="src">
<optgroup label="%i18n:@federation%"> <optgroup :label="$t('federation')">
<option value="federation-instances">%i18n:@charts.federation-instances%</option> <option value="federation-instances">{{ $t('charts.federation-instances') }}</option>
<option value="federation-instances-total">%i18n:@charts.federation-instances-total%</option> <option value="federation-instances-total">{{ $t('charts.federation-instances-total') }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@users%"> <optgroup :label="$t('users')">
<option value="users">%i18n:@charts.users%</option> <option value="users">{{ $t('charts.users') }}</option>
<option value="users-total">%i18n:@charts.users-total%</option> <option value="users-total">{{ $t('charts.users-total') }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@notes%"> <optgroup :label="$t('notes')">
<option value="notes">%i18n:@charts.notes%</option> <option value="notes">{{ $t('charts.notes') }}</option>
<option value="local-notes">%i18n:@charts.local-notes%</option> <option value="local-notes">{{ $t('charts.local-notes') }}</option>
<option value="remote-notes">%i18n:@charts.remote-notes%</option> <option value="remote-notes">{{ $t('charts.remote-notes') }}</option>
<option value="notes-total">%i18n:@charts.notes-total%</option> <option value="notes-total">{{ $t('charts.notes-total') }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@drive%"> <optgroup :label="$t('drive')">
<option value="drive-files">%i18n:@charts.drive-files%</option> <option value="drive-files">{{ $t('charts.drive-files') }}</option>
<option value="drive-files-total">%i18n:@charts.drive-files-total%</option> <option value="drive-files-total">{{ $t('charts.drive-files-total') }}</option>
<option value="drive">%i18n:@charts.drive%</option> <option value="drive">{{ $t('charts.drive') }}</option>
<option value="drive-total">%i18n:@charts.drive-total%</option> <option value="drive-total">{{ $t('charts.drive-total') }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@network%"> <optgroup :label="$t('network')">
<option value="network-requests">%i18n:@charts.network-requests%</option> <option value="network-requests">{{ $t('charts.network-requests') }}</option>
<option value="network-time">%i18n:@charts.network-time%</option> <option value="network-time">{{ $t('charts.network-time') }}</option>
<option value="network-usage">%i18n:@charts.network-usage%</option> <option value="network-usage">{{ $t('charts.network-usage') }}</option>
</optgroup> </optgroup>
</select> </select>
<div> <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> <span @click="span = 'day'" :class="{ active: span == 'day' }">{{ $t('per-day') }}</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">{{ $t('per-hour') }}</span>
</div> </div>
</header> </header>
<div ref="chart"></div> <div ref="chart"></div>
@@ -39,6 +39,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n';
import * as tinycolor from 'tinycolor2'; import * as tinycolor from 'tinycolor2';
import * as ApexCharts from 'apexcharts'; import * as ApexCharts from 'apexcharts';
@@ -48,6 +49,7 @@ const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
const negate = arr => arr.map(x => -x); const negate = arr => arr.map(x => -x);
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/charts.vue'),
data() { data() {
return { return {
chart: null, chart: null,
@@ -103,17 +105,17 @@ export default Vue.extend({
this.now = new Date(); this.now = new Date();
const [perHour, perDay] = await Promise.all([Promise.all([ const [perHour, perDay] = await Promise.all([Promise.all([
(this as any).api('charts/federation', { limit: limit, span: 'hour' }), this.$root.api('charts/federation', { limit: limit, span: 'hour' }),
(this as any).api('charts/users', { limit: limit, span: 'hour' }), this.$root.api('charts/users', { limit: limit, span: 'hour' }),
(this as any).api('charts/notes', { limit: limit, span: 'hour' }), this.$root.api('charts/notes', { limit: limit, span: 'hour' }),
(this as any).api('charts/drive', { limit: limit, span: 'hour' }), this.$root.api('charts/drive', { limit: limit, span: 'hour' }),
(this as any).api('charts/network', { limit: limit, span: 'hour' }) this.$root.api('charts/network', { limit: limit, span: 'hour' })
]), Promise.all([ ]), Promise.all([
(this as any).api('charts/federation', { limit: limit, span: 'day' }), this.$root.api('charts/federation', { limit: limit, span: 'day' }),
(this as any).api('charts/users', { limit: limit, span: 'day' }), this.$root.api('charts/users', { limit: limit, span: 'day' }),
(this as any).api('charts/notes', { limit: limit, span: 'day' }), this.$root.api('charts/notes', { limit: limit, span: 'day' }),
(this as any).api('charts/drive', { limit: limit, span: 'day' }), this.$root.api('charts/drive', { limit: limit, span: 'day' }),
(this as any).api('charts/network', { limit: limit, span: 'day' }) this.$root.api('charts/network', { limit: limit, span: 'day' })
])]); ])]);
const chart = { const chart = {
@@ -138,6 +140,10 @@ export default Vue.extend({
this.render(); this.render();
}, },
beforeDestroy() {
this.chartInstance.destroy();
},
methods: { methods: {
setSrc(src) { setSrc(src) {
this.src = src; this.src = src;
+4 -1
View File
@@ -47,7 +47,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.meta = meta; this.meta = meta;
}); });
@@ -117,6 +117,9 @@ export default Vue.extend({
beforeDestroy() { beforeDestroy() {
this.connection.off('stats', this.onStats); this.connection.off('stats', this.onStats);
this.connection.off('statsLog', this.onStatsLog); this.connection.off('statsLog', this.onStatsLog);
this.cpuChart.destroy();
this.memChart.destroy();
}, },
methods: { methods: {
+15 -13
View File
@@ -5,7 +5,7 @@
<p><b>Machine</b><span>{{ meta.machine }}</span></p> <p><b>Machine</b><span>{{ meta.machine }}</span></p>
<p><b>OS</b><span>{{ meta.os }}</span></p> <p><b>OS</b><span>{{ meta.os }}</span></p>
<p><b>Node</b><span>{{ meta.node }}</span></p> <p><b>Node</b><span>{{ meta.node }}</span></p>
<p>%i18n:common.ai-chan-kawaii%</p> <p>{{ $t('@.ai-chan-kawaii') }}</p>
</header> </header>
<div v-if="stats" class="stats"> <div v-if="stats" class="stats">
@@ -13,12 +13,12 @@
<div> <div>
<div><fa icon="user"/></div> <div><fa icon="user"/></div>
<div> <div>
<span>%i18n:@accounts%</span> <span>{{ $t('accounts') }}</span>
<b class="primary">{{ stats.originalUsersCount | number }}</b> <b class="primary">{{ stats.originalUsersCount | number }}</b>
</div> </div>
</div> </div>
<div> <div>
<span><fa icon="home"/> %i18n:@this-instance%</span> <span><fa icon="home"/> {{ $t('this-instance') }}</span>
<span @click="setChartSrc('users')"><fa :icon="['far', 'chart-bar']"/></span> <span @click="setChartSrc('users')"><fa :icon="['far', 'chart-bar']"/></span>
</div> </div>
</div> </div>
@@ -26,12 +26,12 @@
<div> <div>
<div><fa icon="pencil-alt"/></div> <div><fa icon="pencil-alt"/></div>
<div> <div>
<span>%i18n:@notes%</span> <span>{{ $t('notes') }}</span>
<b class="primary">{{ stats.originalNotesCount | number }}</b> <b class="primary">{{ stats.originalNotesCount | number }}</b>
</div> </div>
</div> </div>
<div> <div>
<span><fa icon="home"/> %i18n:@this-instance%</span> <span><fa icon="home"/> {{ $t('this-instance') }}</span>
<span @click="setChartSrc('notes')"><fa :icon="['far', 'chart-bar']"/></span> <span @click="setChartSrc('notes')"><fa :icon="['far', 'chart-bar']"/></span>
</div> </div>
</div> </div>
@@ -39,12 +39,12 @@
<div> <div>
<div><fa icon="database"/></div> <div><fa icon="database"/></div>
<div> <div>
<span>%i18n:@drive%</span> <span>{{ $t('drive') }}</span>
<b>{{ stats.driveUsageLocal | bytes }}</b> <b>{{ stats.driveUsageLocal | bytes }}</b>
</div> </div>
</div> </div>
<div> <div>
<span><fa icon="home"/> %i18n:@this-instance%</span> <span><fa icon="home"/> {{ $t('this-instance') }}</span>
<span @click="setChartSrc('drive')"><fa :icon="['far', 'chart-bar']"/></span> <span @click="setChartSrc('drive')"><fa :icon="['far', 'chart-bar']"/></span>
</div> </div>
</div> </div>
@@ -52,12 +52,12 @@
<div> <div>
<div><fa :icon="['far', 'hdd']"/></div> <div><fa :icon="['far', 'hdd']"/></div>
<div> <div>
<span>%i18n:@instances%</span> <span>{{ $t('instances') }}</span>
<b>{{ stats.instances | number }}</b> <b>{{ stats.instances | number }}</b>
</div> </div>
</div> </div>
<div> <div>
<span><fa icon="globe"/> %i18n:@federated%</span> <span><fa icon="globe"/> {{ $t('federated') }}</span>
<span @click="setChartSrc('federation-instances-total')"><fa :icon="['far', 'chart-bar']"/></span> <span @click="setChartSrc('federation-instances-total')"><fa :icon="['far', 'chart-bar']"/></span>
</div> </div>
</div> </div>
@@ -78,12 +78,14 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
import XCpuMemory from "./cpu-memory.vue"; import XCpuMemory from "./cpu-memory.vue";
import XCharts from "./charts.vue"; import XCharts from "./charts.vue";
import XApLog from "./ap-log.vue"; import XApLog from "./ap-log.vue";
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/dashboard.vue'),
components: { components: {
XCpuMemory, XCpuMemory,
XCharts, XCharts,
@@ -99,13 +101,13 @@ export default Vue.extend({
}, },
created() { created() {
this.connection = (this as any).os.stream.useSharedConnection('serverStats'); this.connection = this.$root.stream.useSharedConnection('serverStats');
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.meta = meta; this.meta = meta;
}); });
(this as any).api('stats').then(stats => { this.$root.api('stats').then(stats => {
this.stats = stats; this.stats = stats;
}); });
}, },
+25 -23
View File
@@ -1,46 +1,46 @@
<template> <template>
<div class="tumhkfkmgtvzljezfvmgkeurkfncshbe"> <div class="tumhkfkmgtvzljezfvmgkeurkfncshbe">
<ui-card> <ui-card>
<div slot="title"><fa icon="plus"/> %i18n:@add-emoji.title%</div> <div slot="title"><fa icon="plus"/> {{ $t('add-emoji.title') }}</div>
<section class="fit-top"> <section class="fit-top">
<ui-horizon-group inputs> <ui-horizon-group inputs>
<ui-input v-model="name"> <ui-input v-model="name">
<span>%i18n:@add-emoji.name%</span> <span>{{ $t('add-emoji.name') }}</span>
<span slot="desc">%i18n:@add-emoji.name-desc%</span> <span slot="desc">{{ $t('add-emoji.name-desc') }}</span>
</ui-input> </ui-input>
<ui-input v-model="aliases"> <ui-input v-model="aliases">
<span>%i18n:@add-emoji.aliases%</span> <span>{{ $t('add-emoji.aliases') }}</span>
<span slot="desc">%i18n:@add-emoji.aliases-desc%</span> <span slot="desc">{{ $t('add-emoji.aliases-desc') }}</span>
</ui-input> </ui-input>
</ui-horizon-group> </ui-horizon-group>
<ui-input v-model="url"> <ui-input v-model="url">
<i slot="icon"><fa icon="link"/></i> <i slot="icon"><fa icon="link"/></i>
<span>%i18n:@add-emoji.url%</span> <span>{{ $t('add-emoji.url') }}</span>
</ui-input> </ui-input>
<ui-info>%i18n:@add-emoji.info%</ui-info> <ui-info>{{ $t('add-emoji.info') }}</ui-info>
<ui-button @click="add">%i18n:@add-emoji.add%</ui-button> <ui-button @click="add">{{ $t('add-emoji.add') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title"><fa :icon="['far', 'grin']"/> %i18n:@emojis.title%</div> <div slot="title"><fa :icon="['far', 'grin']"/> {{ $t('emojis.title') }}</div>
<section v-for="emoji in emojis"> <section v-for="emoji in emojis">
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/> <img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
<ui-horizon-group inputs> <ui-horizon-group inputs>
<ui-input v-model="emoji.name"> <ui-input v-model="emoji.name">
<span>%i18n:@add-emoji.name%</span> <span>{{ $t('add-emoji.name') }}</span>
</ui-input> </ui-input>
<ui-input v-model="emoji.aliases"> <ui-input v-model="emoji.aliases">
<span>%i18n:@add-emoji.aliases%</span> <span>{{ $t('add-emoji.aliases') }}</span>
</ui-input> </ui-input>
</ui-horizon-group> </ui-horizon-group>
<ui-input v-model="emoji.url"> <ui-input v-model="emoji.url">
<i slot="icon"><fa icon="link"/></i> <i slot="icon"><fa icon="link"/></i>
<span>%i18n:@add-emoji.url%</span> <span>{{ $t('add-emoji.url') }}</span>
</ui-input> </ui-input>
<ui-horizon-group> <ui-horizon-group>
<ui-button @click="updateEmoji(emoji)"><fa :icon="['far', 'save']"/> %i18n:@emojis.update%</ui-button> <ui-button @click="updateEmoji(emoji)"><fa :icon="['far', 'save']"/> {{ $t('emojis.update') }}</ui-button>
<ui-button @click="removeEmoji(emoji)"><fa :icon="['far', 'trash-alt']"/> %i18n:@emojis.remove%</ui-button> <ui-button @click="removeEmoji(emoji)"><fa :icon="['far', 'trash-alt']"/> {{ $t('emojis.remove') }}</ui-button>
</ui-horizon-group> </ui-horizon-group>
</section> </section>
</ui-card> </ui-card>
@@ -48,9 +48,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/emoji.vue'),
data() { data() {
return { return {
name: '', name: '',
@@ -66,14 +68,14 @@ export default Vue.extend({
methods: { methods: {
add() { add() {
(this as any).api('admin/emoji/add', { this.$root.api('admin/emoji/add', {
name: this.name, name: this.name,
url: this.url, url: this.url,
aliases: this.aliases.split(' ').filter(x => x.length > 0) aliases: this.aliases.split(' ').filter(x => x.length > 0)
}).then(() => { }).then(() => {
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@add-emoji.added%' text: this.$t('add-emoji.added')
}); });
this.fetchEmojis(); this.fetchEmojis();
}).catch(e => { }).catch(e => {
@@ -85,7 +87,7 @@ export default Vue.extend({
}, },
fetchEmojis() { fetchEmojis() {
(this as any).api('admin/emoji/list').then(emojis => { this.$root.api('admin/emoji/list').then(emojis => {
emojis.reverse(); emojis.reverse();
emojis.forEach(e => e.aliases = (e.aliases || []).join(' ')); emojis.forEach(e => e.aliases = (e.aliases || []).join(' '));
this.emojis = emojis; this.emojis = emojis;
@@ -93,7 +95,7 @@ export default Vue.extend({
}, },
updateEmoji(emoji) { updateEmoji(emoji) {
(this as any).api('admin/emoji/update', { this.$root.api('admin/emoji/update', {
id: emoji.id, id: emoji.id,
name: emoji.name, name: emoji.name,
url: emoji.url, url: emoji.url,
@@ -101,7 +103,7 @@ export default Vue.extend({
}).then(() => { }).then(() => {
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@updated%' text: this.$t('updated')
}); });
}).catch(e => { }).catch(e => {
this.$swal({ this.$swal({
@@ -114,17 +116,17 @@ export default Vue.extend({
removeEmoji(emoji) { removeEmoji(emoji) {
this.$swal({ this.$swal({
type: 'warning', type: 'warning',
text: '%i18n:@remove-emoji.are-you-sure%'.replace('$1', emoji.name), text: this.$t('remove-emoji.are-you-sure').replace('$1', emoji.name),
showCancelButton: true showCancelButton: true
}).then(res => { }).then(res => {
if (!res.value) return; if (!res.value) return;
(this as any).api('admin/emoji/remove', { this.$root.api('admin/emoji/remove', {
id: emoji.id id: emoji.id
}).then(() => { }).then(() => {
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@remove-emoji.removed%' text: this.$t('remove-emoji.removed')
}); });
this.fetchEmojis(); this.fetchEmojis();
}).catch(e => { }).catch(e => {
+9 -7
View File
@@ -1,37 +1,39 @@
<template> <template>
<div> <div>
<ui-card> <ui-card>
<div slot="title">%i18n:@hided-tags%</div> <div slot="title">{{ $t('hided-tags') }}</div>
<section> <section>
<textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hidedTags"></textarea> <textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hidedTags"></textarea>
<ui-button @click="save">%i18n:@save%</ui-button> <ui-button @click="save">{{ $t('save') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/hashtags.vue'),
data() { data() {
return { return {
hidedTags: '', hidedTags: '',
}; };
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.hidedTags = meta.hidedTags.join('\n'); this.hidedTags = meta.hidedTags.join('\n');
}); });
}, },
methods: { methods: {
save() { save() {
(this as any).api('admin/update-meta', { this.$root.api('admin/update-meta', {
hidedTags: this.hidedTags.split('\n') hidedTags: this.hidedTags.split('\n')
}).then(() => { }).then(() => {
//(this as any).os.apis.dialog({ text: `Saved` }); //this.$root.os.apis.dialog({ text: `Saved` });
}).catch(e => { }).catch(e => {
//(this as any).os.apis.dialog({ text: `Failed ${e}` }); //this.$root.os.apis.dialog({ text: `Failed ${e}` });
}); });
} }
} }
+12 -10
View File
@@ -18,18 +18,18 @@
<p class="name">{{ $store.state.i | userName }}</p> <p class="name">{{ $store.state.i | userName }}</p>
</div> </div>
<ul> <ul>
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>%i18n:@dashboard%</li> <li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>%i18n:@instance%</li> <li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>%i18n:@users%</li> <li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="['far', 'grin']" fixed-width/>%i18n:@emoji%</li> <li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="['far', 'grin']" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>%i18n:@announcements%</li> <li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>%i18n:@hashtags%</li> <li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>%i18n:common.drive%</li> --> <!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li> -->
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> --> <!-- <li @click="nav('update')" :class="{ active: page == 'update' }">{{ $t('update') }}</li> -->
</ul> </ul>
<div class="back-to-misskey"> <div class="back-to-misskey">
<a href="/"><fa icon="arrow-left"/> %i18n:@back-to-misskey%</a> <a href="/"><fa icon="arrow-left"/> {{ $t('back-to-misskey') }}</a>
</div> </div>
<div class="version"> <div class="version">
<small>Misskey {{ version }}</small> <small>Misskey {{ version }}</small>
@@ -49,7 +49,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
import { version } from '../../config'; import { version } from '../../config';
import XDashboard from "./dashboard.vue"; import XDashboard from "./dashboard.vue";
import XInstance from "./instance.vue"; import XInstance from "./instance.vue";
@@ -63,6 +64,7 @@ const ua = navigator.userAgent.toLowerCase();
const isMobile = /mobile|iphone|ipad|android/.test(ua); const isMobile = /mobile|iphone|ipad|android/.test(ua);
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/index.vue'),
components: { components: {
XDashboard, XDashboard,
XInstance, XInstance,
+46 -44
View File
@@ -1,87 +1,89 @@
<template> <template>
<div class="axbwjelsbymowqjyywpirzhdlszoncqs"> <div class="axbwjelsbymowqjyywpirzhdlszoncqs">
<ui-card> <ui-card>
<div slot="title"><fa icon="cog"/> %i18n:@instance%</div> <div slot="title"><fa icon="cog"/> {{ $t('instance') }}</div>
<section class="fit-top fit-bottom"> <section class="fit-top fit-bottom">
<ui-input v-model="name">%i18n:@instance-name%</ui-input> <ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
<ui-textarea v-model="description">%i18n:@instance-description%</ui-textarea> <ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
<ui-input v-model="bannerUrl"><i slot="icon"><fa icon="link"/></i>%i18n:@banner-url%</ui-input> <ui-input v-model="bannerUrl"><i slot="icon"><fa icon="link"/></i>{{ $t('banner-url') }}</ui-input>
<ui-input v-model="languages"><i slot="icon"><fa icon="language"/></i>%i18n:@languages%<span slot="desc">%i18n:@languages-desc%</span></ui-input> <ui-input v-model="languages"><i slot="icon"><fa icon="language"/></i>{{ $t('languages') }}<span slot="desc">{{ $t('languages-desc') }}</span></ui-input>
</section> </section>
<section class="fit-bottom"> <section class="fit-bottom">
<header><fa icon="headset"/> %i18n:@maintainer-config%</header> <header><fa icon="headset"/> {{ $t('maintainer-config') }}</header>
<ui-input v-model="maintainerName">%i18n:@maintainer-name%</ui-input> <ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="['far', 'envelope']"/></i>%i18n:@maintainer-email%</ui-input> <ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="['far', 'envelope']"/></i>{{ $t('maintainer-email') }}</ui-input>
</section> </section>
<section class="fit-top fit-bottom"> <section class="fit-top fit-bottom">
<ui-input v-model="maxNoteTextLength">%i18n:@max-note-text-length%</ui-input> <ui-input v-model="maxNoteTextLength">{{ $t('max-note-text-length') }}</ui-input>
</section> </section>
<section class="fit-bottom"> <section class="fit-bottom">
<header><fa icon="cloud"/> %i18n:@drive-config%</header> <header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
<ui-switch v-model="cacheRemoteFiles">%i18n:@cache-remote-files%<span slot="desc">%i18n:@cache-remote-files-desc%</span></ui-switch> <ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<span slot="desc">{{ $t('cache-remote-files-desc') }}</span></ui-switch>
<ui-input v-model="localDriveCapacityMb">%i18n:@local-drive-capacity-mb%<span slot="suffix">MB</span><span slot="desc">%i18n:@mb%</span></ui-input> <ui-input v-model="localDriveCapacityMb" type="number">{{ $t('local-drive-capacity-mb') }}<span slot="suffix">MB</span><span slot="desc">{{ $t('mb') }}</span></ui-input>
<ui-input v-model="remoteDriveCapacityMb" :disabled="!cacheRemoteFiles">%i18n:@remote-drive-capacity-mb%<span slot="suffix">MB</span><span slot="desc">%i18n:@mb%</span></ui-input> <ui-input v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $t('remote-drive-capacity-mb') }}<span slot="suffix">MB</span><span slot="desc">{{ $t('mb') }}</span></ui-input>
</section> </section>
<section class="fit-bottom"> <section class="fit-bottom">
<header><fa icon="shield-alt"/> %i18n:@recaptcha-config%</header> <header><fa icon="shield-alt"/> {{ $t('recaptcha-config') }}</header>
<ui-switch v-model="enableRecaptcha">%i18n:@enable-recaptcha%</ui-switch> <ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch>
<ui-info>%i18n:@recaptcha-info%</ui-info> <ui-info>{{ $t('recaptcha-info') }}</ui-info>
<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>%i18n:@recaptcha-site-key%</ui-input> <ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>{{ $t('recaptcha-site-key') }}</ui-input>
<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>%i18n:@recaptcha-secret-key%</ui-input> <ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>{{ $t('recaptcha-secret-key') }}</ui-input>
</section> </section>
<section> <section>
<header><fa icon="ghost"/> %i18n:@proxy-account-config%</header> <header><fa icon="ghost"/> {{ $t('proxy-account-config') }}</header>
<ui-info>%i18n:@proxy-account-info%</ui-info> <ui-info>{{ $t('proxy-account-info') }}</ui-info>
<ui-input v-model="proxyAccount"><span slot="prefix">@</span>%i18n:@proxy-account-username%<span slot="desc">%i18n:@proxy-account-username-desc%</span></ui-input> <ui-input v-model="proxyAccount"><span slot="prefix">@</span>{{ $t('proxy-account-username') }}<span slot="desc">{{ $t('proxy-account-username-desc') }}</span></ui-input>
<ui-info warn>%i18n:@proxy-account-warn%</ui-info> <ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
</section> </section>
<section> <section>
<ui-switch v-model="disableRegistration">%i18n:@disable-registration%</ui-switch> <ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
</section> </section>
<section> <section>
<ui-switch v-model="disableLocalTimeline">%i18n:@disable-local-timeline%</ui-switch> <ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
</section> </section>
<section> <section>
<ui-button @click="updateMeta">%i18n:@save%</ui-button> <ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title">%i18n:@invite%</div> <div slot="title">{{ $t('invite') }}</div>
<section> <section>
<ui-button @click="invite">%i18n:@invite%</ui-button> <ui-button @click="invite">{{ $t('invite') }}</ui-button>
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p> <p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title"><fa :icon="['fab', 'twitter']"/> %i18n:@twitter-integration-config%</div> <div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</div>
<section> <section>
<ui-switch v-model="enableTwitterIntegration">%i18n:@enable-twitter-integration%</ui-switch> <ui-switch v-model="enableTwitterIntegration">{{ $t('enable-twitter-integration') }}</ui-switch>
<ui-info>%i18n:@twitter-integration-info%</ui-info> <ui-info>{{ $t('twitter-integration-info') }}</ui-info>
<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@twitter-integration-consumer-key%</ui-input> <ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('twitter-integration-consumer-key') }}</ui-input>
<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@twitter-integration-consumer-secret%</ui-input> <ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('twitter-integration-consumer-secret') }}</ui-input>
<ui-button @click="updateMeta">%i18n:@save%</ui-button> <ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title"><fa :icon="['fab', 'github']"/> %i18n:@github-integration-config%</div> <div slot="title"><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</div>
<section> <section>
<ui-switch v-model="enableGithubIntegration">%i18n:@enable-github-integration%</ui-switch> <ui-switch v-model="enableGithubIntegration">{{ $t('enable-github-integration') }}</ui-switch>
<ui-info>%i18n:@github-integration-info%</ui-info> <ui-info>{{ $t('github-integration-info') }}</ui-info>
<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@github-integration-client-id%</ui-input> <ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('github-integration-client-id') }}</ui-input>
<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@github-integration-client-secret%</ui-input> <ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('github-integration-client-secret') }}</ui-input>
<ui-button @click="updateMeta">%i18n:@save%</ui-button> <ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/instance.vue'),
data() { data() {
return { return {
maintainerName: null, maintainerName: null,
@@ -111,7 +113,7 @@ export default Vue.extend({
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.maintainerName = meta.maintainer.name; this.maintainerName = meta.maintainer.name;
this.maintainerEmail = meta.maintainer.email; this.maintainerEmail = meta.maintainer.email;
this.bannerUrl = meta.bannerUrl; this.bannerUrl = meta.bannerUrl;
@@ -137,7 +139,7 @@ export default Vue.extend({
methods: { methods: {
invite() { invite() {
(this as any).api('admin/invite').then(x => { this.$root.api('admin/invite').then(x => {
this.inviteCode = x.code; this.inviteCode = x.code;
}).catch(e => { }).catch(e => {
this.$swal({ this.$swal({
@@ -148,7 +150,7 @@ export default Vue.extend({
}, },
updateMeta() { updateMeta() {
(this as any).api('admin/update-meta', { this.$root.api('admin/update-meta', {
maintainerName: this.maintainerName, maintainerName: this.maintainerName,
maintainerEmail: this.maintainerEmail, maintainerEmail: this.maintainerEmail,
disableRegistration: this.disableRegistration, disableRegistration: this.disableRegistration,
@@ -174,7 +176,7 @@ export default Vue.extend({
}).then(() => { }).then(() => {
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@saved%' text: this.$t('saved')
}); });
}).catch(e => { }).catch(e => {
this.$swal({ this.$swal({
+27 -25
View File
@@ -1,52 +1,54 @@
<template> <template>
<div class="ucnffhbtogqgscfmqcymwmmupoknpfsw"> <div class="ucnffhbtogqgscfmqcymwmmupoknpfsw">
<ui-card> <ui-card>
<div slot="title">%i18n:@verify-user%</div> <div slot="title">{{ $t('verify-user') }}</div>
<section class="fit-top"> <section class="fit-top">
<ui-input v-model="verifyUsername" type="text"> <ui-input v-model="verifyUsername" type="text">
<span slot="prefix">@</span> <span slot="prefix">@</span>
</ui-input> </ui-input>
<ui-button @click="verifyUser" :disabled="verifying">%i18n:@verify%</ui-button> <ui-button @click="verifyUser" :disabled="verifying">{{ $t('verify') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title">%i18n:@unverify-user%</div> <div slot="title">{{ $t('unverify-user') }}</div>
<section class="fit-top"> <section class="fit-top">
<ui-input v-model="unverifyUsername" type="text"> <ui-input v-model="unverifyUsername" type="text">
<span slot="prefix">@</span> <span slot="prefix">@</span>
</ui-input> </ui-input>
<ui-button @click="unverifyUser" :disabled="unverifying">%i18n:@unverify%</ui-button> <ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title">%i18n:@suspend-user%</div> <div slot="title">{{ $t('suspend-user') }}</div>
<section class="fit-top"> <section class="fit-top">
<ui-input v-model="suspendUsername" type="text"> <ui-input v-model="suspendUsername" type="text">
<span slot="prefix">@</span> <span slot="prefix">@</span>
</ui-input> </ui-input>
<ui-button @click="suspendUser" :disabled="suspending">%i18n:@suspend%</ui-button> <ui-button @click="suspendUser" :disabled="suspending">{{ $t('suspend') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
<ui-card> <ui-card>
<div slot="title">%i18n:@unsuspend-user%</div> <div slot="title">{{ $t('unsuspend-user') }}</div>
<section class="fit-top"> <section class="fit-top">
<ui-input v-model="unsuspendUsername" type="text"> <ui-input v-model="unsuspendUsername" type="text">
<span slot="prefix">@</span> <span slot="prefix">@</span>
</ui-input> </ui-input>
<ui-button @click="unsuspendUser" :disabled="unsuspending">%i18n:@unsuspend%</ui-button> <ui-button @click="unsuspendUser" :disabled="unsuspending">{{ $t('unsuspend') }}</ui-button>
</section> </section>
</ui-card> </ui-card>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from 'vue';
import i18n from '../../i18n';
import parseAcct from "../../../../misc/acct/parse"; import parseAcct from "../../../../misc/acct/parse";
export default Vue.extend({ export default Vue.extend({
i18n: i18n('admin/views/users.vue'),
data() { data() {
return { return {
verifyUsername: null, verifyUsername: null,
@@ -65,13 +67,13 @@ export default Vue.extend({
this.verifying = true; this.verifying = true;
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername)); const user = await this.$root.os.api('users/show', parseAcct(this.verifyUsername));
await (this as any).os.api('admin/verify-user', { userId: user.id }); await this.$root.os.api('admin/verify-user', { userId: user.id });
//(this as any).os.apis.dialog({ text: '%i18n:@verified%' }); //this.$root.os.apis.dialog({ text: this.$t('verified') });
}; };
await process().catch(e => { await process().catch(e => {
//(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //this.$root.os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.verifying = false; this.verifying = false;
@@ -81,13 +83,13 @@ export default Vue.extend({
this.unverifying = true; this.unverifying = true;
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername)); const user = await this.$root.os.api('users/show', parseAcct(this.unverifyUsername));
await (this as any).os.api('admin/unverify-user', { userId: user.id }); await this.$root.os.api('admin/unverify-user', { userId: user.id });
//(this as any).os.apis.dialog({ text: '%i18n:@unverified%' }); //this.$root.os.apis.dialog({ text: this.$t('unverified') });
}; };
await process().catch(e => { await process().catch(e => {
//(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //this.$root.os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.unverifying = false; this.unverifying = false;
@@ -97,13 +99,13 @@ export default Vue.extend({
this.suspending = true; this.suspending = true;
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername)); const user = await this.$root.os.api('users/show', parseAcct(this.suspendUsername));
await (this as any).os.api('admin/suspend-user', { userId: user.id }); await this.$root.os.api('admin/suspend-user', { userId: user.id });
//(this as any).os.apis.dialog({ text: '%i18n:@suspended%' }); //this.$root.os.apis.dialog({ text: this.$t('suspended') });
}; };
await process().catch(e => { await process().catch(e => {
//(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //this.$root.os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.suspending = false; this.suspending = false;
@@ -113,13 +115,13 @@ export default Vue.extend({
this.unsuspending = true; this.unsuspending = true;
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername)); const user = await this.$root.os.api('users/show', parseAcct(this.unsuspendUsername));
await (this as any).os.api('admin/unsuspend-user', { userId: user.id }); await this.$root.os.api('admin/unsuspend-user', { userId: user.id });
//(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' }); //this.$root.os.apis.dialog({ text: this.$t('unsuspended') });
}; };
await process().catch(e => { await process().catch(e => {
//(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //this.$root.os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.unsuspending = false; this.unsuspending = false;
-7
View File
@@ -13,13 +13,6 @@ html
body body
overflow-wrap break-word overflow-wrap break-word
#error
padding 32px
color #fff
hr
border solid 1px #fff
#nprogress #nprogress
pointer-events none pointer-events none
+18 -15
View File
@@ -1,7 +1,7 @@
<template> <template>
<div class="form"> <div class="form">
<header> <header>
<h1>%i18n:@share-access%</h1> <h1 v-html="$t('share-access', { name: app.name })"></h1>
<img :src="app.iconUrl"/> <img :src="app.iconUrl"/>
</header> </header>
<div class="app"> <div class="app">
@@ -11,32 +11,35 @@
<p class="description">{{ app.description }}</p> <p class="description">{{ app.description }}</p>
</section> </section>
<section> <section>
<h2>%i18n:@permission-ask%</h2> <h2>{{ $t('permission-ask') }}</h2>
<ul> <ul>
<template v-for="p in app.permission"> <template v-for="p in app.permission">
<li v-if="p == 'account-read'">%i18n:@account-read%</li> <li v-if="p == 'account-read'">{{ $t('account-read') }}</li>
<li v-if="p == 'account-write'">%i18n:@account-write%</li> <li v-if="p == 'account-write'">{{ $t('account-write') }}</li>
<li v-if="p == 'note-write'">%i18n:@note-write%</li> <li v-if="p == 'note-write'">{{ $t('note-write') }}</li>
<li v-if="p == 'like-write'">%i18n:@like-write%</li> <li v-if="p == 'like-write'">{{ $t('like-write') }}</li>
<li v-if="p == 'following-write'">%i18n:@following-write%</li> <li v-if="p == 'following-write'">{{ $t('following-write') }}</li>
<li v-if="p == 'drive-read'">%i18n:@drive-read%</li> <li v-if="p == 'drive-read'">{{ $t('drive-read') }}</li>
<li v-if="p == 'drive-write'">%i18n:@drive-write%</li> <li v-if="p == 'drive-write'">{{ $t('drive-write') }}</li>
<li v-if="p == 'notification-read'">%i18n:@notification-read%</li> <li v-if="p == 'notification-read'">{{ $t('notification-read') }}</li>
<li v-if="p == 'notification-write'">%i18n:@notification-write%</li> <li v-if="p == 'notification-write'">{{ $t('notification-write') }}</li>
</template> </template>
</ul> </ul>
</section> </section>
</div> </div>
<div class="action"> <div class="action">
<button @click="cancel">%i18n:@cancel%</button> <button @click="cancel">{{ $t('cancel') }}</button>
<button @click="accept">%i18n:@accept%</button> <button @click="accept">{{ $t('accept') }}</button>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('auth/views/form.vue'),
props: ['session'], props: ['session'],
computed: { computed: {
app(): any { app(): any {
@@ -45,7 +48,7 @@ export default Vue.extend({
}, },
methods: { methods: {
cancel() { cancel() {
(this as any).api('auth/deny', { this.$root.api('auth/deny', {
token: this.session.token token: this.session.token
}).then(() => { }).then(() => {
this.$emit('denied'); this.$emit('denied');
@@ -53,7 +56,7 @@ export default Vue.extend({
}, },
accept() { accept() {
(this as any).api('auth/accept', { this.$root.api('auth/accept', {
token: this.session.token token: this.session.token
}).then(() => { }).then(() => {
this.$emit('accepted'); this.$emit('accepted');
+12 -10
View File
@@ -1,7 +1,7 @@
<template> <template>
<div class="index"> <div class="index">
<main v-if="$store.getters.isSignedIn"> <main v-if="$store.getters.isSignedIn">
<p class="fetching" v-if="fetching">%i18n:@loading%<mk-ellipsis/></p> <p class="fetching" v-if="fetching">{{ $t('loading') }}<mk-ellipsis/></p>
<x-form <x-form
class="form" class="form"
ref="form" ref="form"
@@ -11,20 +11,20 @@
@accepted="accepted" @accepted="accepted"
/> />
<div class="denied" v-if="state == 'denied'"> <div class="denied" v-if="state == 'denied'">
<h1>%i18n:@denied%</h1> <h1>{{ $t('denied') }}</h1>
<p>%i18n:@denied-paragraph%</p> <p>{{ $t('denied-paragraph') }}</p>
</div> </div>
<div class="accepted" v-if="state == 'accepted'"> <div class="accepted" v-if="state == 'accepted'">
<h1>{{ session.app.isAuthorized ? '%i18n:@already-authorized%' : '%i18n:@allowed%' }}</h1> <h1>{{ session.app.isAuthorized ? this.$t('already-authorized') : this.$t('allowed') }}</h1>
<p v-if="session.app.callbackUrl">%i18n:@callback-url%<mk-ellipsis/></p> <p v-if="session.app.callbackUrl">{{ $t('callback-url') }}<mk-ellipsis/></p>
<p v-if="!session.app.callbackUrl">%i18n:@please-go-back%</p> <p v-if="!session.app.callbackUrl">{{ $t('please-go-back') }}</p>
</div> </div>
<div class="error" v-if="state == 'fetch-session-error'"> <div class="error" v-if="state == 'fetch-session-error'">
<p>%i18n:@error%</p> <p>{{ $t('error') }}</p>
</div> </div>
</main> </main>
<main class="signin" v-if="!$store.getters.isSignedIn"> <main class="signin" v-if="!$store.getters.isSignedIn">
<h1>%i18n:@sign-in%</h1> <h1>{{ $t('sign-in') }}</h1>
<mk-signin/> <mk-signin/>
</main> </main>
<footer><img src="/assets/auth/icon.svg" alt="Misskey"/></footer> <footer><img src="/assets/auth/icon.svg" alt="Misskey"/></footer>
@@ -33,9 +33,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../i18n';
import XForm from './form.vue'; import XForm from './form.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('auth/views/index.vue'),
components: { components: {
XForm XForm
}, },
@@ -55,7 +57,7 @@ export default Vue.extend({
if (!this.$store.getters.isSignedIn) return; if (!this.$store.getters.isSignedIn) return;
// Fetch session // Fetch session
(this as any).api('auth/session/show', { this.$root.api('auth/session/show', {
token: this.token token: this.token
}).then(session => { }).then(session => {
this.session = session; this.session = session;
@@ -63,7 +65,7 @@ export default Vue.extend({
// 既に連携していた場合 // 既に連携していた場合
if (this.session.app.isAuthorized) { if (this.session.app.isAuthorized) {
(this as any).api('auth/accept', { this.$root.api('auth/accept', {
token: this.session.token token: this.session.token
}).then(() => { }).then(() => {
this.accepted(); this.accepted();
@@ -1,8 +1,7 @@
import MiOS from '../../mios';
import { clientVersion as current } from '../../config'; import { clientVersion as current } from '../../config';
export default async function(mios: MiOS, force = false, silent = false) { export default async function($root: any, force = false, silent = false) {
const meta = await mios.getMeta(force); const meta = await $root.getMeta(force);
const newer = meta.clientVersion; const newer = meta.clientVersion;
if (newer != current) { if (newer != current) {
@@ -23,7 +22,7 @@ export default async function(mios: MiOS, force = false, silent = false) {
} }
if (!silent) { if (!silent) {
mios.apis.dialog({ $root.$dialog({
title: '%i18n:common.update-available-title%', title: '%i18n:common.update-available-title%',
text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current) text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)
}); });
@@ -1,10 +1,10 @@
declare const fuckAdBlock: any; declare const fuckAdBlock: any;
export default (os) => { export default ($root: any) => {
require('fuckadblock'); require('fuckadblock');
function adBlockDetected() { function adBlockDetected() {
os.apis.dialog({ $root.$dialog({
title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%', title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%',
text: '%i18n:common.adblock.warning%', text: '%i18n:common.adblock.warning%',
actins: [{ actins: [{
+8 -8
View File
@@ -88,7 +88,7 @@ export default (opts: Opts = {}) => ({
methods: { methods: {
reply(viaKeyboard = false) { reply(viaKeyboard = false) {
(this as any).apis.post({ this.$root.$post({
reply: this.appearNote, reply: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
cb: () => { cb: () => {
@@ -98,7 +98,7 @@ export default (opts: Opts = {}) => ({
}, },
renote(viaKeyboard = false) { renote(viaKeyboard = false) {
(this as any).apis.post({ this.$root.$post({
renote: this.appearNote, renote: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
cb: () => { cb: () => {
@@ -115,7 +115,7 @@ export default (opts: Opts = {}) => ({
react(viaKeyboard = false) { react(viaKeyboard = false) {
this.blur(); this.blur();
(this as any).os.new(MkReactionPicker, { this.$root.new(MkReactionPicker, {
source: this.$refs.reactButton, source: this.$refs.reactButton,
note: this.appearNote, note: this.appearNote,
showFocus: viaKeyboard, showFocus: viaKeyboard,
@@ -126,28 +126,28 @@ export default (opts: Opts = {}) => ({
}, },
reactDirectly(reaction) { reactDirectly(reaction) {
(this as any).api('notes/reactions/create', { (this.$root.api('notes/reactions/create', {
noteId: this.appearNote.id, noteId: this.appearNote.id,
reaction: reaction reaction: reaction
}); });
}, },
favorite() { favorite() {
(this as any).api('notes/favorites/create', { this.$root.api('notes/favorites/create', {
noteId: this.appearNote.id noteId: this.appearNote.id
}).then(() => { }).then(() => {
(this as any).os.new(Ok); this.$root.new(Ok);
}); });
}, },
del() { del() {
(this as any).api('notes/delete', { this.$root.api('notes/delete', {
noteId: this.appearNote.id noteId: this.appearNote.id
}); });
}, },
menu(viaKeyboard = false) { menu(viaKeyboard = false) {
(this as any).os.new(MkNoteMenu, { this.$root.new(MkNoteMenu, {
source: this.$refs.menuButton, source: this.$refs.menuButton,
note: this.appearNote, note: this.appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
@@ -26,7 +26,7 @@ export default prop => ({
created() { created() {
if (this.$store.getters.isSignedIn) { if (this.$store.getters.isSignedIn) {
this.connection = (this as any).os.stream; this.connection = this.$root.stream;
} }
}, },
@@ -4,29 +4,29 @@
<section class="fit-top"> <section class="fit-top">
<ui-input :value="$store.state.i.token" readonly> <ui-input :value="$store.state.i.token" readonly>
<span>%i18n:@token%</span> <span>{{ $t('token') }}</span>
</ui-input> </ui-input>
<p>%i18n:@intro%</p> <p>{{ $t('intro') }}</p>
<ui-info warn>%i18n:@caution%</ui-info> <ui-info warn>{{ $t('caution') }}</ui-info>
<p>%i18n:@regeneration-of-token%</p> <p>{{ $t('regeneration-of-token') }}</p>
<ui-button @click="regenerateToken"><fa icon="sync-alt"/> %i18n:@regenerate-token%</ui-button> <ui-button @click="regenerateToken"><fa icon="sync-alt"/> {{ $t('regenerate-token') }}</ui-button>
</section> </section>
<section> <section>
<header><fa icon="terminal"/> %i18n:@console.title%</header> <header><fa icon="terminal"/> {{ $t('console.title') }}</header>
<ui-input v-model="endpoint"> <ui-input v-model="endpoint">
<span>%i18n:@console.endpoint%</span> <span>{{ $t('console.endpoint') }}</span>
</ui-input> </ui-input>
<ui-textarea v-model="body"> <ui-textarea v-model="body">
<span>%i18n:@console.parameter% (JSON or JSON5)</span> <span>{{ $t('console.parameter') }} (JSON or JSON5)</span>
<span slot="desc">%i18n:@console.credential-info%</span> <span slot="desc">{{ $t('console.credential-info') }}</span>
</ui-textarea> </ui-textarea>
<ui-button @click="send" :disabled="sending"> <ui-button @click="send" :disabled="sending">
<template v-if="sending">%i18n:@console.sending%</template> <template v-if="sending">{{ $t('console.sending') }}</template>
<template v-else><fa icon="paper-plane"/> %i18n:@console.send%</template> <template v-else><fa icon="paper-plane"/> {{ $t('console.send') }}</template>
</ui-button> </ui-button>
<ui-textarea v-if="res" v-model="res" readonly tall> <ui-textarea v-if="res" v-model="res" readonly tall>
<span>%i18n:@console.response%</span> <span>{{ $t('console.response') }}</span>
</ui-textarea> </ui-textarea>
</section> </section>
</ui-card> </ui-card>
@@ -34,9 +34,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as JSON5 from 'json5'; import * as JSON5 from 'json5';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/api-settings.vue'),
data() { data() {
return { return {
endpoint: '', endpoint: '',
@@ -48,11 +50,11 @@ export default Vue.extend({
methods: { methods: {
regenerateToken() { regenerateToken() {
(this as any).apis.input({ this.$input({
title: '%i18n:@enter-password%', title: this.$t('enter-password'),
type: 'password' type: 'password'
}).then(password => { }).then(password => {
(this as any).api('i/regenerate_token', { this.$root.api('i/regenerate_token', {
password: password password: password
}); });
}); });
@@ -60,7 +62,7 @@ export default Vue.extend({
send() { send() {
this.sending = true; this.sending = true;
(this as any).api(this.endpoint, JSON5.parse(this.body)).then(res => { this.$root.api(this.endpoint, JSON5.parse(this.body)).then(res => {
this.sending = false; this.sending = false;
this.res = JSON5.stringify(res, null, 2); this.res = JSON5.stringify(res, null, 2);
}, err => { }, err => {
@@ -114,7 +114,7 @@ export default Vue.extend({
mounted() { mounted() {
//#region Construct Emoji DB //#region Construct Emoji DB
const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis || []; const customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
const emojiDefinitions: EmojiDef[] = []; const emojiDefinitions: EmojiDef[] = [];
customEmojis.forEach(x => { customEmojis.forEach(x => {
@@ -185,7 +185,7 @@ export default Vue.extend({
this.users = users; this.users = users;
this.fetching = false; this.fetching = false;
} else { } else {
(this as any).api('users/search', { this.$root.api('users/search', {
query: this.q, query: this.q,
limit: 30 limit: 30
}).then(users => { }).then(users => {
@@ -208,7 +208,7 @@ export default Vue.extend({
this.hashtags = hashtags; this.hashtags = hashtags;
this.fetching = false; this.fetching = false;
} else { } else {
(this as any).api('hashtags/search', { this.$root.api('hashtags/search', {
query: this.q, query: this.q,
limit: 30 limit: 30
}).then(hashtags => { }).then(hashtags => {
@@ -15,6 +15,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
export default Vue.extend({ export default Vue.extend({
props: { props: {
user: { user: {
@@ -1,47 +1,49 @@
<template> <template>
<div class="troubleshooter"> <div class="troubleshooter">
<div class="body"> <div class="body">
<h1><fa icon="wrench"/>%i18n:@title%</h1> <h1><fa icon="wrench"/>{{ $t('title') }}</h1>
<div> <div>
<p :data-wip="network == null"> <p :data-wip="network == null">
<template v-if="network != null"> <template v-if="network != null">
<template v-if="network"><fa icon="check"/></template> <template v-if="network"><fa icon="check"/></template>
<template v-if="!network"><fa icon="times"/></template> <template v-if="!network"><fa icon="times"/></template>
</template> </template>
{{ network == null ? '%i18n:@checking-network%' : '%i18n:@network%' }}<mk-ellipsis v-if="network == null"/> {{ network == null ? this.$t('checking-network') : this.$t('network') }}<mk-ellipsis v-if="network == null"/>
</p> </p>
<p v-if="network == true" :data-wip="internet == null"> <p v-if="network == true" :data-wip="internet == null">
<template v-if="internet != null"> <template v-if="internet != null">
<template v-if="internet"><fa icon="check"/></template> <template v-if="internet"><fa icon="check"/></template>
<template v-if="!internet"><fa icon="times"/></template> <template v-if="!internet"><fa icon="times"/></template>
</template> </template>
{{ internet == null ? '%i18n:@checking-internet%' : '%i18n:@internet%' }}<mk-ellipsis v-if="internet == null"/> {{ internet == null ? this.$t('checking-internet') : this.$t('internet') }}<mk-ellipsis v-if="internet == null"/>
</p> </p>
<p v-if="internet == true" :data-wip="server == null"> <p v-if="internet == true" :data-wip="server == null">
<template v-if="server != null"> <template v-if="server != null">
<template v-if="server"><fa icon="check"/></template> <template v-if="server"><fa icon="check"/></template>
<template v-if="!server"><fa icon="times"/></template> <template v-if="!server"><fa icon="times"/></template>
</template> </template>
{{ server == null ? '%i18n:@checking-server%' : '%i18n:@server%' }}<mk-ellipsis v-if="server == null"/> {{ server == null ? this.$t('checking-server') : this.$t('server') }}<mk-ellipsis v-if="server == null"/>
</p> </p>
</div> </div>
<p v-if="!end">%i18n:@finding%<mk-ellipsis/></p> <p v-if="!end">{{ $t('finding') }}<mk-ellipsis/></p>
<p v-if="network === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p> <p v-if="network === false"><b><fa icon="exclamation-triangle"/>{{ $t('no-network') }}</b><br>{{ $t('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="internet === false"><b><fa icon="exclamation-triangle"/>{{ $t('no-internet') }}</b><br>{{ $t('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 === false"><b><fa icon="exclamation-triangle"/>{{ $t('no-server') }}</b><br>{{ $t('no-server-desc') }}</p>
<p v-if="server === true" class="success"><b><fa icon="info-circle"/>%i18n:@success%</b><br>%i18n:@success-desc%</p> <p v-if="server === true" class="success"><b><fa icon="info-circle"/>{{ $t('success') }}</b><br>{{ $t('success-desc') }}</p>
</div> </div>
<footer> <footer>
<a href="/assets/flush.html">%i18n:@flush%</a> | <a href="/assets/version.html">%i18n:@set-version%</a> <a href="/assets/flush.html">{{ $t('flush') }}</a> | <a href="/assets/version.html">{{ $t('set-version') }}</a>
</footer> </footer>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl } from '../../../config'; import { apiUrl } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/connect-failed.troubleshooter.vue'),
data() { data() {
return { return {
network: navigator.onLine, network: navigator.onLine,
@@ -1,23 +1,25 @@
<template> <template>
<div class="mk-connect-failed"> <div class="mk-connect-failed">
<img src="https://raw.githubusercontent.com/syuilo/misskey/develop/src/client/assets/error.jpg" alt=""/> <img src="https://raw.githubusercontent.com/syuilo/misskey/develop/src/client/assets/error.jpg" alt=""/>
<h1>%i18n:@title%</h1> <h1>{{ $t('title') }}</h1>
<p class="text"> <p class="text">
<span>{{ '%i18n:@description%'.substr(0, '%i18n:@description%'.indexOf('{')) }}</span> <span>{{ this.$t('description').substr(0, this.$t('description').indexOf('{')) }}</span>
<a @click="reload">{{ '%i18n:@description%'.match(/\{(.+?)\}/)[1] }}</a> <a @click="reload">{{ this.$t('description').match(/\{(.+?)\}/)[1] }}</a>
<span>{{ '%i18n:@description%'.substr('%i18n:@description%'.indexOf('}') + 1) }}</span> <span>{{ this.$t('description').substr(this.$t('description').indexOf('}') + 1) }}</span>
</p> </p>
<button v-if="!troubleshooting" @click="troubleshooting = true">%i18n:@troubleshoot%</button> <button v-if="!troubleshooting" @click="troubleshooting = true">{{ $t('troubleshoot') }}</button>
<x-troubleshooter v-if="troubleshooting"/> <x-troubleshooter v-if="troubleshooting"/>
<p class="thanks">%i18n:@thanks%</p> <p class="thanks">{{ $t('thanks') }}</p>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import XTroubleshooter from './connect-failed.troubleshooter.vue'; import XTroubleshooter from './connect-failed.troubleshooter.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/connect-failed.vue'),
components: { components: {
XTroubleshooter XTroubleshooter
}, },
@@ -1,11 +1,13 @@
<template> <template>
<button class="nrvgflfuaxwgkxoynpnumyookecqrrvh" @click="toggle">{{ value ? '%i18n:@hide%' : '%i18n:@show%' }}</button> <button class="nrvgflfuaxwgkxoynpnumyookecqrrvh" @click="toggle">{{ value ? this.$t('hide') : this.$t('show') }}</button>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/cw-button.vue'),
props: { props: {
value: { value: {
type: Boolean, type: Boolean,
@@ -1,25 +1,27 @@
<template> <template>
<ui-card> <ui-card>
<div slot="title"><fa icon="cloud"/> %i18n:common.drive%</div> <div slot="title"><fa icon="cloud"/> {{ $t('@.drive') }}</div>
<section v-if="!fetching" class="juakhbxthdewydyreaphkepoxgxvfogn"> <section v-if="!fetching" class="juakhbxthdewydyreaphkepoxgxvfogn">
<div class="meter"><div :style="meterStyle"></div></div> <div class="meter"><div :style="meterStyle"></div></div>
<p>%i18n:@max%: <b>{{ capacity | bytes }}</b> %i18n:@in-use%: <b>{{ usage | bytes }}</b></p> <p>{{ $t('max') }}: <b>{{ capacity | bytes }}</b> {{ $t('in-use') }}: <b>{{ usage | bytes }}</b></p>
</section> </section>
<section> <section>
<header>%i18n:@stats%</header> <header>{{ $t('stats') }}</header>
<div ref="chart" style="margin-bottom: -16px; color: #000;"></div> <div ref="chart" style="margin-bottom: -16px; margin-left: -8px; color: #000;"></div>
</section> </section>
</ui-card> </ui-card>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as tinycolor from 'tinycolor2'; import * as tinycolor from 'tinycolor2';
import * as ApexCharts from 'apexcharts'; import * as ApexCharts from 'apexcharts';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/drive-settings.vue'),
data() { data() {
return { return {
fetching: true, fetching: true,
@@ -42,7 +44,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
(this as any).api('drive').then(info => { this.$root.api('drive').then(info => {
this.capacity = info.capacity; this.capacity = info.capacity;
this.usage = info.usage; this.usage = info.usage;
this.fetching = false; this.fetching = false;
@@ -55,7 +57,7 @@ export default Vue.extend({
methods: { methods: {
renderChart() { renderChart() {
(this as any).api('charts/user/drive', { this.$root.api('charts/user/drive', {
userId: this.$store.state.i.id, userId: this.$store.state.i.id,
span: 'day', span: 'day',
limit: 21 limit: 21
@@ -1,10 +1,19 @@
<template> <template>
<div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj"> <div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj">
<p><fa icon="exclamation-triangle"/> %i18n:common.error.title%</p> <p><fa icon="exclamation-triangle"/> {{ $t('@.error.title') }}</p>
<ui-button @click="() => $emit('retry')">%i18n:common.error.retry%</ui-button> <ui-button @click="() => $emit('retry')">{{ $t('@.error.retry') }}</ui-button>
</div> </div>
</template> </template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n()
});
</script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.wjqjnyhzogztorhrdgcpqlkxhkmuetgj .wjqjnyhzogztorhrdgcpqlkxhkmuetgj
max-width 350px max-width 350px
@@ -1,19 +1,19 @@
<template> <template>
<div class="xqnhankfuuilcwvhgsopeqncafzsquya"> <div class="xqnhankfuuilcwvhgsopeqncafzsquya">
<button class="go-index" v-if="selfNav" @click="goIndex"><fa icon="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> <header><b><router-link :to="blackUser | userPage">{{ blackUser | userName }}</router-link></b>({{ $t('@.reversi.black') }}) vs <b><router-link :to="whiteUser | userPage">{{ whiteUser | userName }}</router-link></b>({{ $t('@.reversi.white') }})</header>
<div style="overflow: hidden; line-height: 28px;"> <div style="overflow: hidden; line-height: 28px;">
<p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}<mk-ellipsis/></p> <p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ $t('@.reversi.turn-of', { name: $options.filters.userName(turnUser) }) }}<mk-ellipsis/></p>
<p class="turn" v-if="logPos != logs.length">{{ '%i18n:common.reversi.past-turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}</p> <p class="turn" v-if="logPos != logs.length">{{ $t('@.reversi.past-turn-of', { name: $options.filters.userName(turnUser) }) }}</p>
<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p> <p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">{{ $t('@.reversi.opponent-turn') }}<mk-ellipsis/></p>
<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p> <p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">{{ $t('@.reversi.my-turn') }}</p>
<p class="result" v-if="game.isEnded && logPos == logs.length"> <p class="result" v-if="game.isEnded && logPos == logs.length">
<template v-if="game.winner"> <template v-if="game.winner">
<span>{{ '%i18n:common.reversi.won%'.replace('{}', $options.filters.userName(game.winner)) }}</span> <span>{{ $t('@.reversi.won', { name: $options.filters.userName(game.winner) }) }}</span>
<span v-if="game.surrendered != null"> (%i18n:@surrendered%)</span> <span v-if="game.surrendered != null"> ({{ $t('surrendered') }})</span>
</template> </template>
<template v-else>%i18n:common.reversi.drawn%</template> <template v-else>{{ $t('@.reversi.drawn') }}</template>
</p> </p>
</div> </div>
@@ -43,10 +43,10 @@
</div> </div>
</div> </div>
<p class="status"><b>{{ '%i18n:common.reversi.this-turn%'.split('{}')[0] }}{{ logPos }}{{ '%i18n:common.reversi.this-turn%'.split('{}')[1] }}</b> %i18n:common.reversi.black%:{{ o.blackCount }} %i18n:common.reversi.white%:{{ o.whiteCount }} %i18n:common.reversi.total%:{{ o.blackCount + o.whiteCount }}</p> <p class="status"><b>{{ $t('@.reversi.this-turn', { count: logPos }) }}</b> {{ $t('@.reversi.black') }}:{{ o.blackCount }} {{ $t('@.reversi.white') }}:{{ o.whiteCount }} {{ $t('@.reversi.total') }}:{{ o.blackCount + o.whiteCount }}</p>
<div class="actions" v-if="!game.isEnded && iAmPlayer"> <div class="actions" v-if="!game.isEnded && iAmPlayer">
<form-button @click="surrender">%i18n:@surrender%</form-button> <form-button @click="surrender">{{ $t('surrender') }}</form-button>
</div> </div>
<div class="player" v-if="game.isEnded"> <div class="player" v-if="game.isEnded">
@@ -62,20 +62,22 @@
</div> </div>
<div class="info"> <div class="info">
<p v-if="game.settings.isLlotheo">%i18n:@is-llotheo%</p> <p v-if="game.settings.isLlotheo">{{ $t('is-llotheo') }}</p>
<p v-if="game.settings.loopedBoard">%i18n:@looped-map%</p> <p v-if="game.settings.loopedBoard">{{ $t('looped-map') }}</p>
<p v-if="game.settings.canPutEverywhere">%i18n:@can-put-everywhere%</p> <p v-if="game.settings.canPutEverywhere">{{ $t('can-put-everywhere') }}</p>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../../i18n';
import * as CRC32 from 'crc-32'; import * as CRC32 from 'crc-32';
import Reversi, { Color } from '../../../../../../../games/reversi/core'; import Reversi, { Color } from '../../../../../../../games/reversi/core';
import { url } from '../../../../../config'; import { url } from '../../../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/games/reversi/reversi.game.vue'),
props: { props: {
initGame: { initGame: {
type: Object, type: Object,
@@ -289,7 +291,7 @@ export default Vue.extend({
}, },
surrender() { surrender() {
(this as any).api('games/reversi/games/surrender', { this.$root.api('games/reversi/games/surrender', {
gameId: this.game.id gameId: this.game.id
}); });
}, },
@@ -7,10 +7,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../../i18n';
import XGame from './reversi.game.vue'; import XGame from './reversi.game.vue';
import XRoom from './reversi.room.vue'; import XRoom from './reversi.room.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/games/reversi/reversi.gameroom.vue'),
components: { components: {
XGame, XGame,
XRoom XRoom
@@ -33,7 +35,7 @@ export default Vue.extend({
}, },
created() { created() {
this.g = this.game; this.g = this.game;
this.connection = (this as any).os.stream.connectToChannel('gamesReversiGame', { this.connection = this.$root.stream.connectToChannel('gamesReversiGame', {
gameId: this.game.id gameId: this.game.id
}); });
this.connection.on('started', this.onStarted); this.connection.on('started', this.onStarted);
@@ -1,22 +1,22 @@
<template> <template>
<div class="phgnkghfpyvkrvwiajkiuoxyrdaqpzcx"> <div class="phgnkghfpyvkrvwiajkiuoxyrdaqpzcx">
<h1>%i18n:@title%</h1> <h1>{{ $t('title') }}</h1>
<p>%i18n:@sub-title%</p> <p>{{ $t('sub-title') }}</p>
<div class="play"> <div class="play">
<form-button primary round @click="match">%i18n:@invite%</form-button> <form-button primary round @click="match">{{ $t('invite') }}</form-button>
<details> <details>
<summary>%i18n:@rule%</summary> <summary>{{ $t('rule') }}</summary>
<div> <div>
<p>%i18n:@rule-desc%</p> <p>{{ $t('rule-desc') }}</p>
<dl> <dl>
<dt><b>%i18n:@mode-invite%</b></dt> <dt><b>{{ $t('mode-invite') }}</b></dt>
<dd>%i18n:@mode-invite-desc%</dd> <dd>{{ $t('mode-invite-desc') }}</dd>
</dl> </dl>
</div> </div>
</details> </details>
</div> </div>
<section v-if="invitations.length > 0"> <section v-if="invitations.length > 0">
<h2>%i18n:@invitations%</h2> <h2>{{ $t('invitations') }}</h2>
<div class="invitation" v-for="i in invitations" tabindex="-1" @click="accept(i)"> <div class="invitation" v-for="i in invitations" tabindex="-1" @click="accept(i)">
<mk-avatar class="avatar" :user="i.parent"/> <mk-avatar class="avatar" :user="i.parent"/>
<span class="name"><b>{{ i.parent | userName }}</b></span> <span class="name"><b>{{ i.parent | userName }}</b></span>
@@ -25,22 +25,22 @@
</div> </div>
</section> </section>
<section v-if="myGames.length > 0"> <section v-if="myGames.length > 0">
<h2>%i18n:@my-games%</h2> <h2>{{ $t('my-games') }}</h2>
<a class="game" v-for="g in myGames" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`"> <a class="game" v-for="g in myGames" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`">
<mk-avatar class="avatar" :user="g.user1"/> <mk-avatar class="avatar" :user="g.user1"/>
<mk-avatar class="avatar" :user="g.user2"/> <mk-avatar class="avatar" :user="g.user2"/>
<span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span> <span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span>
<span class="state">{{ g.isEnded ? '%i18n:@game-state.ended%' : '%i18n:@game-state.playing%' }}</span> <span class="state">{{ g.isEnded ? $t('game-state.ended') : $t('game-state.playing') }}</span>
<mk-time :time="g.createdAt" /> <mk-time :time="g.createdAt" />
</a> </a>
</section> </section>
<section v-if="games.length > 0"> <section v-if="games.length > 0">
<h2>%i18n:@all-games%</h2> <h2>{{ $t('all-games') }}</h2>
<a class="game" v-for="g in games" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`"> <a class="game" v-for="g in games" tabindex="-1" @click.prevent="go(g)" :href="`/reversi/${g.id}`">
<mk-avatar class="avatar" :user="g.user1"/> <mk-avatar class="avatar" :user="g.user1"/>
<mk-avatar class="avatar" :user="g.user2"/> <mk-avatar class="avatar" :user="g.user2"/>
<span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span> <span><b>{{ g.user1 | userName }}</b> vs <b>{{ g.user2 | userName }}</b></span>
<span class="state">{{ g.isEnded ? '%i18n:@game-state.ended%' : '%i18n:@game-state.playing%' }}</span> <span class="state">{{ g.isEnded ? $t('game-state.ended') : $t('game-state.playing') }}</span>
<mk-time :time="g.createdAt" /> <mk-time :time="g.createdAt" />
</a> </a>
</section> </section>
@@ -49,8 +49,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/games/reversi/reversi.index.vue'),
data() { data() {
return { return {
games: [], games: [],
@@ -65,22 +67,22 @@ export default Vue.extend({
mounted() { mounted() {
if (this.$store.getters.isSignedIn) { if (this.$store.getters.isSignedIn) {
this.connection = (this as any).os.stream.useSharedConnection('gamesReversi'); this.connection = this.$root.stream.useSharedConnection('gamesReversi');
this.connection.on('invited', this.onInvited); this.connection.on('invited', this.onInvited);
(this as any).api('games/reversi/games', { this.$root.api('games/reversi/games', {
my: true my: true
}).then(games => { }).then(games => {
this.myGames = games; this.myGames = games;
}); });
(this as any).api('games/reversi/invitations').then(invitations => { this.$root.api('games/reversi/invitations').then(invitations => {
this.invitations = this.invitations.concat(invitations); this.invitations = this.invitations.concat(invitations);
}); });
} }
(this as any).api('games/reversi/games').then(games => { this.$root.api('games/reversi/games').then(games => {
this.games = games; this.games = games;
this.gamesFetching = false; this.gamesFetching = false;
}); });
@@ -98,13 +100,13 @@ export default Vue.extend({
}, },
match() { match() {
(this as any).apis.input({ this.$input({
title: '%i18n:@enter-username%' title: this.$t('enter-username')
}).then(username => { }).then(username => {
(this as any).api('users/show', { this.$root.api('users/show', {
username username
}).then(user => { }).then(user => {
(this as any).api('games/reversi/match', { this.$root.api('games/reversi/match', {
userId: user.id userId: user.id
}).then(res => { }).then(res => {
if (res == null) { if (res == null) {
@@ -118,7 +120,7 @@ export default Vue.extend({
}, },
accept(invitation) { accept(invitation) {
(this as any).api('games/reversi/match', { this.$root.api('games/reversi/match', {
userId: invitation.parent.id userId: invitation.parent.id
}).then(game => { }).then(game => {
if (game) { if (game) {
@@ -3,13 +3,13 @@
<header><b>{{ game.user1 | userName }}</b> vs <b>{{ game.user2 | userName }}</b></header> <header><b>{{ game.user1 | userName }}</b> vs <b>{{ game.user2 | userName }}</b></header>
<div> <div>
<p>%i18n:@settings-of-the-game%</p> <p>{{ $t('settings-of-the-game') }}</p>
<div class="card map"> <div class="card map">
<header> <header>
<select v-model="mapName" placeholder="%i18n:@choose-map%" @change="onMapChange"> <select v-model="mapName" :placeholder="$t('choose-map')" @change="onMapChange">
<option label="-Custom-" :value="mapName" v-if="mapName == '-Custom-'"/> <option label="-Custom-" :value="mapName" v-if="mapName == '-Custom-'"/>
<option label="%i18n:@random%" :value="null"/> <option :label="$t('random')" :value="null"/>
<optgroup v-for="c in mapCategories" :key="c" :label="c"> <optgroup v-for="c in mapCategories" :key="c" :label="c">
<option v-for="m in maps" v-if="m.category == c" :key="m.name" :label="m.name" :value="m.name">{{ m.name }}</option> <option v-for="m in maps" v-if="m.category == c" :key="m.name" :label="m.name" :value="m.name">{{ m.name }}</option>
</optgroup> </optgroup>
@@ -31,31 +31,31 @@
<div class="card"> <div class="card">
<header> <header>
<span>%i18n:@black-or-white%</span> <span>{{ $t('black-or-white') }}</span>
</header> </header>
<div> <div>
<form-radio v-model="game.settings.bw" value="random" @change="updateSettings">%i18n:@random%</form-radio> <form-radio v-model="game.settings.bw" value="random" @change="updateSettings">{{ $t('random') }}</form-radio>
<form-radio v-model="game.settings.bw" :value="1" @change="updateSettings">{{ '%i18n:@black-is%'.split('{}')[0] }}<b>{{ game.user1 | userName }}</b>{{ '%i18n:@black-is%'.split('{}')[1] }}</form-radio> <form-radio v-model="game.settings.bw" :value="1" @change="updateSettings">{{ this.$t('black-is').split('{}')[0] }}<b>{{ game.user1 | userName }}</b>{{ this.$t('black-is').split('{}')[1] }}</form-radio>
<form-radio v-model="game.settings.bw" :value="2" @change="updateSettings">{{ '%i18n:@black-is%'.split('{}')[0] }}<b>{{ game.user2 | userName }}</b>{{ '%i18n:@black-is%'.split('{}')[1] }}</form-radio> <form-radio v-model="game.settings.bw" :value="2" @change="updateSettings">{{ this.$t('black-is').split('{}')[0] }}<b>{{ game.user2 | userName }}</b>{{ this.$t('black-is').split('{}')[1] }}</form-radio>
</div> </div>
</div> </div>
<div class="card"> <div class="card">
<header> <header>
<span>%i18n:@rules%</span> <span>{{ $t('rules') }}</span>
</header> </header>
<div> <div>
<ui-switch v-model="game.settings.isLlotheo" @change="updateSettings">%i18n:@is-llotheo%</ui-switch> <ui-switch v-model="game.settings.isLlotheo" @change="updateSettings">{{ $t('is-llotheo') }}</ui-switch>
<ui-switch v-model="game.settings.loopedBoard" @change="updateSettings">%i18n:@looped-map%</ui-switch> <ui-switch v-model="game.settings.loopedBoard" @change="updateSettings">{{ $t('looped-map') }}</ui-switch>
<ui-switch v-model="game.settings.canPutEverywhere" @change="updateSettings">%i18n:@can-put-everywhere%</ui-switch> <ui-switch v-model="game.settings.canPutEverywhere" @change="updateSettings">{{ $t('can-put-everywhere') }}</ui-switch>
</div> </div>
</div> </div>
<div class="card form" v-if="form"> <div class="card form" v-if="form">
<header> <header>
<span>%i18n:@settings-of-the-bot%</span> <span>{{ $t('settings-of-the-bot') }}</span>
</header> </header>
<div> <div>
@@ -98,16 +98,16 @@
<footer> <footer>
<p class="status"> <p class="status">
<template v-if="isAccepted && isOpAccepted">%i18n:@this-game-is-started-soon%<mk-ellipsis/></template> <template v-if="isAccepted && isOpAccepted">{{ $t('this-game-is-started-soon') }}<mk-ellipsis/></template>
<template v-if="isAccepted && !isOpAccepted">%i18n:@waiting-for-other%<mk-ellipsis/></template> <template v-if="isAccepted && !isOpAccepted">{{ $t('waiting-for-other') }}<mk-ellipsis/></template>
<template v-if="!isAccepted && isOpAccepted">%i18n:@waiting-for-me%</template> <template v-if="!isAccepted && isOpAccepted">{{ $t('waiting-for-me') }}</template>
<template v-if="!isAccepted && !isOpAccepted">%i18n:@waiting-for-both%<mk-ellipsis/></template> <template v-if="!isAccepted && !isOpAccepted">{{ $t('waiting-for-both') }}<mk-ellipsis/></template>
</p> </p>
<div class="actions"> <div class="actions">
<form-button @click="exit">%i18n:@cancel%</form-button> <form-button @click="exit">{{ $t('cancel') }}</form-button>
<form-button primary @click="accept" v-if="!isAccepted">%i18n:@ready%</form-button> <form-button primary @click="accept" v-if="!isAccepted">{{ $t('ready') }}</form-button>
<form-button primary @click="cancel" v-if="isAccepted">%i18n:@cancel-ready%</form-button> <form-button primary @click="cancel" v-if="isAccepted">{{ $t('cancel-ready') }}</form-button>
</div> </div>
</footer> </footer>
</div> </div>
@@ -115,9 +115,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../../i18n';
import * as maps from '../../../../../../../games/reversi/maps'; import * as maps from '../../../../../../../games/reversi/maps';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/games/reversi/reversi.room.vue'),
props: ['game', 'connection'], props: ['game', 'connection'],
data() { data() {
@@ -4,9 +4,9 @@
<x-gameroom :game="game" :self-nav="selfNav" @go-index="goIndex"/> <x-gameroom :game="game" :self-nav="selfNav" @go-index="goIndex"/>
</div> </div>
<div class="matching" v-else-if="matching"> <div class="matching" v-else-if="matching">
<h1>{{ '%i18n:@matching.waiting-for%'.split('{}')[0] }}<b>{{ matching | userName }}</b>{{ '%i18n:@matching.waiting-for%'.split('{}')[1] }}<mk-ellipsis/></h1> <h1>{{ this.$t('matching.waiting-for').split('{}')[0] }}<b>{{ matching | userName }}</b>{{ this.$t('matching.waiting-for').split('{}')[1] }}<mk-ellipsis/></h1>
<div class="cancel"> <div class="cancel">
<form-button round @click="cancel">%i18n:@matching.cancel%</form-button> <form-button round @click="cancel">{{ $t('matching.cancel') }}</form-button>
</div> </div>
</div> </div>
<div v-else-if="gameId"> <div v-else-if="gameId">
@@ -20,11 +20,13 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../../i18n';
import XGameroom from './reversi.gameroom.vue'; import XGameroom from './reversi.gameroom.vue';
import XIndex from './reversi.index.vue'; import XIndex from './reversi.index.vue';
import Progress from '../../../../scripts/loading'; import Progress from '../../../../scripts/loading';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/games/reversi/reversi.vue'),
components: { components: {
XGameroom, XGameroom,
XIndex XIndex
@@ -65,7 +67,7 @@ export default Vue.extend({
this.fetch(); this.fetch();
if (this.$store.getters.isSignedIn) { if (this.$store.getters.isSignedIn) {
this.connection = (this as any).os.stream.useSharedConnection('gamesReversi'); this.connection = this.$root.stream.useSharedConnection('gamesReversi');
this.connection.on('matched', this.onMatched); this.connection.on('matched', this.onMatched);
@@ -92,7 +94,7 @@ export default Vue.extend({
this.game = null; this.game = null;
} else { } else {
Progress.start(); Progress.start();
(this as any).api('games/reversi/games/show', { this.$root.api('games/reversi/games/show', {
gameId: this.gameId gameId: this.gameId
}).then(game => { }).then(game => {
this.game = game; this.game = game;
@@ -105,7 +107,7 @@ export default Vue.extend({
if (this.selfNav) { if (this.selfNav) {
// //
if (game != null && (game.settings == null || game.settings.map == null)) { if (game != null && (game.settings == null || game.settings.map == null)) {
game = await (this as any).api('games/reversi/games/show', { game = await this.$root.api('games/reversi/games/show', {
gameId: game.id gameId: game.id
}); });
} }
@@ -122,11 +124,11 @@ export default Vue.extend({
cancel() { cancel() {
this.matching = null; this.matching = null;
(this as any).api('games/reversi/match/cancel'); this.$root.api('games/reversi/match/cancel');
}, },
accept(invitation) { accept(invitation) {
(this as any).api('games/reversi/match', { this.$root.api('games/reversi/match', {
userId: invitation.parent.id userId: invitation.parent.id
}).then(game => { }).then(game => {
if (game) { if (game) {
@@ -1,11 +1,11 @@
<template> <template>
<div class="mk-github-setting"> <div class="mk-github-setting">
<p>%i18n:@description%<a :href="`${docsUrl}/link-to-github`" target="_blank">%i18n:@detail%</a></p> <p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-github`" target="_blank">{{ $t('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 class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
<p> <p>
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a> <a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? this.$t('reconnect') : this.$t('connect') }}</a>
<span v-if="$store.state.i.github"> or </span> <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> <a :href="`${apiUrl}/disconnect/github`" target="_blank" v-if="$store.state.i.github" @click.prevent="disconnect">{{ $t('disconnect') }}</a>
</p> </p>
<p class="id" v-if="$store.state.i.github">GitHub ID: {{ $store.state.i.github.id }}</p> <p class="id" v-if="$store.state.i.github">GitHub ID: {{ $store.state.i.github.id }}</p>
</div> </div>
@@ -13,9 +13,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, docsUrl } from '../../../config'; import { apiUrl, docsUrl } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/github-setting.vue'),
data() { data() {
return { return {
form: null, form: null,
@@ -1,13 +1,16 @@
<template> <template>
<div class="mk-google"> <div class="mk-google">
<input type="search" v-model="query" :placeholder="q"> <input type="search" v-model="query" :placeholder="q">
<button @click="search"><fa icon="search"/> %i18n:common.search%</button> <button @click="search"><fa icon="search"/> {{ $t('@.search') }}</button>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n(),
props: ['q'], props: ['q'],
data() { data() {
return { return {
@@ -1,5 +1,5 @@
<template> <template>
<div class="mk-media-image-dialog"> <div class="dkjvrdxtkvqrwmhfickhndpmnncsgacq">
<div class="bg" @click="close"></div> <div class="bg" @click="close"></div>
<img :src="image.url" :alt="image.name" :title="image.name" @click="close"/> <img :src="image.url" :alt="image.name" :title="image.name" @click="close"/>
</div> </div>
@@ -34,7 +34,7 @@ export default Vue.extend({
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mk-media-image-dialog .dkjvrdxtkvqrwmhfickhndpmnncsgacq
display block display block
position fixed position fixed
z-index 2048 z-index 2048
@@ -30,7 +30,6 @@ import time from './time.vue';
import timer from './timer.vue'; import timer from './timer.vue';
import mediaList from './media-list.vue'; import mediaList from './media-list.vue';
import uploader from './uploader.vue'; import uploader from './uploader.vue';
import specialMessage from './special-message.vue';
import streamIndicator from './stream-indicator.vue'; import streamIndicator from './stream-indicator.vue';
import ellipsis from './ellipsis.vue'; import ellipsis from './ellipsis.vue';
import messaging from './messaging.vue'; import messaging from './messaging.vue';
@@ -84,7 +83,6 @@ Vue.component('mk-time', time);
Vue.component('mk-timer', timer); Vue.component('mk-timer', timer);
Vue.component('mk-media-list', mediaList); Vue.component('mk-media-list', mediaList);
Vue.component('mk-uploader', uploader); Vue.component('mk-uploader', uploader);
Vue.component('mk-special-message', specialMessage);
Vue.component('mk-stream-indicator', streamIndicator); Vue.component('mk-stream-indicator', streamIndicator);
Vue.component('mk-ellipsis', ellipsis); Vue.component('mk-ellipsis', ellipsis);
Vue.component('mk-messaging', messaging); Vue.component('mk-messaging', messaging);
@@ -3,22 +3,24 @@
<div class="banner" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"></div> <div class="banner" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"></div>
<h1>{{ meta.name }}</h1> <h1>{{ meta.name }}</h1>
<p v-html="meta.description || '%i18n:common.about%'"></p> <p v-html="meta.description || this.$t('@.about')"></p>
<router-link to="/">%i18n:@start%</router-link> <router-link to="/">{{ $t('start') }}</router-link>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/instance.vue'),
data() { data() {
return { return {
meta: null meta: null
} }
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.meta = meta; this.meta = meta;
}); });
} }
@@ -2,8 +2,8 @@
<div class="mk-media-banner"> <div class="mk-media-banner">
<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false"> <div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
<span class="icon"><fa icon="exclamation-triangle"/></span> <span class="icon"><fa icon="exclamation-triangle"/></span>
<b>%i18n:@sensitive%</b> <b>{{ $t('sensitive') }}</b>
<span>%i18n:@click-to-show%</span> <span>{{ $t('click-to-show') }}</span>
</div> </div>
<div class="audio" v-else-if="media.type.startsWith('audio')"> <div class="audio" v-else-if="media.type.startsWith('audio')">
<audio class="audio" <audio class="audio"
@@ -26,8 +26,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/media-banner.vue'),
props: { props: {
media: { media: {
type: Object, type: Object,
@@ -8,18 +8,18 @@
ref="textarea" ref="textarea"
@keypress="onKeypress" @keypress="onKeypress"
@paste="onPaste" @paste="onPaste"
placeholder="%i18n:@input-message-here%" :placeholder="$t('input-message-here')"
v-autocomplete="'text'" v-autocomplete="'text'"
></textarea> ></textarea>
<div class="file" @click="file = null" v-if="file">{{ file.name }}</div> <div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
<mk-uploader ref="uploader" @uploaded="onUploaded"/> <mk-uploader ref="uploader" @uploaded="onUploaded"/>
<button class="send" @click="send" :disabled="!canSend || sending" title="%i18n:@send%"> <button class="send" @click="send" :disabled="!canSend || sending" :title="$t('send')">
<template v-if="!sending"><fa icon="paper-plane"/></template><template v-if="sending"><fa icon="spinner .spin"/></template> <template v-if="!sending"><fa icon="paper-plane"/></template><template v-if="sending"><fa icon="spinner .spin"/></template>
</button> </button>
<button class="attach-from-local" @click="chooseFile" title="%i18n:@attach-from-local%"> <button class="attach-from-local" @click="chooseFile" :title="$t('attach-from-local')">
<fa icon="upload"/> <fa icon="upload"/>
</button> </button>
<button class="attach-from-drive" @click="chooseFileFromDrive" title="%i18n:@attach-from-drive%"> <button class="attach-from-drive" @click="chooseFileFromDrive" :title="$t('attach-from-drive')">
<fa :icon="['far', 'folder-open']"/> <fa :icon="['far', 'folder-open']"/>
</button> </button>
<input ref="file" type="file" @change="onChangeFile"/> <input ref="file" type="file" @change="onChangeFile"/>
@@ -28,9 +28,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as autosize from 'autosize'; import * as autosize from 'autosize';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/messaging-room.form.vue'),
props: ['user'], props: ['user'],
data() { data() {
return { return {
@@ -129,7 +131,7 @@ export default Vue.extend({
}, },
chooseFileFromDrive() { chooseFileFromDrive() {
(this as any).apis.chooseDriveFile({ this.$chooseDriveFile({
multiple: false multiple: false
}).then(file => { }).then(file => {
this.file = file; this.file = file;
@@ -150,7 +152,7 @@ export default Vue.extend({
send() { send() {
this.sending = true; this.sending = true;
(this as any).api('messaging/messages/create', { this.$root.api('messaging/messages/create', {
userId: this.user.id, userId: this.user.id,
text: this.text ? this.text : undefined, text: this.text ? this.text : undefined,
fileId: this.file ? this.file.id : undefined fileId: this.file ? this.file.id : undefined
@@ -3,7 +3,7 @@
<mk-avatar class="avatar" :user="message.user" target="_blank"/> <mk-avatar class="avatar" :user="message.user" target="_blank"/>
<div class="content"> <div class="content">
<div class="balloon" :data-no-text="message.text == null"> <div class="balloon" :data-no-text="message.text == null">
<!-- <button class="delete-button" v-if="isMe" title="%i18n:common.delete%"> <!-- <button class="delete-button" v-if="isMe" :title="$t('@.delete')">
<img src="/assets/desktop/messaging/delete.png" alt="Delete"/> <img src="/assets/desktop/messaging/delete.png" alt="Delete"/>
</button> --> </button> -->
<div class="content" v-if="!message.isDeleted"> <div class="content" v-if="!message.isDeleted">
@@ -16,13 +16,13 @@
</div> </div>
</div> </div>
<div class="content" v-if="message.isDeleted"> <div class="content" v-if="message.isDeleted">
<p class="is-deleted">%i18n:@deleted%</p> <p class="is-deleted">{{ $t('deleted') }}</p>
</div> </div>
</div> </div>
<div></div> <div></div>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<footer> <footer>
<span class="read" v-if="isMe && message.isRead">%i18n:@is-read%</span> <span class="read" v-if="isMe && message.isRead">{{ $t('is-read') }}</span>
<mk-time :time="message.createdAt"/> <mk-time :time="message.createdAt"/>
<template v-if="message.is_edited"><fa icon="pencil-alt"/></template> <template v-if="message.is_edited"><fa icon="pencil-alt"/></template>
</footer> </footer>
@@ -32,9 +32,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import parse from '../../../../../mfm/parse'; import parse from '../../../../../mfm/parse';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/messaging-room.message.vue'),
props: { props: {
message: { message: {
required: true required: true
@@ -4,11 +4,11 @@
@drop.prevent.stop="onDrop" @drop.prevent.stop="onDrop"
> >
<div class="body"> <div class="body">
<p class="init" v-if="init"><fa icon="spinner .spin"/>%i18n:common.loading%</p> <p class="init" v-if="init"><fa icon="spinner .spin"/>{{ $t('@.loading') }}</p>
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>%i18n:@empty%</p> <p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>%i18n:@no-history%</p> <p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>{{ $t('no-history') }}</p>
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages"> <button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }} <template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
</button> </button>
<template v-for="(message, i) in _messages"> <template v-for="(message, i) in _messages">
<x-message :message="message" :key="message.id"/> <x-message :message="message" :key="message.id"/>
@@ -20,7 +20,7 @@
<footer> <footer>
<transition name="fade"> <transition name="fade">
<div class="new-message" v-show="showIndicator"> <div class="new-message" v-show="showIndicator">
<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>%i18n:@new-message%</button> <button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>{{ $t('new-message') }}</button>
</div> </div>
</transition> </transition>
<x-form :user="user" ref="form"/> <x-form :user="user" ref="form"/>
@@ -30,11 +30,13 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import XMessage from './messaging-room.message.vue'; import XMessage from './messaging-room.message.vue';
import XForm from './messaging-room.form.vue'; import XForm from './messaging-room.form.vue';
import { url } from '../../../config'; import { url } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/messaging-room.vue'),
components: { components: {
XMessage, XMessage,
XForm XForm
@@ -60,7 +62,7 @@ export default Vue.extend({
const date = new Date(message.createdAt).getDate(); const date = new Date(message.createdAt).getDate();
const month = new Date(message.createdAt).getMonth() + 1; const month = new Date(message.createdAt).getMonth() + 1;
message._date = date; message._date = date;
message._datetext = '%i18n:common.month-and-day%'.replace('{month}', month.toString()).replace('{day}', date.toString()); message._datetext = this.$t('@.month-and-day').replace('{month}', month.toString()).replace('{day}', date.toString());
return message; return message;
}); });
}, },
@@ -71,7 +73,7 @@ export default Vue.extend({
}, },
mounted() { mounted() {
this.connection = (this as any).os.stream.connectToChannel('messaging', { otherparty: this.user.id }); this.connection = this.$root.stream.connectToChannel('messaging', { otherparty: this.user.id });
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
this.connection.on('read', this.onRead); this.connection.on('read', this.onRead);
@@ -120,7 +122,7 @@ export default Vue.extend({
this.form.upload(e.dataTransfer.files[0]); this.form.upload(e.dataTransfer.files[0]);
return; return;
} else if (e.dataTransfer.files.length > 1) { } else if (e.dataTransfer.files.length > 1) {
alert('%i18n:@only-one-file-attached%'); alert(this.$t('only-one-file-attached'));
return; return;
} }
@@ -137,7 +139,7 @@ export default Vue.extend({
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const max = this.existMoreMessages ? 20 : 10; const max = this.existMoreMessages ? 20 : 10;
(this as any).api('messaging/messages', { this.$root.api('messaging/messages', {
userId: this.user.id, userId: this.user.id,
limit: max + 1, limit: max + 1,
untilId: this.existMoreMessages ? this.messages[0].id : undefined untilId: this.existMoreMessages ? this.messages[0].id : undefined
@@ -3,7 +3,7 @@
<div class="search" v-if="!compact" :style="{ top: headerTop + 'px' }"> <div class="search" v-if="!compact" :style="{ top: headerTop + 'px' }">
<div class="form"> <div class="form">
<label for="search-input"><i><fa icon="search"/></i></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%"/> <input v-model="q" type="search" @input="search" @keydown="onSearchKeydown" :placeholder="$t('search-user')"/>
</div> </div>
<div class="result"> <div class="result">
<ol class="users" v-if="result.length > 0" ref="searchResult"> <ol class="users" v-if="result.length > 0" ref="searchResult">
@@ -38,22 +38,24 @@
<mk-time :time="message.createdAt"/> <mk-time :time="message.createdAt"/>
</header> </header>
<div class="body"> <div class="body">
<p class="text"><span class="me" v-if="isMe(message)">%i18n:@you%:</span>{{ message.text }}</p> <p class="text"><span class="me" v-if="isMe(message)">{{ $t('you') }}:</span>{{ message.text }}</p>
</div> </div>
</div> </div>
</a> </a>
</template> </template>
</div> </div>
<p class="no-history" v-if="!fetching && messages.length == 0">%i18n:@no-history%</p> <p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p> <p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import getAcct from '../../../../../misc/acct/render'; import getAcct from '../../../../../misc/acct/render';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/messaging.vue'),
props: { props: {
compact: { compact: {
type: Boolean, type: Boolean,
@@ -75,12 +77,12 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
this.connection = (this as any).os.stream.useSharedConnection('messagingIndex'); this.connection = this.$root.stream.useSharedConnection('messagingIndex');
this.connection.on('message', this.onMessage); this.connection.on('message', this.onMessage);
this.connection.on('read', this.onRead); this.connection.on('read', this.onRead);
(this as any).api('messaging/history').then(messages => { this.$root.api('messaging/history').then(messages => {
this.messages = messages; this.messages = messages;
this.fetching = false; this.fetching = false;
}); });
@@ -111,7 +113,7 @@ export default Vue.extend({
this.result = []; this.result = [];
return; return;
} }
(this as any).api('users/search', { this.$root.api('users/search', {
query: this.q, query: this.q,
max: 5 max: 5
}).then(users => { }).then(users => {
@@ -187,7 +187,7 @@ export default Vue.component('misskey-flavored-markdown', {
} }
case 'emoji': { case 'emoji': {
const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis || []; const customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
return [createElement('mk-emoji', { return [createElement('mk-emoji', {
attrs: { attrs: {
emoji: token.emoji, emoji: token.emoji,
@@ -1,10 +1,10 @@
<template> <template>
<ui-card> <ui-card>
<div slot="title"><fa icon="ban"/> %i18n:@mute-and-block%</div> <div slot="title"><fa icon="ban"/> {{ $t('mute-and-block') }}</div>
<section> <section>
<header>%i18n:@mute%</header> <header>{{ $t('mute') }}</header>
<ui-info v-if="!muteFetching && mute.length == 0">%i18n:@no-muted-users%</ui-info> <ui-info v-if="!muteFetching && mute.length == 0">{{ $t('no-muted-users') }}</ui-info>
<div class="users" v-if="mute.length != 0"> <div class="users" v-if="mute.length != 0">
<div v-for="user in mute" :key="user.id"> <div v-for="user in mute" :key="user.id">
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p> <p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
@@ -13,8 +13,8 @@
</section> </section>
<section> <section>
<header>%i18n:@block%</header> <header>{{ $t('block') }}</header>
<ui-info v-if="!blockFetching && block.length == 0">%i18n:@no-blocked-users%</ui-info> <ui-info v-if="!blockFetching && block.length == 0">{{ $t('no-blocked-users') }}</ui-info>
<div class="users" v-if="block.length != 0"> <div class="users" v-if="block.length != 0">
<div v-for="user in block" :key="user.id"> <div v-for="user in block" :key="user.id">
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p> <p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
@@ -26,8 +26,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/mute-and-block.vue'),
data() { data() {
return { return {
muteFetching: true, muteFetching: true,
@@ -38,12 +40,12 @@ export default Vue.extend({
}, },
mounted() { mounted() {
(this as any).api('mute/list').then(mute => { this.$root.api('mute/list').then(mute => {
this.mute = mute.map(x => x.mutee); this.mute = mute.map(x => x.mutee);
this.muteFetching = false; this.muteFetching = false;
}); });
(this as any).api('blocking/list').then(blocking => { this.$root.api('blocking/list').then(blocking => {
this.block = blocking.map(x => x.blockee); this.block = blocking.map(x => x.blockee);
this.blockFetching = false; this.blockFetching = false;
}); });
@@ -1,20 +1,22 @@
<template> <template>
<span class="mk-nav"> <span class="mk-nav">
<a :href="aboutUrl">%i18n:@about%</a> <a :href="aboutUrl">{{ $t('about') }}</a>
<i></i> <i></i>
<a :href="repositoryUrl">%i18n:@repository%</a> <a :href="repositoryUrl">{{ $t('repository') }}</a>
<i></i> <i></i>
<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a> <a :href="feedbackUrl" target="_blank">{{ $t('feedback') }}</a>
<i></i> <i></i>
<a href="/dev">%i18n:@develop%</a> <a href="/dev">{{ $t('develop') }}</a>
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { lang } from '../../../config'; import { lang } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/nav.vue'),
data() { data() {
return { return {
aboutUrl: `/docs/${lang}/about`, aboutUrl: `/docs/${lang}/about`,
@@ -23,7 +25,7 @@ export default Vue.extend({
} }
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
if (meta.maintainer.repository_url) this.repositoryUrl = meta.maintainer.repository_url; if (meta.maintainer.repository_url) this.repositoryUrl = meta.maintainer.repository_url;
if (meta.maintainer.feedback_url) this.feedbackUrl = meta.maintainer.feedback_url; if (meta.maintainer.feedback_url) this.feedbackUrl = meta.maintainer.feedback_url;
}); });
@@ -6,9 +6,9 @@
<span class="is-bot" v-if="note.user.isBot">bot</span> <span class="is-bot" v-if="note.user.isBot">bot</span>
<span class="is-cat" v-if="note.user.isCat">cat</span> <span class="is-cat" v-if="note.user.isCat">cat</span>
<span class="username"><mk-acct :user="note.user"/></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 icon="star"/></span> <span class="is-verified" v-if="note.user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
<div class="info"> <div class="info">
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span> <span class="app" v-if="note.app && !mini && $store.state.settings.showVia">via <b>{{ note.app.name }}</b></span>
<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span> <span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
<router-link class="created-at" :to="note | notePage"> <router-link class="created-at" :to="note | notePage">
<mk-time :time="note.createdAt"/> <mk-time :time="note.createdAt"/>
@@ -25,8 +25,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n(),
props: { props: {
note: { note: {
type: Object, type: Object,
@@ -6,28 +6,30 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { url } from '../../../config'; import { url } from '../../../config';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard'; import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import Ok from './ok.vue'; import Ok from './ok.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/note-menu.vue'),
props: ['note', 'source', 'compact'], props: ['note', 'source', 'compact'],
computed: { computed: {
items() { items() {
const items = [{ const items = [{
icon: 'info-circle', icon: 'info-circle',
text: '%i18n:@detail%', text: this.$t('detail'),
action: this.detail action: this.detail
}, { }, {
icon: 'link', icon: 'link',
text: '%i18n:@copy-link%', text: this.$t('copy-link'),
action: this.copyLink action: this.copyLink
}]; }];
if (this.note.uri) { if (this.note.uri) {
items.push({ items.push({
icon: 'external-link-square-alt', icon: 'external-link-square-alt',
text: '%i18n:@remote%', text: this.$t('remote'),
action: () => { action: () => {
window.open(this.note.uri, '_blank'); window.open(this.note.uri, '_blank');
} }
@@ -39,13 +41,13 @@ export default Vue.extend({
if (this.note.isFavorited) { if (this.note.isFavorited) {
items.push({ items.push({
icon: 'star', icon: 'star',
text: '%i18n:@unfavorite%', text: this.$t('unfavorite'),
action: this.unfavorite action: this.unfavorite
}); });
} else { } else {
items.push({ items.push({
icon: 'star', icon: 'star',
text: '%i18n:@favorite%', text: this.$t('favorite'),
action: this.favorite action: this.favorite
}); });
} }
@@ -54,13 +56,13 @@ export default Vue.extend({
if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) { if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) {
items.push({ items.push({
icon: 'thumbtack', icon: 'thumbtack',
text: '%i18n:@unpin%', text: this.$t('unpin'),
action: this.unpin action: this.unpin
}); });
} else { } else {
items.push({ items.push({
icon: 'thumbtack', icon: 'thumbtack',
text: '%i18n:@pin%', text: this.$t('pin'),
action: this.pin action: this.pin
}); });
} }
@@ -70,7 +72,7 @@ export default Vue.extend({
items.push(null); items.push(null);
items.push({ items.push({
icon: ['far', 'trash-alt'], icon: ['far', 'trash-alt'],
text: '%i18n:@delete%', text: this.$t('delete'),
action: this.del action: this.del
}); });
} }
@@ -89,16 +91,16 @@ export default Vue.extend({
}, },
pin() { pin() {
(this as any).api('i/pin', { this.$root.api('i/pin', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
(this as any).os.new(Ok); this.$root.new(Ok);
this.destroyDom(); this.destroyDom();
}); });
}, },
unpin() { unpin() {
(this as any).api('i/unpin', { this.$root.api('i/unpin', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
this.destroyDom(); this.destroyDom();
@@ -106,8 +108,8 @@ export default Vue.extend({
}, },
del() { del() {
if (!window.confirm('%i18n:@delete-confirm%')) return; if (!window.confirm(this.$t('delete-confirm'))) return;
(this as any).api('notes/delete', { this.$root.api('notes/delete', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
this.destroyDom(); this.destroyDom();
@@ -115,19 +117,19 @@ export default Vue.extend({
}, },
favorite() { favorite() {
(this as any).api('notes/favorites/create', { this.$root.api('notes/favorites/create', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
(this as any).os.new(Ok); this.$root.new(Ok);
this.destroyDom(); this.destroyDom();
}); });
}, },
unfavorite() { unfavorite() {
(this as any).api('notes/favorites/delete', { this.$root.api('notes/favorites/delete', {
noteId: this.note.id noteId: this.note.id
}).then(() => { }).then(() => {
(this as any).os.new(Ok); this.$root.new(Ok);
this.destroyDom(); this.destroyDom();
}); });
}, },
@@ -1,42 +1,44 @@
<template> <template>
<div> <div>
<ui-button @click="reset">%i18n:@reset%</ui-button> <ui-button @click="reset">{{ $t('reset') }}</ui-button>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/password-settings.vue'),
methods: { methods: {
reset() { reset() {
(this as any).apis.input({ this.$input({
title: '%i18n:@enter-current-password%', title: this.$t('enter-current-password'),
type: 'password' type: 'password'
}).then(currentPassword => { }).then(currentPassword => {
(this as any).apis.input({ this.$input({
title: '%i18n:@enter-new-password%', title: this.$t('enter-new-password'),
type: 'password' type: 'password'
}).then(newPassword => { }).then(newPassword => {
(this as any).apis.input({ this.$input({
title: '%i18n:@enter-new-password-again%', title: this.$t('enter-new-password-again'),
type: 'password' type: 'password'
}).then(newPassword2 => { }).then(newPassword2 => {
if (newPassword !== newPassword2) { if (newPassword !== newPassword2) {
(this as any).apis.dialog({ this.$dialog({
title: null, title: null,
text: '%i18n:@not-match%', text: this.$t('not-match'),
actions: [{ actions: [{
text: 'OK' text: 'OK'
}] }]
}); });
return; return;
} }
(this as any).api('i/change_password', { this.$root.api('i/change_password', {
currentPasword: currentPassword, currentPasword: currentPassword,
newPassword: newPassword newPassword: newPassword
}).then(() => { }).then(() => {
(this as any).apis.notify('%i18n:@changed%'); this.$notify(this.$t('changed'));
}); });
}); });
}); });
@@ -1,18 +1,18 @@
<template> <template>
<div class="mk-poll-editor"> <div class="mk-poll-editor">
<p class="caution" v-if="choices.length < 2"> <p class="caution" v-if="choices.length < 2">
<fa icon="exclamation-triangle"/>%i18n:@no-only-one-choice% <fa icon="exclamation-triangle"/>{{ $t('no-only-one-choice') }}
</p> </p>
<ul ref="choices"> <ul ref="choices">
<li v-for="(choice, i) in choices"> <li v-for="(choice, i) in choices">
<input :value="choice" @input="onInput(i, $event)" :placeholder="'%i18n:@choice-n%'.replace('{}', i + 1)"> <input :value="choice" @input="onInput(i, $event)" :placeholder="this.$t('choice-n').replace('{}', i + 1)">
<button @click="remove(i)" title="%i18n:@remove%"> <button @click="remove(i)" :title="$t('remove')">
<fa icon="times"/> <fa icon="times"/>
</button> </button>
</li> </li>
</ul> </ul>
<button class="add" v-if="choices.length < 10" @click="add">%i18n:@add%</button> <button class="add" v-if="choices.length < 10" @click="add">{{ $t('add') }}</button>
<button class="destroy" @click="destroy" title="%i18n:@destroy%"> <button class="destroy" @click="destroy" :title="$t('destroy')">
<fa icon="times"/> <fa icon="times"/>
</button> </button>
</div> </div>
@@ -20,8 +20,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { erase } from '../../../../../prelude/array'; import { erase } from '../../../../../prelude/array';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/poll-editor.vue'),
data() { data() {
return { return {
choices: ['', ''] choices: ['', '']
@@ -1,28 +1,30 @@
<template> <template>
<div class="mk-poll" :data-is-voted="isVoted"> <div class="mk-poll" :data-is-voted="isVoted">
<ul> <ul>
<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) : ''"> <li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? $t('vote-to').replace('{}', choice.text) : ''">
<div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div> <div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div>
<span> <span>
<template v-if="choice.isVoted"><fa icon="check"/></template> <template v-if="choice.isVoted"><fa icon="check"/></template>
<span>{{ choice.text }}</span> <span>{{ choice.text }}</span>
<span class="votes" v-if="showResult">({{ '%i18n:@vote-count%'.replace('{}', choice.votes) }})</span> <span class="votes" v-if="showResult">({{ $t('vote-count').replace('{}', choice.votes) }})</span>
</span> </span>
</li> </li>
</ul> </ul>
<p v-if="total > 0"> <p v-if="total > 0">
<span>{{ '%i18n:@total-users%'.replace('{}', total) }}</span> <span>{{ $t('total-users').replace('{}', total) }}</span>
<span></span> <span></span>
<a v-if="!isVoted" @click="toggleShowResult">{{ showResult ? '%i18n:@vote%' : '%i18n:@show-result%' }}</a> <a v-if="!isVoted" @click="toggleShowResult">{{ showResult ? $t('vote') : $t('show-result') }}</a>
<span v-if="isVoted">%i18n:@voted%</span> <span v-if="isVoted">{{ $t('voted') }}</span>
</p> </p>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { sum } from '../../../../../prelude/array'; import { sum } from '../../../../../prelude/array';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/poll.vue'),
props: ['note'], props: ['note'],
data() { data() {
return { return {
@@ -49,7 +51,7 @@ export default Vue.extend({
}, },
vote(id) { vote(id) {
if (this.poll.choices.some(c => c.isVoted)) return; if (this.poll.choices.some(c => c.isVoted)) return;
(this as any).api('notes/polls/vote', { this.$root.api('notes/polls/vote', {
noteId: this.note.id, noteId: this.note.id,
choice: id choice: id
}).then(() => { }).then(() => {
@@ -1,65 +1,69 @@
<template> <template>
<ui-card> <ui-card>
<div slot="title"><fa icon="user"/> %i18n:@title%</div> <div slot="title"><fa icon="user"/> {{ $t('title') }}</div>
<section class="esokaraujimuwfttfzgocmutcihewscl">
<div class="header" :style="bannerStyle">
<mk-avatar class="avatar" :user="$store.state.i" :disable-preview="true" :disable-link="true"/>
</div>
<section class="fit-top">
<ui-form :disabled="saving"> <ui-form :disabled="saving">
<ui-input v-model="name" :max="30"> <ui-input v-model="name" :max="30">
<span>%i18n:@name%</span> <span>{{ $t('name') }}</span>
</ui-input> </ui-input>
<ui-input v-model="username" readonly> <ui-input v-model="username" readonly>
<span>%i18n:@account%</span> <span>{{ $t('account') }}</span>
<span slot="prefix">@</span> <span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span> <span slot="suffix">@{{ host }}</span>
</ui-input> </ui-input>
<ui-input v-model="location"> <ui-input v-model="location">
<span>%i18n:@location%</span> <span>{{ $t('location') }}</span>
<span slot="prefix"><fa icon="map-marker-alt"/></span> <span slot="prefix"><fa icon="map-marker-alt"/></span>
</ui-input> </ui-input>
<ui-input v-model="birthday" type="date"> <ui-input v-model="birthday" type="date">
<span>%i18n:@birthday%</span> <span>{{ $t('birthday') }}</span>
<span slot="prefix"><fa icon="birthday-cake"/></span> <span slot="prefix"><fa icon="birthday-cake"/></span>
</ui-input> </ui-input>
<ui-textarea v-model="description" :max="500"> <ui-textarea v-model="description" :max="500">
<span>%i18n:@description%</span> <span>{{ $t('description') }}</span>
</ui-textarea> </ui-textarea>
<ui-input type="file" @change="onAvatarChange"> <ui-input type="file" @change="onAvatarChange">
<span>%i18n:@avatar%</span> <span>{{ $t('avatar') }}</span>
<span slot="icon"><fa icon="image"/></span> <span slot="icon"><fa icon="image"/></span>
<span slot="desc" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span> <span slot="desc" v-if="avatarUploading">{{ $t('uploading') }}<mk-ellipsis/></span>
</ui-input> </ui-input>
<ui-input type="file" @change="onBannerChange"> <ui-input type="file" @change="onBannerChange">
<span>%i18n:@banner%</span> <span>{{ $t('banner') }}</span>
<span slot="icon"><fa icon="image"/></span> <span slot="icon"><fa icon="image"/></span>
<span slot="desc" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span> <span slot="desc" v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></span>
</ui-input> </ui-input>
<ui-button @click="save(true)">%i18n:@save%</ui-button> <ui-button @click="save(true)">{{ $t('save') }}</ui-button>
</ui-form> </ui-form>
</section> </section>
<section> <section>
<header>%i18n:@advanced%</header> <header>{{ $t('advanced') }}</header>
<div> <div>
<ui-switch v-model="isCat" @change="save(false)">%i18n:@is-cat%</ui-switch> <ui-switch v-model="isCat" @change="save(false)">{{ $t('is-cat') }}</ui-switch>
<ui-switch v-model="isBot" @change="save(false)">%i18n:@is-bot%</ui-switch> <ui-switch v-model="isBot" @change="save(false)">{{ $t('is-bot') }}</ui-switch>
<ui-switch v-model="alwaysMarkNsfw">%i18n:common.always-mark-nsfw%</ui-switch> <ui-switch v-model="alwaysMarkNsfw">{{ $t('@.always-mark-nsfw') }}</ui-switch>
</div> </div>
</section> </section>
<section> <section>
<header>%i18n:@privacy%</header> <header>{{ $t('privacy') }}</header>
<div> <div>
<ui-switch v-model="isLocked" @change="save(false)">%i18n:@is-locked%</ui-switch> <ui-switch v-model="isLocked" @change="save(false)">{{ $t('is-locked') }}</ui-switch>
<ui-switch v-model="carefulBot" @change="save(false)">%i18n:@careful-bot%</ui-switch> <ui-switch v-model="carefulBot" @change="save(false)">{{ $t('careful-bot') }}</ui-switch>
</div> </div>
</section> </section>
</ui-card> </ui-card>
@@ -67,9 +71,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, host } from '../../../config'; import { apiUrl, host } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/profile-editor.vue'),
data() { data() {
return { return {
host, host,
@@ -93,7 +99,15 @@ export default Vue.extend({
computed: { computed: {
alwaysMarkNsfw: { alwaysMarkNsfw: {
get() { return this.$store.state.i.settings.alwaysMarkNsfw; }, get() { return this.$store.state.i.settings.alwaysMarkNsfw; },
set(value) { (this as any).api('i/update', { alwaysMarkNsfw: value }); } set(value) { this.$root.api('i/update', { alwaysMarkNsfw: value }); }
},
bannerStyle(): any {
if (this.$store.state.i.bannerUrl == null) return {};
return {
backgroundColor: this.$store.state.i.bannerColor && this.$store.state.i.bannerColor.length == 3 ? `rgb(${ this.$store.state.i.bannerColor.join(',') })` : null,
backgroundImage: `url(${ this.$store.state.i.bannerUrl })`
};
}, },
}, },
@@ -159,17 +173,17 @@ export default Vue.extend({
save(notify) { save(notify) {
this.saving = true; this.saving = true;
(this as any).api('i/update', { this.$root.api('i/update', {
name: this.name || null, name: this.name || null,
location: this.location || null, location: this.location || null,
description: this.description || null, description: this.description || null,
birthday: this.birthday || null, birthday: this.birthday || null,
avatarId: this.avatarId, avatarId: this.avatarId,
bannerId: this.bannerId, bannerId: this.bannerId,
isCat: this.isCat, isCat: !!this.isCat,
isBot: this.isBot, isBot: !!this.isBot,
isLocked: this.isLocked, isLocked: !!this.isLocked,
carefulBot: this.carefulBot carefulBot: !!this.carefulBot
}).then(i => { }).then(i => {
this.saving = false; this.saving = false;
this.$store.state.i.avatarId = i.avatarId; this.$store.state.i.avatarId = i.avatarId;
@@ -180,7 +194,7 @@ export default Vue.extend({
if (notify) { if (notify) {
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@saved%' text: this.$t('saved')
}); });
} }
}); });
@@ -188,3 +202,26 @@ export default Vue.extend({
} }
}); });
</script> </script>
<style lang="stylus" scoped>
.esokaraujimuwfttfzgocmutcihewscl
> .header
height 150px
overflow hidden
background-size cover
background-position center
border-radius 4px
> .avatar
position absolute
top 0
bottom 0
left 0
right 0
display block
width 72px
height 72px
margin auto
box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
</style>
@@ -1,24 +1,26 @@
<template> <template>
<span class="mk-reaction-icon"> <span class="mk-reaction-icon">
<img v-if="reaction == 'like'" src="https://twemoji.maxcdn.com/2/svg/1f44d.svg" alt="%i18n:common.reactions.like%"> <img v-if="reaction == 'like'" src="https://twemoji.maxcdn.com/2/svg/1f44d.svg" :alt="$t('@.reactions.like')">
<img v-if="reaction == 'love'" src="https://twemoji.maxcdn.com/2/svg/2764.svg" alt="%i18n:common.reactions.love%"> <img v-if="reaction == 'love'" src="https://twemoji.maxcdn.com/2/svg/2764.svg" :alt="$t('@.reactions.love')">
<img v-if="reaction == 'laugh'" src="https://twemoji.maxcdn.com/2/svg/1f606.svg" alt="%i18n:common.reactions.laugh%"> <img v-if="reaction == 'laugh'" src="https://twemoji.maxcdn.com/2/svg/1f606.svg" :alt="$t('@.reactions.laugh')">
<img v-if="reaction == 'hmm'" src="https://twemoji.maxcdn.com/2/svg/1f914.svg" alt="%i18n:common.reactions.hmm%"> <img v-if="reaction == 'hmm'" src="https://twemoji.maxcdn.com/2/svg/1f914.svg" :alt="$t('@.reactions.hmm')">
<img v-if="reaction == 'surprise'" src="https://twemoji.maxcdn.com/2/svg/1f62e.svg" alt="%i18n:common.reactions.surprise%"> <img v-if="reaction == 'surprise'" src="https://twemoji.maxcdn.com/2/svg/1f62e.svg" :alt="$t('@.reactions.surprise')">
<img v-if="reaction == 'congrats'" src="https://twemoji.maxcdn.com/2/svg/1f389.svg" alt="%i18n:common.reactions.congrats%"> <img v-if="reaction == 'congrats'" src="https://twemoji.maxcdn.com/2/svg/1f389.svg" :alt="$t('@.reactions.congrats')">
<img v-if="reaction == 'angry'" src="https://twemoji.maxcdn.com/2/svg/1f4a2.svg" alt="%i18n:common.reactions.angry%"> <img v-if="reaction == 'angry'" src="https://twemoji.maxcdn.com/2/svg/1f4a2.svg" :alt="$t('@.reactions.angry')">
<img v-if="reaction == 'confused'" src="https://twemoji.maxcdn.com/2/svg/1f625.svg" alt="%i18n:common.reactions.confused%"> <img v-if="reaction == 'confused'" src="https://twemoji.maxcdn.com/2/svg/1f625.svg" :alt="$t('@.reactions.confused')">
<img v-if="reaction == 'rip'" src="https://twemoji.maxcdn.com/2/svg/1f607.svg" alt="%i18n:common.reactions.rip%"> <img v-if="reaction == 'rip'" src="https://twemoji.maxcdn.com/2/svg/1f607.svg" :alt="$t('@.reactions.rip')">
<template v-if="reaction == 'pudding'"> <template v-if="reaction == 'pudding'">
<img v-if="$store.getters.isSignedIn && $store.state.settings.iLikeSushi" src="https://twemoji.maxcdn.com/2/svg/1f363.svg" alt="%i18n:common.reactions.pudding%"> <img v-if="$store.getters.isSignedIn && $store.state.settings.iLikeSushi" src="https://twemoji.maxcdn.com/2/svg/1f363.svg" :alt="$t('@.reactions.pudding')">
<img v-else src="https://twemoji.maxcdn.com/2/svg/1f36e.svg" alt="%i18n:common.reactions.pudding%"> <img v-else src="https://twemoji.maxcdn.com/2/svg/1f36e.svg" :alt="$t('@.reactions.pudding')">
</template> </template>
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n(),
props: ['reaction'] props: ['reaction']
}); });
</script> </script>
@@ -4,16 +4,16 @@
<div class="popover" :class="{ compact, big }" ref="popover"> <div class="popover" :class="{ compact, big }" ref="popover">
<p v-if="!compact">{{ title }}</p> <p v-if="!compact">{{ title }}</p>
<div ref="buttons" :class="{ showFocus }"> <div ref="buttons" :class="{ showFocus }">
<button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" title="%i18n:common.reactions.like%"><mk-reaction-icon reaction='like'/></button> <button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" :title="$t('@.reactions.like')"><mk-reaction-icon reaction='like'/></button>
<button @click="react('love')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="2" title="%i18n:common.reactions.love%"><mk-reaction-icon reaction='love'/></button> <button @click="react('love')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="2" :title="$t('@.reactions.love')"><mk-reaction-icon reaction='love'/></button>
<button @click="react('laugh')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="3" title="%i18n:common.reactions.laugh%"><mk-reaction-icon reaction='laugh'/></button> <button @click="react('laugh')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="3" :title="$t('@.reactions.laugh')"><mk-reaction-icon reaction='laugh'/></button>
<button @click="react('hmm')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="4" title="%i18n:common.reactions.hmm%"><mk-reaction-icon reaction='hmm'/></button> <button @click="react('hmm')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="4" :title="$t('@.reactions.hmm')"><mk-reaction-icon reaction='hmm'/></button>
<button @click="react('surprise')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="5" title="%i18n:common.reactions.surprise%"><mk-reaction-icon reaction='surprise'/></button> <button @click="react('surprise')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="5" :title="$t('@.reactions.surprise')"><mk-reaction-icon reaction='surprise'/></button>
<button @click="react('congrats')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="6" title="%i18n:common.reactions.congrats%"><mk-reaction-icon reaction='congrats'/></button> <button @click="react('congrats')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="6" :title="$t('@.reactions.congrats')"><mk-reaction-icon reaction='congrats'/></button>
<button @click="react('angry')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="7" title="%i18n:common.reactions.angry%"><mk-reaction-icon reaction='angry'/></button> <button @click="react('angry')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="7" :title="$t('@.reactions.angry')"><mk-reaction-icon reaction='angry'/></button>
<button @click="react('confused')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="8" title="%i18n:common.reactions.confused%"><mk-reaction-icon reaction='confused'/></button> <button @click="react('confused')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="8" :title="$t('@.reactions.confused')"><mk-reaction-icon reaction='confused'/></button>
<button @click="react('rip')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="9" title="%i18n:common.reactions.rip%"><mk-reaction-icon reaction='rip'/></button> <button @click="react('rip')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="9" :title="$t('@.reactions.rip')"><mk-reaction-icon reaction='rip'/></button>
<button @click="react('pudding')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="10" title="%i18n:common.reactions.pudding%"><mk-reaction-icon reaction='pudding'/></button> <button @click="react('pudding')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="10" :title="$t('@.reactions.pudding')"><mk-reaction-icon reaction='pudding'/></button>
</div> </div>
</div> </div>
</div> </div>
@@ -21,11 +21,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as anime from 'animejs'; import * as anime from 'animejs';
const placeholder = '%i18n:@choose-reaction%';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/reaction-picker.vue'),
props: { props: {
note: { note: {
type: Object, type: Object,
@@ -67,7 +67,7 @@ export default Vue.extend({
data() { data() {
return { return {
title: placeholder, title: this.$t('choose-reaction'),
focus: null focus: null
}; };
}, },
@@ -145,7 +145,7 @@ export default Vue.extend({
methods: { methods: {
react(reaction) { react(reaction) {
(this as any).api('notes/reactions/create', { this.$root.api('notes/reactions/create', {
noteId: this.note.id, noteId: this.note.id,
reaction: reaction reaction: reaction
}).then(() => { }).then(() => {
@@ -160,7 +160,7 @@ export default Vue.extend({
}, },
onMouseout(e) { onMouseout(e) {
this.title = placeholder; this.title = this.$t('choose-reaction');
}, },
close() { close() {
@@ -17,6 +17,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
export default Vue.extend({ export default Vue.extend({
props: ['note'], props: ['note'],
computed: { computed: {
@@ -26,7 +27,7 @@ export default Vue.extend({
}, },
methods: { methods: {
react(reaction: string) { react(reaction: string) {
(this as any).api('notes/reactions/create', { this.$root.api('notes/reactions/create', {
noteId: this.note.id, noteId: this.note.id,
reaction: reaction reaction: reaction
}); });
@@ -2,26 +2,28 @@
<form class="mk-signin" :class="{ signing }" @submit.prevent="onSubmit"> <form class="mk-signin" :class="{ signing }" @submit.prevent="onSubmit">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div> <div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange" styl="fill"> <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange" styl="fill">
<span>%i18n:@username%</span> <span>{{ $t('username') }}</span>
<span slot="prefix">@</span> <span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span> <span slot="suffix">@{{ host }}</span>
</ui-input> </ui-input>
<ui-input v-model="password" type="password" required styl="fill"> <ui-input v-model="password" type="password" required styl="fill">
<span>%i18n:@password%</span> <span>{{ $t('password') }}</span>
<span slot="prefix"><fa icon="lock"/></span> <span slot="prefix"><fa icon="lock"/></span>
</ui-input> </ui-input>
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill"/> <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> <ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('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;"><a :href="`${apiUrl}/signin/twitter`">{{ $t('signin-with-twitter') }}</a></p>
<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/github`">%i18n:@signin-with-github%</a></p> <p style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`">{{ $t('signin-with-github') }}</a></p>
</form> </form>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, host } from '../../../config'; import { apiUrl, host } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/signin.vue'),
props: { props: {
withAvatar: { withAvatar: {
type: Boolean, type: Boolean,
@@ -42,7 +44,7 @@ export default Vue.extend({
}, },
methods: { methods: {
onUsernameChange() { onUsernameChange() {
(this as any).api('users/show', { this.$root.api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.user = user; this.user = user;
@@ -53,14 +55,14 @@ export default Vue.extend({
onSubmit() { onSubmit() {
this.signing = true; this.signing = true;
(this as any).api('signin', { this.$root.api('signin', {
username: this.username, username: this.username,
password: this.password, password: this.password,
token: this.user && this.user.twoFactorEnabled ? this.token : undefined token: this.user && this.user.twoFactorEnabled ? this.token : undefined
}, true).then(() => { }, true).then(() => {
location.reload(); location.reload();
}).catch(() => { }).catch(() => {
alert('%i18n:@login-failed%'); alert(this.$t('login-failed'));
this.signing = false; this.signing = false;
}); });
} }
@@ -2,51 +2,53 @@
<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()"> <form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
<template v-if="meta"> <template v-if="meta">
<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill"> <ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill">
<span>%i18n:@invitation-code%</span> <span>{{ $t('invitation-code') }}</span>
<span slot="prefix"><fa icon="id-card-alt"/></span> <span slot="prefix"><fa icon="id-card-alt"/></span>
<p slot="desc" v-html="'%i18n:@invitation-info%'.replace('{}', 'mailto:' + meta.maintainer.email)"></p> <p slot="desc" v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainer.email)"></p>
</ui-input> </ui-input>
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill"> <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill">
<span>%i18n:@username%</span> <span>{{ $t('username') }}</span>
<span slot="prefix">@</span> <span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span> <span slot="suffix">@{{ host }}</span>
<p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> %i18n:@checking%</p> <p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> {{ $t('checking') }}</p>
<p slot="desc" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@available%</p> <p slot="desc" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</p>
<p slot="desc" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@unavailable%</p> <p slot="desc" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</p>
<p slot="desc" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@error%</p> <p slot="desc" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</p>
<p slot="desc" v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@invalid-format%</p> <p slot="desc" v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('invalid-format') }}</p>
<p slot="desc" v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-short%</p> <p slot="desc" v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-short') }}</p>
<p slot="desc" v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-long%</p> <p slot="desc" v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-long') }}</p>
</ui-input> </ui-input>
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill"> <ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill">
<span>%i18n:@password%</span> <span>{{ $t('password') }}</span>
<span slot="prefix"><fa icon="lock"/></span> <span slot="prefix"><fa icon="lock"/></span>
<div slot="desc"> <div slot="desc">
<p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@weak-password%</p> <p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('weak-password') }}</p>
<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@normal-password%</p> <p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('normal-password') }}</p>
<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@strong-password%</p> <p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('strong-password') }}</p>
</div> </div>
</ui-input> </ui-input>
<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill"> <ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill">
<span>%i18n:@password% (%i18n:@retype%)</span> <span>{{ $t('password') }} ({{ $t('retype') }})</span>
<span slot="prefix"><fa icon="lock"/></span> <span slot="prefix"><fa icon="lock"/></span>
<div slot="desc"> <div slot="desc">
<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@password-matched%</p> <p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('password-matched') }}</p>
<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@password-not-matched%</p> <p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('password-not-matched') }}</p>
</div> </div>
</ui-input> </ui-input>
<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div> <div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>
<ui-button type="submit">%i18n:@create%</ui-button> <ui-button type="submit">{{ $t('create') }}</ui-button>
</template> </template>
</form> </form>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
const getPasswordStrength = require('syuilo-password-strength'); const getPasswordStrength = require('syuilo-password-strength');
import { host, url } from '../../../config'; import { host, url } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/signup.vue'),
data() { data() {
return { return {
host, host,
@@ -70,7 +72,7 @@ export default Vue.extend({
} }
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.meta = meta; this.meta = meta;
}); });
}, },
@@ -100,7 +102,7 @@ export default Vue.extend({
this.usernameState = 'wait'; this.usernameState = 'wait';
(this as any).api('username/available', { this.$root.api('username/available', {
username: this.username username: this.username
}).then(result => { }).then(result => {
this.usernameState = result.available ? 'ok' : 'unavailable'; this.usernameState = result.available ? 'ok' : 'unavailable';
@@ -126,20 +128,20 @@ export default Vue.extend({
this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match'; this.passwordRetypeState = this.password == this.retypedPassword ? 'match' : 'not-match';
}, },
onSubmit() { onSubmit() {
(this as any).api('signup', { this.$root.api('signup', {
username: this.username, username: this.username,
password: this.password, password: this.password,
invitationCode: this.invitationCode, invitationCode: this.invitationCode,
'g-recaptcha-response': this.meta.enableRecaptcha ? (window as any).grecaptcha.getResponse() : null 'g-recaptcha-response': this.meta.enableRecaptcha ? (window as any).grecaptcha.getResponse() : null
}, true).then(() => { }, true).then(() => {
(this as any).api('signin', { this.$root.api('signin', {
username: this.username, username: this.username,
password: this.password password: this.password
}, true).then(() => { }, true).then(() => {
location.href = '/'; location.href = '/';
}); });
}).catch(() => { }).catch(() => {
alert('%i18n:@some-error%'); alert(this.$t('some-error'));
if (this.meta.enableRecaptcha) { if (this.meta.enableRecaptcha) {
(window as any).grecaptcha.reset(); (window as any).grecaptcha.reset();
@@ -1,42 +0,0 @@
<template>
<div class="mk-special-message">
<p v-if="m == 1 && d == 1">%i18n:@new-year%</p>
<p v-if="m == 12 && d == 25">%i18n:@christmas%</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
data() {
return {
now: new Date()
};
},
computed: {
d(): number {
return this.now.getDate();
},
m(): number {
return this.now.getMonth() + 1;
}
}
});
</script>
<style lang="stylus" scoped>
.mk-special-message
&:empty
display none
> p
margin 0
padding 4px
text-align center
font-size 14px
font-weight bold
text-transform uppercase
color #fff
background #ff1036
</style>
@@ -2,32 +2,34 @@
<div class="mk-stream-indicator"> <div class="mk-stream-indicator">
<p v-if="stream.state == 'initializing'"> <p v-if="stream.state == 'initializing'">
<fa icon="spinner .pulse"/> <fa icon="spinner .pulse"/>
<span>%i18n:@connecting%<mk-ellipsis/></span> <span>{{ $t('connecting') }}<mk-ellipsis/></span>
</p> </p>
<p v-if="stream.state == 'reconnecting'"> <p v-if="stream.state == 'reconnecting'">
<fa icon="spinner .pulse"/> <fa icon="spinner .pulse"/>
<span>%i18n:@reconnecting%<mk-ellipsis/></span> <span>{{ $t('reconnecting') }}<mk-ellipsis/></span>
</p> </p>
<p v-if="stream.state == 'connected'"> <p v-if="stream.state == 'connected'">
<fa icon="check"/> <fa icon="check"/>
<span>%i18n:@connected%</span> <span>{{ $t('connected') }}</span>
</p> </p>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as anime from 'animejs'; import * as anime from 'animejs';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/stream-indicator.vue'),
computed: { computed: {
stream() { stream() {
return (this as any).os.stream; return this.$root.stream;
} }
}, },
created() { created() {
(this as any).os.stream.on('_connected_', this.onConnected); this.$root.stream.on('_connected_', this.onConnected);
(this as any).os.stream.on('_disconnected_', this.onDisconnected); this.$root.stream.on('_disconnected_', this.onDisconnected);
this.$nextTick(() => { this.$nextTick(() => {
if (this.stream.state == 'connected') { if (this.stream.state == 'connected') {
@@ -36,8 +38,8 @@ export default Vue.extend({
}); });
}, },
beforeDestroy() { beforeDestroy() {
(this as any).os.stream.off('_connected_', this.onConnected); this.$root.stream.off('_connected_', this.onConnected);
(this as any).os.stream.off('_disconnected_', this.onDisconnected); this.$root.stream.off('_disconnected_', this.onDisconnected);
}, },
methods: { methods: {
onConnected() { onConnected() {
@@ -1,7 +1,7 @@
<template> <template>
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo"> <div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p> <p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p> <p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
<div v-else> <div v-else>
<vue-word-cloud <vue-word-cloud
:words="tags.slice(0, 20).map(x => [x.name, x.count])" :words="tags.slice(0, 20).map(x => [x.name, x.count])"
@@ -19,9 +19,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as VueWordCloud from 'vuewordcloud'; import * as VueWordCloud from 'vuewordcloud';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/tag-cloud.vue'),
components: { components: {
[VueWordCloud.name]: VueWordCloud [VueWordCloud.name]: VueWordCloud
}, },
@@ -41,7 +43,7 @@ export default Vue.extend({
}, },
methods: { methods: {
fetch() { fetch() {
(this as any).api('aggregation/hashtags').then(tags => { this.$root.api('aggregation/hashtags').then(tags => {
this.tags = tags; this.tags = tags;
this.fetching = false; this.fetching = false;
}); });
@@ -1,96 +1,96 @@
<template> <template>
<div class="nicnklzforebnpfgasiypmpdaaglujqm"> <div class="nicnklzforebnpfgasiypmpdaaglujqm">
<label> <label>
<span>%i18n:@light-theme%</span> <span>{{ $t('light-theme') }}</span>
<ui-select v-model="light" placeholder="%i18n:@light-theme%"> <ui-select v-model="light" :placeholder="$t('light-theme')">
<optgroup label="%i18n:@light-themes%"> <optgroup :label="$t('light-themes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@dark-themes%"> <optgroup :label="$t('dark-themes')">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
</ui-select> </ui-select>
</label> </label>
<label> <label>
<span>%i18n:@dark-theme%</span> <span>{{ $t('dark-theme') }}</span>
<ui-select v-model="dark" placeholder="%i18n:@dark-theme%"> <ui-select v-model="dark" :placeholder="$t('dark-theme')">
<optgroup label="%i18n:@dark-themes%"> <optgroup :label="$t('dark-themes')">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@light-themes%"> <optgroup :label="$t('light-themes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
</ui-select> </ui-select>
</label> </label>
<details class="creator"> <details class="creator">
<summary><fa icon="palette"/> %i18n:@create-a-theme%</summary> <summary><fa icon="palette"/> {{ $t('create-a-theme') }}</summary>
<div> <div>
<span>%i18n:@base-theme%:</span> <span>{{ $t('base-theme') }}:</span>
<ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio> <ui-radio v-model="myThemeBase" value="light">{{ $t('base-theme-light') }}</ui-radio>
<ui-radio v-model="myThemeBase" value="dark">%i18n:@base-theme-dark%</ui-radio> <ui-radio v-model="myThemeBase" value="dark">{{ $t('base-theme-dark') }}</ui-radio>
</div> </div>
<div> <div>
<ui-input v-model="myThemeName"> <ui-input v-model="myThemeName">
<span>%i18n:@theme-name%</span> <span>{{ $t('theme-name') }}</span>
</ui-input> </ui-input>
<ui-textarea v-model="myThemeDesc"> <ui-textarea v-model="myThemeDesc">
<span>%i18n:@desc%</span> <span>{{ $t('desc') }}</span>
</ui-textarea> </ui-textarea>
</div> </div>
<div> <div>
<div style="padding-bottom:8px;">%i18n:@primary-color%:</div> <div style="padding-bottom:8px;">{{ $t('primary-color') }}:</div>
<color-picker v-model="myThemePrimary"/> <color-picker v-model="myThemePrimary"/>
</div> </div>
<div> <div>
<div style="padding-bottom:8px;">%i18n:@secondary-color%:</div> <div style="padding-bottom:8px;">{{ $t('secondary-color') }}:</div>
<color-picker v-model="myThemeSecondary"/> <color-picker v-model="myThemeSecondary"/>
</div> </div>
<div> <div>
<div style="padding-bottom:8px;">%i18n:@text-color%:</div> <div style="padding-bottom:8px;">{{ $t('text-color') }}:</div>
<color-picker v-model="myThemeText"/> <color-picker v-model="myThemeText"/>
</div> </div>
<ui-button @click="preview()"><fa icon="eye"/> %i18n:@preview-created-theme%</ui-button> <ui-button @click="preview()"><fa icon="eye"/> {{ $t('preview-created-theme') }}</ui-button>
<ui-button primary @click="gen()"><fa :icon="['far', 'save']"/> %i18n:@save-created-theme%</ui-button> <ui-button primary @click="gen()"><fa :icon="['far', 'save']"/> {{ $t('save-created-theme') }}</ui-button>
</details> </details>
<details> <details>
<summary><fa icon="download"/> %i18n:@install-a-theme%</summary> <summary><fa icon="download"/> {{ $t('install-a-theme') }}</summary>
<ui-button @click="import_()"><fa icon="file-import"/> %i18n:@import%</ui-button> <ui-button @click="import_()"><fa icon="file-import"/> {{ $t('import') }}</ui-button>
<input ref="file" type="file" accept=".misskeytheme" style="display:none;" @change="onUpdateImportFile"/> <input ref="file" type="file" accept=".misskeytheme" style="display:none;" @change="onUpdateImportFile"/>
<p>%i18n:@import-by-code%:</p> <p>{{ $t('import-by-code') }}:</p>
<ui-textarea v-model="installThemeCode"> <ui-textarea v-model="installThemeCode">
<span>%i18n:@theme-code%</span> <span>{{ $t('theme-code') }}</span>
</ui-textarea> </ui-textarea>
<ui-button @click="() => install(this.installThemeCode)"><fa icon="check"/> %i18n:@install%</ui-button> <ui-button @click="() => install(this.installThemeCode)"><fa icon="check"/> {{ $t('install') }}</ui-button>
</details> </details>
<details> <details>
<summary><fa icon="folder-open"/> %i18n:@manage-themes%</summary> <summary><fa icon="folder-open"/> {{ $t('manage-themes') }}</summary>
<ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%"> <ui-select v-model="selectedThemeId" :placeholder="$t('select-theme')">
<optgroup label="%i18n:@builtin-themes%"> <optgroup :label="$t('builtin-themes')">
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@my-themes%"> <optgroup :label="$t('my-themes')">
<option v-for="x in installedThemes.filter(t => t.author == this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in installedThemes.filter(t => t.author == this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
<optgroup label="%i18n:@installed-themes%"> <optgroup :label="$t('installed-themes')">
<option v-for="x in installedThemes.filter(t => t.author != this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option> <option v-for="x in installedThemes.filter(t => t.author != this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup> </optgroup>
</ui-select> </ui-select>
<template v-if="selectedTheme"> <template v-if="selectedTheme">
<ui-input readonly :value="selectedTheme.author"> <ui-input readonly :value="selectedTheme.author">
<span>%i18n:@author%</span> <span>{{ $t('author') }}</span>
</ui-input> </ui-input>
<ui-textarea v-if="selectedTheme.desc" readonly :value="selectedTheme.desc"> <ui-textarea v-if="selectedTheme.desc" readonly :value="selectedTheme.desc">
<span>%i18n:@desc%</span> <span>{{ $t('desc') }}</span>
</ui-textarea> </ui-textarea>
<ui-textarea readonly :value="selectedThemeCode"> <ui-textarea readonly :value="selectedThemeCode">
<span>%i18n:@theme-code%</span> <span>{{ $t('theme-code') }}</span>
</ui-textarea> </ui-textarea>
<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export"><fa icon="box"/> %i18n:@export%</ui-button> <ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export"><fa icon="box"/> {{ $t('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> <ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><fa :icon="['far', 'trash-alt']"/> {{ $t('uninstall') }}</ui-button>
</template> </template>
</details> </details>
</div> </div>
@@ -98,6 +98,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { lightTheme, darkTheme, builtinThemes, applyTheme, Theme } from '../../../theme'; import { lightTheme, darkTheme, builtinThemes, applyTheme, Theme } from '../../../theme';
import { Chrome } from 'vue-color'; import { Chrome } from 'vue-color';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
@@ -119,6 +120,7 @@ function convertOldThemedefinition(t) {
} }
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/theme.vue'),
components: { components: {
ColorPicker: Chrome ColorPicker: Chrome
}, },
@@ -221,7 +223,7 @@ export default Vue.extend({
} catch (e) { } catch (e) {
this.$swal({ this.$swal({
type: 'error', type: 'error',
text: '%i18n:@invalid-theme%' text: this.$t('invalid-theme')
}); });
return; return;
} }
@@ -234,7 +236,7 @@ export default Vue.extend({
if (theme.id == null) { if (theme.id == null) {
this.$swal({ this.$swal({
type: 'error', type: 'error',
text: '%i18n:@invalid-theme%' text: this.$t('invalid-theme')
}); });
return; return;
} }
@@ -242,7 +244,7 @@ export default Vue.extend({
if (this.$store.state.device.themes.some(t => t.id == theme.id)) { if (this.$store.state.device.themes.some(t => t.id == theme.id)) {
this.$swal({ this.$swal({
type: 'info', type: 'info',
text: '%i18n:@already-installed%' text: this.$t('already-installed')
}); });
return; return;
} }
@@ -254,7 +256,7 @@ export default Vue.extend({
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@installed%'.replace('{}', theme.name) text: this.$t('installed').replace('{}', theme.name)
}); });
}, },
@@ -267,7 +269,7 @@ export default Vue.extend({
this.$swal({ this.$swal({
type: 'info', type: 'info',
text: '%i18n:@uninstalled%'.replace('{}', theme.name) text: this.$t('uninstalled').replace('{}', theme.name)
}); });
}, },
@@ -304,7 +306,7 @@ export default Vue.extend({
if (theme.name == null || theme.name.trim() == '') { if (theme.name == null || theme.name.trim() == '') {
this.$swal({ this.$swal({
type: 'warning', type: 'warning',
text: '%i18n:@theme-name-required%' text: this.$t('theme-name-required')
}); });
return; return;
} }
@@ -318,7 +320,7 @@ export default Vue.extend({
this.$swal({ this.$swal({
type: 'success', type: 'success',
text: '%i18n:@saved%' text: this.$t('saved')
}); });
} }
} }
+12 -10
View File
@@ -8,8 +8,10 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n(),
props: { props: {
time: { time: {
type: [Date, String], type: [Date, String],
@@ -44,16 +46,16 @@ export default Vue.extend({
const time = this._time; const time = this._time;
const ago = (this.now.getTime() - time.getTime()) / 1000/*ms*/; const ago = (this.now.getTime() - time.getTime()) / 1000/*ms*/;
return ( return (
ago >= 31536000 ? '%i18n:common.time.years_ago%' .replace('{}', (~~(ago / 31536000)).toString()) : ago >= 31536000 ? this.$t('@.time.years_ago') .replace('{}', (~~(ago / 31536000)).toString()) :
ago >= 2592000 ? '%i18n:common.time.months_ago%' .replace('{}', (~~(ago / 2592000)).toString()) : ago >= 2592000 ? this.$t('@.time.months_ago') .replace('{}', (~~(ago / 2592000)).toString()) :
ago >= 604800 ? '%i18n:common.time.weeks_ago%' .replace('{}', (~~(ago / 604800)).toString()) : ago >= 604800 ? this.$t('@.time.weeks_ago') .replace('{}', (~~(ago / 604800)).toString()) :
ago >= 86400 ? '%i18n:common.time.days_ago%' .replace('{}', (~~(ago / 86400)).toString()) : ago >= 86400 ? this.$t('@.time.days_ago') .replace('{}', (~~(ago / 86400)).toString()) :
ago >= 3600 ? '%i18n:common.time.hours_ago%' .replace('{}', (~~(ago / 3600)).toString()) : ago >= 3600 ? this.$t('@.time.hours_ago') .replace('{}', (~~(ago / 3600)).toString()) :
ago >= 60 ? '%i18n:common.time.minutes_ago%'.replace('{}', (~~(ago / 60)).toString()) : ago >= 60 ? this.$t('@.time.minutes_ago').replace('{}', (~~(ago / 60)).toString()) :
ago >= 10 ? '%i18n:common.time.seconds_ago%'.replace('{}', (~~(ago % 60)).toString()) : ago >= 10 ? this.$t('@.time.seconds_ago').replace('{}', (~~(ago % 60)).toString()) :
ago >= 0 ? '%i18n:common.time.just_now%' : ago >= 0 ? this.$t('@.time.just_now') :
ago < 0 ? '%i18n:common.time.future%' : ago < 0 ? this.$t('@.time.future') :
'%i18n:common.time.unknown%'); this.$t('@.time.unknown'));
} }
}, },
created() { created() {
@@ -1,13 +1,13 @@
<template> <template>
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc"> <div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p> <p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p> <p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
<!-- トランジションを有効にするとなぜかメモリリークする --> <!-- トランジションを有効にするとなぜかメモリリークする -->
<transition-group v-else tag="div" name="chart"> <transition-group v-else tag="div" name="chart">
<div v-for="stat in stats" :key="stat.tag"> <div v-for="stat in stats" :key="stat.tag">
<div class="tag"> <div class="tag">
<router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link> <router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
<p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p> <p>{{ this.$t('count').replace('{}', stat.usersCount) }}</p>
</div> </div>
<x-chart class="chart" :src="stat.chart"/> <x-chart class="chart" :src="stat.chart"/>
</div> </div>
@@ -17,9 +17,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import XChart from './trends.chart.vue'; import XChart from './trends.chart.vue';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/trends.vue'),
components: { components: {
XChart XChart
}, },
@@ -39,7 +41,7 @@ export default Vue.extend({
}, },
methods: { methods: {
fetch() { fetch() {
(this as any).api('hashtags/trend').then(stats => { this.$root.api('hashtags/trend').then(stats => {
this.stats = stats; this.stats = stats;
this.fetching = false; this.fetching = false;
}); });
@@ -1,11 +1,11 @@
<template> <template>
<div class="mk-twitter-setting"> <div class="mk-twitter-setting">
<p>%i18n:@description%<a :href="`${docsUrl}/link-to-twitter`" target="_blank">%i18n:@detail%</a></p> <p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-twitter`" target="_blank">{{ $t('detail') }}</a></p>
<p class="account" v-if="$store.state.i.twitter" :title="`Twitter ID: ${$store.state.i.twitter.userId}`">%i18n:@connected-to%: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p> <p class="account" v-if="$store.state.i.twitter" :title="`Twitter ID: ${$store.state.i.twitter.userId}`">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
<p> <p>
<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ $store.state.i.twitter ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a> <a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ $store.state.i.twitter ? this.$t('reconnect') : this.$t('connect') }}</a>
<span v-if="$store.state.i.twitter"> or </span> <span v-if="$store.state.i.twitter"> or </span>
<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter" @click.prevent="disconnect">%i18n:@disconnect%</a> <a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="$store.state.i.twitter" @click.prevent="disconnect">{{ $t('disconnect') }}</a>
</p> </p>
<p class="id" v-if="$store.state.i.twitter">Twitter ID: {{ $store.state.i.twitter.userId }}</p> <p class="id" v-if="$store.state.i.twitter">Twitter ID: {{ $store.state.i.twitter.userId }}</p>
</div> </div>
@@ -13,9 +13,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, docsUrl } from '../../../config'; import { apiUrl, docsUrl } from '../../../config';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/twitter-setting.vue'),
data() { data() {
return { return {
form: null, form: null,
@@ -53,7 +53,7 @@ export default Vue.extend({
display block display block
width 100% width 100%
margin 0 margin 0
padding 8px padding 8px 10px
text-align center text-align center
font-weight normal font-weight normal
font-size 16px font-size 16px
@@ -19,7 +19,6 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
const getPasswordStrength = require('syuilo-password-strength');
export default Vue.extend({ export default Vue.extend({
props: { props: {
@@ -5,7 +5,7 @@
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div> <div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p> <p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
<p class="status"> <p class="status">
<span class="initing" v-if="ctx.progress == undefined">%i18n:@waiting%<mk-ellipsis/></span> <span class="initing" v-if="ctx.progress == undefined">{{ $t('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> <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>
<span class="percentage" v-if="ctx.progress != undefined">{{ Math.floor((ctx.progress.value / ctx.progress.max) * 100) }}</span> <span class="percentage" v-if="ctx.progress != undefined">{{ Math.floor((ctx.progress.value / ctx.progress.max) * 100) }}</span>
</p> </p>
@@ -19,10 +19,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl } from '../../../config'; import { apiUrl } from '../../../config';
import getMD5 from '../../scripts/get-md5'; import getMD5 from '../../scripts/get-md5';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/uploader.vue'),
data() { data() {
return { return {
uploads: [] uploads: []
@@ -34,7 +36,7 @@ export default Vue.extend({
const data = new FormData(); const data = new FormData();
data.append('md5', getMD5(fileData)); data.append('md5', getMD5(fileData));
(this as any).api('drive/files/check_existence', { this.$root.api('drive/files/check_existence', {
md5: getMD5(fileData) md5: getMD5(fileData)
}).then(resp => { }).then(resp => {
resolve(resp.file); resolve(resp.file);
@@ -13,6 +13,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import { toUnicode as decodePunycode } from 'punycode'; import { toUnicode as decodePunycode } from 'punycode';
export default Vue.extend({ export default Vue.extend({
props: ['url', 'target'], props: ['url', 'target'],
data() { data() {
@@ -5,34 +5,34 @@
<div @click="choose('public')" :class="{ active: v == 'public' }"> <div @click="choose('public')" :class="{ active: v == 'public' }">
<div><fa icon="globe"/></div> <div><fa icon="globe"/></div>
<div> <div>
<span>%i18n:@public%</span> <span>{{ $t('public') }}</span>
</div> </div>
</div> </div>
<div @click="choose('home')" :class="{ active: v == 'home' }"> <div @click="choose('home')" :class="{ active: v == 'home' }">
<div><fa icon="home"/></div> <div><fa icon="home"/></div>
<div> <div>
<span>%i18n:@home%</span> <span>{{ $t('home') }}</span>
<span>%i18n:@home-desc%</span> <span>{{ $t('home-desc') }}</span>
</div> </div>
</div> </div>
<div @click="choose('followers')" :class="{ active: v == 'followers' }"> <div @click="choose('followers')" :class="{ active: v == 'followers' }">
<div><fa icon="unlock"/></div> <div><fa icon="unlock"/></div>
<div> <div>
<span>%i18n:@followers%</span> <span>{{ $t('followers') }}</span>
<span>%i18n:@followers-desc%</span> <span>{{ $t('followers-desc') }}</span>
</div> </div>
</div> </div>
<div @click="choose('specified')" :class="{ active: v == 'specified' }"> <div @click="choose('specified')" :class="{ active: v == 'specified' }">
<div><fa icon="envelope"/></div> <div><fa icon="envelope"/></div>
<div> <div>
<span>%i18n:@specified%</span> <span>{{ $t('specified') }}</span>
<span>%i18n:@specified-desc%</span> <span>{{ $t('specified-desc') }}</span>
</div> </div>
</div> </div>
<div @click="choose('private')" :class="{ active: v == 'private' }"> <div @click="choose('private')" :class="{ active: v == 'private' }">
<div><fa icon="lock"/></div> <div><fa icon="lock"/></div>
<div> <div>
<span>%i18n:@private%</span> <span>{{ $t('private') }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -41,9 +41,11 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import * as anime from 'animejs'; import * as anime from 'animejs';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/visibility-chooser.vue'),
props: ['source', 'compact'], props: ['source', 'compact'],
data() { data() {
return { return {
@@ -45,7 +45,7 @@ export default Vue.extend({
mounted() { mounted() {
this.fetch(); this.fetch();
this.connection = (this as any).os.stream.useSharedConnection('localTimeline'); this.connection = this.$root.stream.useSharedConnection('localTimeline');
this.connection.on('note', this.onNote); this.connection.on('note', this.onNote);
}, },
@@ -57,7 +57,7 @@ export default Vue.extend({
methods: { methods: {
fetch(cb?) { fetch(cb?) {
this.fetching = true; this.fetching = true;
(this as any).api('notes', { this.$root.api('notes', {
limit: this.max, limit: this.max,
local: true, local: true,
reply: false, reply: false,
+13 -11
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="syxhndwprovvuqhmyvveewmbqayniwkv" v-if="!fetching"> <div class="syxhndwprovvuqhmyvveewmbqayniwkv" v-if="!fetching">
<div class="signed-in-as" v-html="'%i18n:@signed-in-as%'.replace('{}', `<b>${myName}`)"></div> <div class="signed-in-as" v-html="this.$t('signed-in-as').replace('{}', `<b>${myName}`)"></div>
<main> <main>
<div class="banner" :style="bannerStyle"></div> <div class="banner" :style="bannerStyle"></div>
@@ -19,11 +19,11 @@
@click="onClick" @click="onClick"
:disabled="followWait"> :disabled="followWait">
<template v-if="!followWait"> <template v-if="!followWait">
<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked"><fa icon="hourglass-half"/> %i18n:@request-pending%</template> <template v-if="user.hasPendingFollowRequestFromYou && user.isLocked"><fa icon="hourglass-half"/> {{ $t('request-pending') }}</template>
<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked"><fa icon="hourglass-start"/> %i18n:@follow-processing%</template> <template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked"><fa icon="hourglass-start"/> {{ $t('follow-processing') }}</template>
<template v-else-if="user.isFollowing"><fa icon="minus"/> %i18n:@following%</template> <template v-else-if="user.isFollowing"><fa icon="minus"/> {{ $t('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"/> {{ $t('follow-request') }}</template>
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> %i18n:@follow%</template> <template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
</template> </template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template> <template v-else><fa icon="spinner .pulse" fixed-width/></template>
</button> </button>
@@ -32,10 +32,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
import parseAcct from '../../../../../misc/acct/parse'; import parseAcct from '../../../../../misc/acct/parse';
import Progress from '../../../common/scripts/loading'; import Progress from '../../../common/scripts/loading';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/pages/follow.vue'),
data() { data() {
return { return {
fetching: true, fetching: true,
@@ -67,7 +69,7 @@ export default Vue.extend({
const acct = new URL(location.href).searchParams.get('acct'); const acct = new URL(location.href).searchParams.get('acct');
this.fetching = true; this.fetching = true;
Progress.start(); Progress.start();
(this as any).api('users/show', parseAcct(acct)).then(user => { this.$root.api('users/show', parseAcct(acct)).then(user => {
this.user = user; this.user = user;
this.fetching = false; this.fetching = false;
Progress.done(); Progress.done();
@@ -79,20 +81,20 @@ export default Vue.extend({
try { try {
if (this.user.isFollowing) { if (this.user.isFollowing) {
this.user = await (this as any).api('following/delete', { this.user = await this.$root.api('following/delete', {
userId: this.user.id userId: this.user.id
}); });
} else { } else {
if (this.user.hasPendingFollowRequestFromYou) { if (this.user.hasPendingFollowRequestFromYou) {
this.user = await (this as any).api('following/requests/cancel', { this.user = await this.$root.api('following/requests/cancel', {
userId: this.user.id userId: this.user.id
}); });
} else if (this.user.isLocked) { } else if (this.user.isLocked) {
this.user = await (this as any).api('following/create', { this.user = await this.$root.api('following/create', {
userId: this.user.id userId: this.user.id
}); });
} else { } else {
this.user = await (this as any).api('following/create', { this.user = await this.$root.api('following/create', {
userId: this.user.id userId: this.user.id
}); });
} }
@@ -15,13 +15,13 @@
<path class="wave d" d="M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z"></path> <path class="wave d" d="M29.18,1.06c-0.479-0.502-1.273-0.522-1.775-0.044c-0.016,0.015-0.029,0.029-0.045,0.044c-0.5,0.52-0.5,1.36,0,1.88 c1.361,1.4,2.041,3.24,2.041,5.08s-0.68,3.66-2.041,5.08c-0.5,0.52-0.5,1.36,0,1.88c0.509,0.508,1.332,0.508,1.841,0 c1.86-1.92,2.8-4.44,2.8-6.96C31.99,5.424,30.98,2.931,29.18,1.06z"></path>
</svg> </svg>
</div> </div>
<p class="fetching" v-if="fetching">%i18n:@fetching%<mk-ellipsis/></p> <p class="fetching" v-if="fetching">{{ $t('fetching') }}<mk-ellipsis/></p>
<h1 v-if="!fetching">{{ announcements.length == 0 ? '%i18n:@no-broadcasts%' : announcements[i].title }}</h1> <h1 v-if="!fetching">{{ announcements.length == 0 ? this.$t('no-broadcasts') : announcements[i].title }}</h1>
<p v-if="!fetching"> <p v-if="!fetching">
<span v-if="announcements.length != 0" v-html="announcements[i].text"></span> <span v-if="announcements.length != 0" v-html="announcements[i].text"></span>
<template v-if="announcements.length == 0">%i18n:@have-a-nice-day%</template> <template v-if="announcements.length == 0">{{ $t('have-a-nice-day') }}</template>
</p> </p>
<a v-if="announcements.length > 1" @click="next">%i18n:@next% &gt;&gt;</a> <a v-if="announcements.length > 1" @click="next">{{ $t('next') }} &gt;&gt;</a>
</div> </div>
</mk-widget-container> </mk-widget-container>
</div> </div>
@@ -29,6 +29,7 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'broadcast', name: 'broadcast',
@@ -36,6 +37,7 @@ export default define({
design: 0 design: 0
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/broadcast.vue'),
data() { data() {
return { return {
i: 0, i: 0,
@@ -44,7 +46,7 @@ export default define({
}; };
}, },
mounted() { mounted() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.announcements = meta.broadcasts; this.announcements = meta.broadcasts;
this.fetching = false; this.fetching = false;
}); });
@@ -4,27 +4,27 @@
<div class="mkw-calendar--body"> <div class="mkw-calendar--body">
<div class="calendar" :data-is-holiday="isHoliday"> <div class="calendar" :data-is-holiday="isHoliday">
<p class="month-and-year"> <p class="month-and-year">
<span class="year">{{ '%i18n:@year%'.split('{}')[0] }}{{ year }}{{ '%i18n:@year%'.split('{}')[1] }}</span> <span class="year">{{ this.$t('year').split('{}')[0] }}{{ year }}{{ this.$t('year').split('{}')[1] }}</span>
<span class="month">{{ '%i18n:@month%'.split('{}')[0] }}{{ month }}{{ '%i18n:@month%'.split('{}')[1] }}</span> <span class="month">{{ this.$t('month').split('{}')[0] }}{{ month }}{{ this.$t('month').split('{}')[1] }}</span>
</p> </p>
<p class="day">{{ '%i18n:@day%'.split('{}')[0] }}{{ day }}{{ '%i18n:@day%'.split('{}')[1] }}</p> <p class="day">{{ this.$t('day').split('{}')[0] }}{{ day }}{{ this.$t('day').split('{}')[1] }}</p>
<p class="week-day">{{ weekDay }}</p> <p class="week-day">{{ weekDay }}</p>
</div> </div>
<div class="info"> <div class="info">
<div> <div>
<p>%i18n:@today%<b>{{ dayP.toFixed(1) }}%</b></p> <p>{{ $t('today') }}<b>{{ dayP.toFixed(1) }}%</b></p>
<div class="meter"> <div class="meter">
<div class="val" :style="{ width: `${dayP}%` }"></div> <div class="val" :style="{ width: `${dayP}%` }"></div>
</div> </div>
</div> </div>
<div> <div>
<p>%i18n:@this-month%<b>{{ monthP.toFixed(1) }}%</b></p> <p>{{ $t('this-month') }}<b>{{ monthP.toFixed(1) }}%</b></p>
<div class="meter"> <div class="meter">
<div class="val" :style="{ width: `${monthP}%` }"></div> <div class="val" :style="{ width: `${monthP}%` }"></div>
</div> </div>
</div> </div>
<div> <div>
<p>%i18n:@this-year%<b>{{ yearP.toFixed(1) }}%</b></p> <p>{{ $t('this-year') }}<b>{{ yearP.toFixed(1) }}%</b></p>
<div class="meter"> <div class="meter">
<div class="val" :style="{ width: `${yearP}%` }"></div> <div class="val" :style="{ width: `${yearP}%` }"></div>
</div> </div>
@@ -37,12 +37,15 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'calendar', name: 'calendar',
props: () => ({ props: () => ({
design: 0 design: 0
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/calendar.vue'),
data() { data() {
return { return {
now: new Date(), now: new Date(),
@@ -85,13 +88,13 @@ export default define({
this.month = nm + 1; this.month = nm + 1;
this.day = nd; this.day = nd;
this.weekDay = [ this.weekDay = [
'%i18n:common.weekday.sunday%', this.$t('@.weekday.sunday'),
'%i18n:common.weekday.monday%', this.$t('@.weekday.monday'),
'%i18n:common.weekday.tuesday%', this.$t('@.weekday.tuesday'),
'%i18n:common.weekday.wednesday%', this.$t('@.weekday.wednesday'),
'%i18n:common.weekday.thursday%', this.$t('@.weekday.thursday'),
'%i18n:common.weekday.friday%', this.$t('@.weekday.friday'),
'%i18n:common.weekday.saturday%' this.$t('@.weekday.saturday')
][now.getDay()]; ][now.getDay()];
const dayNumer = now.getTime() - new Date(ny, nm, nd).getTime(); const dayNumer = now.getTime() - new Date(ny, nm, nd).getTime();
@@ -2,11 +2,11 @@
<div> <div>
<mk-widget-container :show-header="false"> <mk-widget-container :show-header="false">
<article class="dolfvtibguprpxxhfndqaosjitixjohx"> <article class="dolfvtibguprpxxhfndqaosjitixjohx">
<h1><fa icon="heart"/>%i18n:@title%</h1> <h1><fa icon="heart"/>{{ $t('title') }}</h1>
<p v-if="meta"> <p v-if="meta">
{{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }} {{ this.$t('text').substr(0, this.$t('text').indexOf('{')) }}
<a :href="'mailto:' + meta.maintainer.email">{{ meta.maintainer.name }}</a> <a :href="'mailto:' + meta.maintainer.email">{{ meta.maintainer.name }}</a>
{{ '%i18n:@text%'.substr('%i18n:@text%'.indexOf('}') + 1) }} {{ this.$t('text').substr(this.$t('text').indexOf('}') + 1) }}
</p> </p>
</article> </article>
</mk-widget-container> </mk-widget-container>
@@ -15,16 +15,19 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'donation' name: 'donation'
}).extend({ }).extend({
i18n: i18n('common/views/widgets/donation.vue'),
data() { data() {
return { return {
meta: null meta: null
}; };
}, },
created() { created() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.meta = meta; this.meta = meta;
}); });
} }
@@ -1,7 +1,7 @@
<template> <template>
<div class="mkw-hashtags"> <div class="mkw-hashtags">
<mk-widget-container :show-header="!props.compact"> <mk-widget-container :show-header="!props.compact">
<template slot="header"><fa icon="hashtag"/>%i18n:@title%</template> <template slot="header"><fa icon="hashtag"/>{{ $t('title') }}</template>
<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'"> <div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
<mk-trends/> <mk-trends/>
@@ -12,6 +12,7 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'hashtags', name: 'hashtags',
@@ -19,6 +20,7 @@ export default define({
compact: false compact: false
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/hashtags.vue'),
methods: { methods: {
func() { func() {
this.props.compact = !this.props.compact; this.props.compact = !this.props.compact;
+5 -3
View File
@@ -1,11 +1,11 @@
<template> <template>
<div class="mkw-memo"> <div class="mkw-memo">
<mk-widget-container :show-header="!props.compact"> <mk-widget-container :show-header="!props.compact">
<template slot="header"><fa :icon="['far', 'sticky-note']"/>%i18n:@title%</template> <template slot="header"><fa :icon="['far', 'sticky-note']"/>{{ $t('title') }}</template>
<div class="mkw-memo--body"> <div class="mkw-memo--body">
<textarea v-model="text" placeholder="%i18n:@memo%" @input="onChange"></textarea> <textarea v-model="text" :placeholder="$t('placeholder')" @input="onChange"></textarea>
<button @click="saveMemo" :disabled="!changed">%i18n:@save%</button> <button @click="saveMemo" :disabled="!changed">{{ $t('save') }}</button>
</div> </div>
</mk-widget-container> </mk-widget-container>
</div> </div>
@@ -13,6 +13,7 @@
<script lang="ts"> <script lang="ts">
import define from '../../define-widget'; import define from '../../define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'memo', name: 'memo',
@@ -20,6 +21,7 @@ export default define({
compact: false compact: false
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/memo.vue'),
data() { data() {
return { return {
text: null, text: null,
@@ -1,25 +1,29 @@
<template> <template>
<div class="mkw-photo-stream" :class="$style.root" :data-melt="props.design == 2"> <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"> <mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="camera"/>%i18n:@title%</template> <template slot="header"><fa icon="camera"/>{{ $t('title') }}</template>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p> <p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div :class="$style.stream" v-if="!fetching && images.length > 0"> <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 v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
</div> </div>
<p :class="$style.empty" v-if="!fetching && images.length == 0">%i18n:@no-photos%</p> <p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
</mk-widget-container> </mk-widget-container>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'photo-stream', name: 'photo-stream',
props: () => ({ props: () => ({
design: 0 design: 0
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/photo-stream.vue'),
data() { data() {
return { return {
images: [], images: [],
@@ -28,11 +32,11 @@ export default define({
}; };
}, },
mounted() { mounted() {
this.connection = (this as any).os.stream.useSharedConnection('main'); this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('driveFileCreated', this.onDriveFileCreated); this.connection.on('driveFileCreated', this.onDriveFileCreated);
(this as any).api('drive/stream', { this.$root.api('drive/stream', {
type: 'image/*', type: 'image/*',
limit: 9 limit: 9
}).then(images => { }).then(images => {
@@ -1,8 +1,8 @@
<template> <template>
<div class="mkw-posts-monitor"> <div class="mkw-posts-monitor">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2"> <mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="chart-line"/>%i18n:@title%</template> <template slot="header"><fa icon="chart-line"/>{{ $t('title') }}</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button> <button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }"> <div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2"> <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">
@@ -70,15 +70,18 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
export default define({ export default define({
name: 'server', name: 'posts-monitor',
props: () => ({ props: () => ({
design: 0, design: 0,
view: 0 view: 0
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/posts-monitor.vue'),
data() { data() {
return { return {
connection: null, connection: null,
@@ -109,7 +112,7 @@ export default define({
} }
}, },
mounted() { mounted() {
this.connection = (this as any).os.stream.useSharedConnection('notesStats'); this.connection = this.$root.stream.useSharedConnection('notesStats');
this.connection.on('stats', this.onStats); this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog); this.connection.on('statsLog', this.onStatsLog);
+4 -1
View File
@@ -5,7 +5,7 @@
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button> <button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'"> <div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p> <p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div class="feed" v-else> <div class="feed" v-else>
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a> <a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
</div> </div>
@@ -16,6 +16,8 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'rss', name: 'rss',
props: () => ({ props: () => ({
@@ -23,6 +25,7 @@ export default define({
url: 'http://news.yahoo.co.jp/pickup/rss.xml' url: 'http://news.yahoo.co.jp/pickup/rss.xml'
}) })
}).extend({ }).extend({
i18n: i18n(),
data() { data() {
return { return {
items: [], items: [],
@@ -1,10 +1,10 @@
<template> <template>
<div class="mkw-server"> <div class="mkw-server">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2"> <mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="server"/>%i18n:@title%</template> <template slot="header"><fa icon="server"/>{{ $t('title') }}</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button> <button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p> <p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-if="!fetching"> <template v-if="!fetching">
<x-cpu-memory v-show="props.view == 0" :connection="connection"/> <x-cpu-memory v-show="props.view == 0" :connection="connection"/>
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/> <x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
@@ -19,6 +19,7 @@
<script lang="ts"> <script lang="ts">
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
import XCpuMemory from './server.cpu-memory.vue'; import XCpuMemory from './server.cpu-memory.vue';
import XCpu from './server.cpu.vue'; import XCpu from './server.cpu.vue';
import XMemory from './server.memory.vue'; import XMemory from './server.memory.vue';
@@ -33,6 +34,8 @@ export default define({
view: 0 view: 0
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/server.vue'),
components: { components: {
XCpuMemory, XCpuMemory,
XCpu, XCpu,
@@ -49,12 +52,12 @@ export default define({
}; };
}, },
mounted() { mounted() {
(this as any).os.getMeta().then(meta => { this.$root.getMeta().then(meta => {
this.meta = meta; this.meta = meta;
this.fetching = false; this.fetching = false;
}); });
this.connection = (this as any).os.stream.useSharedConnection('serverStats'); this.connection = this.$root.stream.useSharedConnection('serverStats');
}, },
beforeDestroy() { beforeDestroy() {
this.connection.dispose(); this.connection.dispose();
@@ -2,10 +2,10 @@
<div class="mkw-slideshow" :data-mobile="platform == 'mobile'"> <div class="mkw-slideshow" :data-mobile="platform == 'mobile'">
<div @click="choose"> <div @click="choose">
<p v-if="props.folder === undefined"> <p v-if="props.folder === undefined">
<template v-if="isCustomizeMode">%i18n:@folder-customize-mode%</template> <template v-if="isCustomizeMode">{{ $t('folder-customize-mode') }}</template>
<template v-else>%i18n:@folder%</template> <template v-else>{{ $t('folder') }}</template>
</p> </p>
<p v-if="props.folder !== undefined && images.length == 0 && !fetching">%i18n:@no-image%</p> <p v-if="props.folder !== undefined && images.length == 0 && !fetching">{{ $t('no-image') }}</p>
<div ref="slideA" class="slide a"></div> <div ref="slideA" class="slide a"></div>
<div ref="slideB" class="slide b"></div> <div ref="slideB" class="slide b"></div>
</div> </div>
@@ -15,6 +15,8 @@
<script lang="ts"> <script lang="ts">
import * as anime from 'animejs'; import * as anime from 'animejs';
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
export default define({ export default define({
name: 'slideshow', name: 'slideshow',
props: () => ({ props: () => ({
@@ -22,6 +24,8 @@ export default define({
size: 0 size: 0
}) })
}).extend({ }).extend({
i18n: i18n('common/views/widgets/slideshow.vue'),
data() { data() {
return { return {
images: [], images: [],
@@ -97,7 +101,7 @@ export default define({
fetch() { fetch() {
this.fetching = true; this.fetching = true;
(this as any).api('drive/files', { this.$root.api('drive/files', {
folderId: this.props.folder, folderId: this.props.folder,
type: 'image/*', type: 'image/*',
limit: 100 limit: 100
@@ -110,7 +114,7 @@ export default define({
}); });
}, },
choose() { choose() {
(this as any).apis.chooseDriveFolder().then(folder => { this.$chooseDriveFolder().then(folder => {
this.props.folder = folder ? folder.id : null; this.props.folder = folder ? folder.id : null;
this.save(); this.save();
this.fetch(); this.fetch();
+29 -24
View File
@@ -7,39 +7,44 @@
<script lang="ts"> <script lang="ts">
import * as anime from 'animejs'; import * as anime from 'animejs';
import define from '../../../common/define-widget'; import define from '../../../common/define-widget';
import i18n from '../../../i18n';
const tips = [
'%i18n:@tips-line1%',
'%i18n:@tips-line2%',
'%i18n:@tips-line3%',
'%i18n:@tips-line4%',
'%i18n:@tips-line5%',
'%i18n:@tips-line6%',
'%i18n:@tips-line7%',
'%i18n:@tips-line8%',
'%i18n:@tips-line9%',
'%i18n:@tips-line10%',
'%i18n:@tips-line11%',
'%i18n:@tips-line13%',
'%i18n:@tips-line14%',
'%i18n:@tips-line17%',
'%i18n:@tips-line19%',
'%i18n:@tips-line20%',
'%i18n:@tips-line21%',
'%i18n:@tips-line23%',
'%i18n:@tips-line24%',
'%i18n:@tips-line25%'
]
export default define({ export default define({
name: 'tips' name: 'tips'
}).extend({ }).extend({
i18n: i18n('common/views/widgets/tips.vue'),
data() { data() {
return { return {
tips: [],
tip: null, tip: null,
clock: null clock: null
}; };
}, },
created() {
this.tips = [
this.$t('tips-line1'),
this.$t('tips-line2'),
this.$t('tips-line3'),
this.$t('tips-line4'),
this.$t('tips-line5'),
this.$t('tips-line6'),
this.$t('tips-line7'),
this.$t('tips-line8'),
this.$t('tips-line9'),
this.$t('tips-line10'),
this.$t('tips-line11'),
this.$t('tips-line13'),
this.$t('tips-line14'),
this.$t('tips-line17'),
this.$t('tips-line19'),
this.$t('tips-line20'),
this.$t('tips-line21'),
this.$t('tips-line23'),
this.$t('tips-line24'),
this.$t('tips-line25')
];
},
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.set(); this.set();
@@ -52,7 +57,7 @@ export default define({
}, },
methods: { methods: {
set() { set() {
this.tip = tips[Math.floor(Math.random() * tips.length)]; this.tip = this.tips[Math.floor(Math.random() * this.tips.length)];
}, },
change() { change() {
anime({ anime({
+2
View File
@@ -1,5 +1,6 @@
declare const _LANG_: string; declare const _LANG_: string;
declare const _LANGS_: string; declare const _LANGS_: string;
declare const _LOCALE_: { [key: string]: any };
declare const _THEME_COLOR_: string; declare const _THEME_COLOR_: string;
declare const _COPYRIGHT_: string; declare const _COPYRIGHT_: string;
declare const _VERSION_: string; declare const _VERSION_: string;
@@ -16,6 +17,7 @@ export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
export const lang = _LANG_; export const lang = _LANG_;
export const langs = _LANGS_; export const langs = _LANGS_;
export const locale = _LOCALE_;
export const themeColor = _THEME_COLOR_; export const themeColor = _THEME_COLOR_;
export const copyright = _COPYRIGHT_; export const copyright = _COPYRIGHT_;
export const version = _VERSION_; export const version = _VERSION_;
@@ -1,29 +0,0 @@
import OS from '../../mios';
import { url } from '../../config';
import MkChooseFileFromDriveWindow from '../views/components/choose-file-from-drive-window.vue';
export default (os: OS) => opts => {
return new Promise((res, rej) => {
const o = opts || {};
if (document.body.clientWidth > 800) {
const w = os.new(MkChooseFileFromDriveWindow, {
title: o.title,
multiple: o.multiple,
initFolder: o.currentFolder
});
w.$once('selected', file => {
res(file);
});
document.body.appendChild(w.$el);
} else {
window['cb'] = file => {
res(file);
};
window.open(url + `/selectdrive?multiple=${o.multiple}`,
'choose_drive_window',
'height=500, width=800');
}
});
};
@@ -1,16 +0,0 @@
import OS from '../../mios';
import MkChooseFolderFromDriveWindow from '../views/components/choose-folder-from-drive-window.vue';
export default (os: OS) => opts => {
return new Promise((res, rej) => {
const o = opts || {};
const w = os.new(MkChooseFolderFromDriveWindow, {
title: o.title,
initFolder: o.currentFolder
});
w.$once('selected', folder => {
res(folder);
});
document.body.appendChild(w.$el);
});
};
-15
View File
@@ -1,15 +0,0 @@
import OS from '../../mios';
import Ctx from '../views/components/context-menu.vue';
export default (os: OS) => (e, menu, opts?) => {
const o = opts || {};
const vm = os.new(Ctx, {
menu,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset,
});
vm.$once('closed', () => {
if (o.closed) o.closed();
});
document.body.appendChild(vm.$el);
};
-18
View File
@@ -1,18 +0,0 @@
import OS from '../../mios';
import Dialog from '../views/components/dialog.vue';
export default (os: OS) => opts => {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = os.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
document.body.appendChild(d.$el);
});
};
-19
View File
@@ -1,19 +0,0 @@
import OS from '../../mios';
import InputDialog from '../views/components/input-dialog.vue';
export default (os: OS) => opts => {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = os.new(InputDialog, {
title: o.title,
placeholder: o.placeholder,
default: o.default,
type: o.type || 'text',
allowEmpty: o.allowEmpty
});
d.$once('done', text => {
res(text);
});
document.body.appendChild(d.$el);
});
};
-9
View File
@@ -1,9 +0,0 @@
import OS from '../../mios';
import Notification from '../views/components/ui-notification.vue';
export default (os: OS) => message => {
const vm = os.new(Notification, {
message
});
document.body.appendChild(vm.$el);
};
-22
View File
@@ -1,22 +0,0 @@
import OS from '../../mios';
import PostFormWindow from '../views/components/post-form-window.vue';
import RenoteFormWindow from '../views/components/renote-form-window.vue';
export default (os: OS) => opts => {
const o = opts || {};
if (o.renote) {
const vm = os.new(RenoteFormWindow, {
note: o.renote,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el);
} else {
const vm = os.new(PostFormWindow, {
reply: o.reply,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
document.body.appendChild(vm.$el);
}
};
+12 -13
View File
@@ -1,15 +1,14 @@
import OS from '../../mios';
import { apiUrl } from '../../config'; import { apiUrl } from '../../config';
import CropWindow from '../views/components/crop-window.vue'; import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue'; import ProgressDialog from '../views/components/progress-dialog.vue';
export default (os: OS) => { export default ($root: any) => {
const cropImage = file => new Promise((resolve, reject) => { const cropImage = file => new Promise((resolve, reject) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$'); const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) { if (!regex.test(file.name) ) {
os.apis.dialog({ $root.$dialog({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%', title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null, text: null,
actions: [{ actions: [{
@@ -19,7 +18,7 @@ export default (os: OS) => {
return reject('invalid-filetype'); return reject('invalid-filetype');
} }
const w = os.new(CropWindow, { const w = $root.new(CropWindow, {
image: file, image: file,
title: '%i18n:desktop.avatar-crop-title%', title: '%i18n:desktop.avatar-crop-title%',
aspectRatio: 1 / 1 aspectRatio: 1 / 1
@@ -27,14 +26,14 @@ export default (os: OS) => {
w.$once('cropped', blob => { w.$once('cropped', blob => {
const data = new FormData(); const data = new FormData();
data.append('i', os.store.state.i.token); data.append('i', $root.$store.state.i.token);
data.append('file', blob, file.name + '.cropped.png'); data.append('file', blob, file.name + '.cropped.png');
os.api('drive/folders/find', { $root.api('drive/folders/find', {
name: '%i18n:desktop.avatar%' name: '%i18n:desktop.avatar%'
}).then(avatarFolder => { }).then(avatarFolder => {
if (avatarFolder.length === 0) { if (avatarFolder.length === 0) {
os.api('drive/folders/create', { $root.api('drive/folders/create', {
name: '%i18n:desktop.avatar%' name: '%i18n:desktop.avatar%'
}).then(iconFolder => { }).then(iconFolder => {
resolve(upload(data, iconFolder)); resolve(upload(data, iconFolder));
@@ -55,7 +54,7 @@ export default (os: OS) => {
}); });
const upload = (data, folder) => new Promise((resolve, reject) => { const upload = (data, folder) => new Promise((resolve, reject) => {
const dialog = os.new(ProgressDialog, { const dialog = $root.new(ProgressDialog, {
title: '%i18n:desktop.uploading-avatar%' title: '%i18n:desktop.uploading-avatar%'
}); });
document.body.appendChild(dialog.$el); document.body.appendChild(dialog.$el);
@@ -79,19 +78,19 @@ export default (os: OS) => {
}); });
const setAvatar = file => { const setAvatar = file => {
return os.api('i/update', { return $root.api('i/update', {
avatarId: file.id avatarId: file.id
}).then(i => { }).then(i => {
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'avatarId', key: 'avatarId',
value: i.avatarId value: i.avatarId
}); });
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'avatarUrl', key: 'avatarUrl',
value: i.avatarUrl value: i.avatarUrl
}); });
os.apis.dialog({ $root.$dialog({
title: '%fa:info-circle% %i18n:desktop.avatar-updated%', title: '%fa:info-circle% %i18n:desktop.avatar-updated%',
text: null, text: null,
actions: [{ actions: [{
@@ -106,7 +105,7 @@ export default (os: OS) => {
return (file = null) => { return (file = null) => {
const selectedFile = file const selectedFile = file
? Promise.resolve(file) ? Promise.resolve(file)
: os.apis.chooseDriveFile({ : $root.$chooseDriveFile({
multiple: false, multiple: false,
title: '%fa:image% %i18n:desktop.choose-avatar%' title: '%fa:image% %i18n:desktop.choose-avatar%'
}); });
+12 -13
View File
@@ -1,15 +1,14 @@
import OS from '../../mios';
import { apiUrl } from '../../config'; import { apiUrl } from '../../config';
import CropWindow from '../views/components/crop-window.vue'; import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue'; import ProgressDialog from '../views/components/progress-dialog.vue';
export default (os: OS) => { export default ($root: any) => {
const cropImage = file => new Promise((resolve, reject) => { const cropImage = file => new Promise((resolve, reject) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$'); const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) { if (!regex.test(file.name) ) {
os.apis.dialog({ $root.dialog({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%', title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null, text: null,
actions: [{ actions: [{
@@ -19,7 +18,7 @@ export default (os: OS) => {
return reject('invalid-filetype'); return reject('invalid-filetype');
} }
const w = os.new(CropWindow, { const w = $root.new(CropWindow, {
image: file, image: file,
title: '%i18n:desktop.banner-crop-title%', title: '%i18n:desktop.banner-crop-title%',
aspectRatio: 16 / 9 aspectRatio: 16 / 9
@@ -27,14 +26,14 @@ export default (os: OS) => {
w.$once('cropped', blob => { w.$once('cropped', blob => {
const data = new FormData(); const data = new FormData();
data.append('i', os.store.state.i.token); data.append('i', $root.$store.state.i.token);
data.append('file', blob, file.name + '.cropped.png'); data.append('file', blob, file.name + '.cropped.png');
os.api('drive/folders/find', { $root.api('drive/folders/find', {
name: '%i18n:desktop.banner%' name: '%i18n:desktop.banner%'
}).then(bannerFolder => { }).then(bannerFolder => {
if (bannerFolder.length === 0) { if (bannerFolder.length === 0) {
os.api('drive/folders/create', { $root.api('drive/folders/create', {
name: '%i18n:desktop.banner%' name: '%i18n:desktop.banner%'
}).then(iconFolder => { }).then(iconFolder => {
resolve(upload(data, iconFolder)); resolve(upload(data, iconFolder));
@@ -55,7 +54,7 @@ export default (os: OS) => {
}); });
const upload = (data, folder) => new Promise((resolve, reject) => { const upload = (data, folder) => new Promise((resolve, reject) => {
const dialog = os.new(ProgressDialog, { const dialog = $root.new(ProgressDialog, {
title: '%i18n:desktop.uploading-banner%' title: '%i18n:desktop.uploading-banner%'
}); });
document.body.appendChild(dialog.$el); document.body.appendChild(dialog.$el);
@@ -79,19 +78,19 @@ export default (os: OS) => {
}); });
const setBanner = file => { const setBanner = file => {
return os.api('i/update', { return $root.api('i/update', {
bannerId: file.id bannerId: file.id
}).then(i => { }).then(i => {
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'bannerId', key: 'bannerId',
value: i.bannerId value: i.bannerId
}); });
os.store.commit('updateIKeyValue', { $root.$store.commit('updateIKeyValue', {
key: 'bannerUrl', key: 'bannerUrl',
value: i.bannerUrl value: i.bannerUrl
}); });
os.apis.dialog({ $root.$dialog({
title: '%fa:info-circle% %i18n:desktop.banner-updated%', title: '%fa:info-circle% %i18n:desktop.banner-updated%',
text: null, text: null,
actions: [{ actions: [{
@@ -106,7 +105,7 @@ export default (os: OS) => {
return (file = null) => { return (file = null) => {
const selectedFile = file const selectedFile = file
? Promise.resolve(file) ? Promise.resolve(file)
: os.apis.chooseDriveFile({ : $root.$chooseDriveFile({
multiple: false, multiple: false,
title: '%fa:image% %i18n:desktop.choose-banner%' title: '%fa:image% %i18n:desktop.choose-banner%'
}); });
+125 -20
View File
@@ -2,6 +2,7 @@
* Desktop Client * Desktop Client
*/ */
import Vue from 'vue';
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
// Style // Style
@@ -11,19 +12,11 @@ import init from '../init';
import fuckAdBlock from '../common/scripts/fuck-ad-block'; import fuckAdBlock from '../common/scripts/fuck-ad-block';
import composeNotification from '../common/scripts/compose-notification'; import composeNotification from '../common/scripts/compose-notification';
import chooseDriveFolder from './api/choose-drive-folder';
import chooseDriveFile from './api/choose-drive-file';
import dialog from './api/dialog';
import input from './api/input';
import post from './api/post';
import notify from './api/notify';
import updateAvatar from './api/update-avatar';
import updateBanner from './api/update-banner';
import MkIndex from './views/pages/index.vue'; import MkIndex from './views/pages/index.vue';
import MkHome from './views/pages/home.vue'; import MkHome from './views/pages/home.vue';
import MkDeck from './views/pages/deck/deck.vue'; import MkDeck from './views/pages/deck/deck.vue';
import MkUser from './views/pages/user/user.vue'; import MkUser from './views/pages/user/user.vue';
import MkUserFollowingOrFollowers from './views/pages/user-following-or-followers.vue';
import MkFavorites from './views/pages/favorites.vue'; import MkFavorites from './views/pages/favorites.vue';
import MkSelectDrive from './views/pages/selectdrive.vue'; import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue'; import MkDrive from './views/pages/drive.vue';
@@ -35,12 +28,131 @@ import MkTag from './views/pages/tag.vue';
import MkReversi from './views/pages/games/reversi.vue'; import MkReversi from './views/pages/games/reversi.vue';
import MkShare from './views/pages/share.vue'; import MkShare from './views/pages/share.vue';
import MkFollow from '../common/views/pages/follow.vue'; import MkFollow from '../common/views/pages/follow.vue';
import Ctx from './views/components/context-menu.vue';
import PostFormWindow from './views/components/post-form-window.vue';
import RenoteFormWindow from './views/components/renote-form-window.vue';
import MkChooseFileFromDriveWindow from './views/components/choose-file-from-drive-window.vue';
import MkChooseFolderFromDriveWindow from './views/components/choose-folder-from-drive-window.vue';
import Dialog from './views/components/dialog.vue';
import InputDialog from './views/components/input-dialog.vue';
import Notification from './views/components/ui-notification.vue';
import { url } from '../config';
import MiOS from '../mios'; import MiOS from '../mios';
/** /**
* init * init
*/ */
init(async (launch) => { init(async (launch) => {
Vue.mixin({
methods: {
$contextmenu(e, menu, opts?) {
const o = opts || {};
const vm = this.$root.new(Ctx, {
menu,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset,
});
vm.$once('closed', () => {
if (o.closed) o.closed();
});
},
$post(opts) {
const o = opts || {};
if (o.renote) {
const vm = this.$root.new(RenoteFormWindow, {
note: o.renote,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
} else {
const vm = this.$root.new(PostFormWindow, {
reply: o.reply,
animation: o.animation == null ? true : o.animation
});
if (o.cb) vm.$once('closed', o.cb);
}
},
$chooseDriveFile(opts) {
return new Promise((res, rej) => {
const o = opts || {};
if (document.body.clientWidth > 800) {
const w = this.$root.new(MkChooseFileFromDriveWindow, {
title: o.title,
multiple: o.multiple,
initFolder: o.currentFolder
});
w.$once('selected', file => {
res(file);
});
} else {
window['cb'] = file => {
res(file);
};
window.open(url + `/selectdrive?multiple=${o.multiple}`,
'choose_drive_window',
'height=500, width=800');
}
});
},
$chooseDriveFolder(opts) {
return new Promise((res, rej) => {
const o = opts || {};
const w = this.$root.new(MkChooseFolderFromDriveWindow, {
title: o.title,
initFolder: o.currentFolder
});
w.$once('selected', folder => {
res(folder);
});
});
},
$dialog(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
});
},
$input(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(InputDialog, {
title: o.title,
placeholder: o.placeholder,
default: o.default,
type: o.type || 'text',
allowEmpty: o.allowEmpty
});
d.$once('done', text => {
res(text);
});
});
},
$notify(message) {
this.$root.new(Notification, {
message
});
}
}
});
// Register directives // Register directives
require('./views/directives'); require('./views/directives');
@@ -66,28 +178,21 @@ init(async (launch) => {
{ path: '/share', component: MkShare }, { path: '/share', component: MkShare },
{ path: '/reversi/:game?', component: MkReversi }, { path: '/reversi/:game?', component: MkReversi },
{ path: '/@:user', name: 'user', component: MkUser }, { path: '/@:user', name: 'user', component: MkUser },
{ path: '/@:user/following', name: 'userFollowing', component: MkUserFollowingOrFollowers },
{ path: '/@:user/followers', name: 'userFollowers', component: MkUserFollowingOrFollowers },
{ path: '/notes/:note', name: 'note', component: MkNote }, { path: '/notes/:note', name: 'note', component: MkNote },
{ path: '/authorize-follow', component: MkFollow } { path: '/authorize-follow', component: MkFollow }
] ]
}); });
// Launch the app // Launch the app
const [, os] = launch(router, os => ({ const [app, os] = launch(router);
chooseDriveFolder: chooseDriveFolder(os),
chooseDriveFile: chooseDriveFile(os),
dialog: dialog(os),
input: input(os),
post: post(os),
notify: notify(os),
updateAvatar: updateAvatar(os),
updateBanner: updateBanner(os)
}));
if (os.store.getters.isSignedIn) { if (os.store.getters.isSignedIn) {
/** /**
* Fuck AD Block * Fuck AD Block
*/ */
fuckAdBlock(os); fuckAdBlock(app);
} }
/** /**
@@ -1,6 +1,6 @@
<template> <template>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" @mousedown.prevent="onMousedown"> <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" @mousedown.prevent="onMousedown">
<title>%i18n:@total%<br/>%i18n:@notes%<br/>%i18n:@replies%<br/>%i18n:@renotes%</title> <title>{{ $t('total') }}<br/>{{ $t('notes') }}<br/>{{ $t('replies') }}<br/>{{ $t('renotes') }}</title>
<polyline <polyline
:points="pointsNote" :points="pointsNote"
fill="none" fill="none"
@@ -27,6 +27,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n';
function dragListen(fn) { function dragListen(fn) {
window.addEventListener('mousemove', fn); window.addEventListener('mousemove', fn);
@@ -41,6 +42,7 @@ function dragClear(fn) {
} }
export default Vue.extend({ export default Vue.extend({
i18n: i18n('desktop/views/components/activity.chart.vue'),
props: ['data'], props: ['data'],
data() { data() {
return { return {

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