Compare commits

...

131 Commits

Author SHA1 Message Date
syuilo
6f97142b59 10.87.3 2019-02-18 20:54:14 +09:00
syuilo
b31d1ce61d フォローリクエスト数がおかしい場合の応急処置APIを追加 2019-02-18 20:52:41 +09:00
syuilo
b07cd37a16 🎨 2019-02-18 20:48:39 +09:00
syuilo
69b74a46b9 Split cropperjs to reduce bundle size 2019-02-18 19:26:53 +09:00
syuilo
882d829558 Fix #4063 2019-02-18 19:22:10 +09:00
syuilo
532821d503 Fix #4309
Close #4311
2019-02-18 18:50:45 +09:00
syuilo
522ce67498 10.87.2 2019-02-18 11:49:32 +09:00
syuilo
0e046faf4a みつけるの人気のタグを第2ソートで連合含めたユーザー数にしたりユーザーのタグ以外は除外するように 2019-02-18 11:47:25 +09:00
syuilo
d9092dc81f ユーザーが存在しない場合の表示を追加 2019-02-18 11:27:15 +09:00
syuilo
92a4e90026 Remove duplicated route name 2019-02-18 11:21:50 +09:00
syuilo
07dccad5b1 Avoid dupulicated route name 2019-02-18 11:20:20 +09:00
syuilo
146b0d2889 Improve usability 2019-02-18 11:16:52 +09:00
syuilo
388565fb10 Use # instead of v-slot: 2019-02-18 11:13:56 +09:00
syuilo
da4ba51a74 Fix bug 2019-02-18 10:05:58 +09:00
syuilo
1edcd136a4 Revert "🎨"
This reverts commit 9883c751da.
2019-02-18 10:02:32 +09:00
syuilo
9883c751da 🎨 2019-02-18 09:57:19 +09:00
syuilo
f78b28b995 🎨 2019-02-18 09:51:22 +09:00
syuilo
54d40420ad Use v-slot instead of slot 2019-02-18 09:48:00 +09:00
syuilo
ba1492f977 Refactor client (#4307)
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Fix bug

* 🎨

* 🎨

* 🎨
2019-02-18 09:17:55 +09:00
syuilo
efd0368e56 Resolve #4305 2019-02-18 04:52:40 +09:00
syuilo
a766a57af9 誰もフォローしていない状態でホームTLを表示したときにexploreページへ誘導するように 2019-02-18 04:45:16 +09:00
Acid Chicken (硫酸鶏)
3bdd8a2d90 Update CircleCI configuration (#4297)
* Update config.yml

* Fix typo

* Add name
2019-02-18 02:44:46 +09:00
syuilo
7ef1205f8b 10.87.1 2019-02-18 01:11:28 +09:00
syuilo
e8db63e788 Fix bug 2019-02-18 01:11:14 +09:00
syuilo
0bcef2453c 10.87.0 2019-02-18 00:51:39 +09:00
syuilo
b9f549135c Fix bug 2019-02-18 00:49:34 +09:00
syuilo
87b0017386 Fix bug 2019-02-18 00:47:45 +09:00
syuilo
cc8ff556d4 Fix bug 2019-02-18 00:45:43 +09:00
syuilo
021f74da54 🎨 2019-02-18 00:41:05 +09:00
syuilo
f9389802d7 Update CHANGELOG.md 2019-02-18 00:39:31 +09:00
syuilo
18dd172c97 Chore: Fix type definition 2019-02-18 00:20:14 +09:00
syuilo
1d5a54ff6f ハッシュタグでユーザー検索できるように (#4298)
* ハッシュタグでユーザー検索できるように

* 🎨

* Increase limit

* リモートユーザーも表示

* Fix bug

* Fix bug

* Improve performance
2019-02-17 23:41:47 +09:00
MeiMei
03e2c7eec6 Fix #4300 (#4304)
* Fix #4300

* sidebar
2019-02-17 23:40:00 +09:00
dependabot[bot]
0902727d1c Update @types/node requirement from 10.12.21 to 10.12.24 (#4231)
Updates the requirements on [@types/node](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>
2019-02-17 21:42:40 +09:00
dependabot[bot]
496895634d Update @types/sharp requirement from 0.21.1 to 0.21.2 (#4263)
Updates the requirements on [@types/sharp](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>
2019-02-17 21:42:03 +09:00
dependabot[bot]
9414e9e258 Update cssnano requirement from 4.1.8 to 4.1.10 (#4265)
Updates the requirements on [cssnano](https://github.com/cssnano/cssnano) to permit the latest version.
- [Release notes](https://github.com/cssnano/cssnano/releases)
- [Commits](https://github.com/cssnano/cssnano/commits/v4.1.10)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-17 21:41:36 +09:00
Acid Chicken (硫酸鶏)
357528d139 Use object instead of if chain (#4212) 2019-02-17 21:40:53 +09:00
MeiMei
c4efbdf4c7 Fix #4292 (#4294)
* Fix #4292

* use commit
2019-02-17 21:28:20 +09:00
かひわし4(バージョン1)
fb4a921cd9 Docker: Add support for service worker (#4296)
Service worker requires web-push package.
2019-02-17 21:26:00 +09:00
Acid Chicken (硫酸鶏)
683b242215 Use Record<string, number> instead of any (#4293) 2019-02-17 19:38:46 +09:00
syuilo
a5660d6c82 Exploreページに新規ユーザー一覧を追加 (#4295) 2019-02-17 17:53:51 +09:00
syuilo
f632ec50c1 10.86.2 2019-02-17 03:29:17 +09:00
syuilo
a55d15214b Update ja-JP.yml 2019-02-17 03:28:36 +09:00
syuilo
f1709a2cc2 Update CHANGELOG.md 2019-02-17 03:23:43 +09:00
syuilo
effa542958 Improve user page 2019-02-17 03:23:34 +09:00
MeiMei
e8bf742c87 別タブでルートより下を開いたときにはデッキにしないように (#4291) 2019-02-17 02:26:17 +09:00
syuilo
2e6652edce Resolve #4272 2019-02-17 01:50:17 +09:00
syuilo
230c204b48 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-17 01:12:08 +09:00
syuilo
3755c600b1 Update CHANGELOG.md 2019-02-17 01:11:59 +09:00
Acid Chicken (硫酸鶏)
24513fc0a3 Update media-banner.vue (#4287) 2019-02-17 01:09:49 +09:00
syuilo
0a79a6564a Add support for disabled timeline to deck
Close #4286
Resolve #4275
2019-02-17 01:04:21 +09:00
syuilo
562bb5842b [Client] Improve featured notes page 2019-02-17 00:28:41 +09:00
syuilo
ec3ca3032e ミュートワードで正規表現を使えるように 2019-02-16 19:37:05 +09:00
syuilo
890770c275 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-16 17:42:01 +09:00
syuilo
9ed58a1b4e 🎨 2019-02-16 17:41:54 +09:00
Acid Chicken (硫酸鶏)
08984be2fe Update config.yml 2019-02-16 17:05:56 +09:00
syuilo
e3ade148ca 10.86.1 2019-02-16 16:59:56 +09:00
syuilo
34c0eff89f 🎨 2019-02-16 16:46:06 +09:00
syuilo
40aba47a47 Fix bug 2019-02-16 16:43:17 +09:00
syuilo
6736f51134 Use home icon 2019-02-16 16:40:17 +09:00
syuilo
9d826d6e52 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-16 16:39:42 +09:00
syuilo
902d9bc7a5 Fix bug 2019-02-16 16:39:34 +09:00
MeiMei
b6c86e2845 Change home button to timeline (#4282)
* Home to Timeline

* remove home from locales
2019-02-16 16:27:24 +09:00
Acid Chicken (硫酸鶏)
34dffdfc8f Update config.yml 2019-02-16 11:10:06 +09:00
syuilo
a56f3f1d89 10.86.0 2019-02-16 11:06:45 +09:00
syuilo
88dc4c83cb Improve UI 2019-02-16 10:58:44 +09:00
syuilo
5a28dc0198 Improve user-list component 2019-02-16 09:27:53 +09:00
syuilo
40d2650d49 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-16 08:51:38 +09:00
syuilo
545e83efb1 🎨 2019-02-16 08:51:35 +09:00
Acid Chicken (硫酸鶏)
d4b00a5482 Update README.md [AUTOGEN] (#4280) 2019-02-16 06:58:51 +09:00
syuilo
c2b1bbeec5 Exploreページを実装 2019-02-16 06:50:58 +09:00
syuilo
8c8f165a6e Add missing comma 2019-02-16 04:51:57 +09:00
MeiMei
04553de230 Fix #4276 (#4278) 2019-02-15 23:43:49 +09:00
Acid Chicken (硫酸鶏)
2776934728 Update is-objectid.ts (#4277)
* Update is-objectid.ts

* Update is-objectid.ts
2019-02-15 23:42:44 +09:00
syuilo
0064dbb010 🎨 2019-02-15 19:40:11 +09:00
Acid Chicken (硫酸鶏)
d52e671adf Update README.md [AUTOGEN] (#4274) 2019-02-15 17:20:17 +09:00
syuilo
6017dc2dff 10.85.2 2019-02-15 15:38:10 +09:00
syuilo
937f7cbd60 🎨 2019-02-15 15:35:52 +09:00
syuilo
f8b3f66904 Refactor 2019-02-15 15:12:23 +09:00
syuilo
9d5701f35a Update deck.vue 2019-02-15 15:06:15 +09:00
syuilo
dff65810c6 Increase featured limit 2019-02-15 15:01:05 +09:00
syuilo
6752cf1d64 🎨 2019-02-15 15:00:20 +09:00
syuilo
8336910a59 🎨 2019-02-15 14:58:15 +09:00
syuilo
957a1149e0 検索結果を内部コンポーネントに 2019-02-15 14:52:21 +09:00
syuilo
e8719ff6e6 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-15 14:29:59 +09:00
syuilo
28b63298e5 Increase featured limit 2019-02-15 14:29:42 +09:00
MeiMei
dd4dee8095 デッキから フォロー/フォロワー ページに行けるように (#4271) 2019-02-15 14:27:16 +09:00
syuilo
c47818fed4 🎨 2019-02-15 08:52:09 +09:00
syuilo
e53c383908 10.85.1 2019-02-15 08:42:48 +09:00
syuilo
55c9c0436b 🎨 2019-02-15 08:42:41 +09:00
syuilo
66b79e5e24 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-15 08:40:25 +09:00
syuilo
514b830910 Fix bug 2019-02-15 08:40:16 +09:00
MeiMei
e4f799bf1d Hide localOnly from welcome TL (#4257) 2019-02-15 08:38:59 +09:00
syuilo
b383427d3d 🎨 2019-02-15 08:37:46 +09:00
syuilo
e969518139 Fix bug 2019-02-15 08:34:54 +09:00
syuilo
113fe294bd Fix #4267
Close #4269
2019-02-15 08:32:18 +09:00
syuilo
a4d92f781f 10.85.0 2019-02-15 06:34:45 +09:00
syuilo
414cac49c3 Improve featured api 2019-02-15 06:31:22 +09:00
syuilo
95b157ac3e Add featured page 2019-02-15 06:31:03 +09:00
syuilo
8e3d884081 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-15 06:06:24 +09:00
syuilo
9def6fcadd 🎨 2019-02-15 06:06:13 +09:00
Lynx Kotoura
7837bd44fc Add i18n when timelines are empty (#4261) 2019-02-15 05:59:07 +09:00
syuilo
a6c3663155 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-15 05:58:18 +09:00
syuilo
0b5afadbb8 Update setup.ja.md 2019-02-15 05:58:09 +09:00
syuilo
43864f0da4 既にフォローされている場合はフォローリクエストを生成しないように (#4266) 2019-02-15 05:56:28 +09:00
syuilo
6a0d9d70ed 非ログイン時にお知らせを表示 2019-02-15 05:55:59 +09:00
syuilo
63c6dce68e 🎨 2019-02-15 05:51:22 +09:00
syuilo
53422ffcb2 Improve desktop UX (#4262)
* wip

* wip

* wip

* wip

* wip

* wip

* Merge

* wip

* wip

* wip

* wip

* wip

* wip
2019-02-15 05:08:59 +09:00
Acid Chicken (硫酸鶏)
38ca514f53 Update README.md [AUTOGEN] (#4253) 2019-02-15 00:58:57 +09:00
MeiMei
caea0f0376 Change minimum allowed maxNoteTextLength to 0 (#4256) 2019-02-14 16:30:20 +09:00
Lynx Kotoura
25a8b26977 Emojify user.friends of desktop view (#4249) 2019-02-14 13:42:14 +09:00
Lynx Kotoura
bcaefe8d62 Fix huge close icon of friends-maker (#4245) 2019-02-14 13:41:48 +09:00
MeiMei
46f1e8c599 Restore web max-age to 5 minutes (#4246) 2019-02-14 13:41:28 +09:00
Lynx Kotoura
16230f320e Unify translations of frequently replied users (#4248) 2019-02-14 13:40:48 +09:00
Acid Chicken (硫酸鶏)
ace6419aef 無効化されているタイムラインのフォールバック (#4242)
* Update timeline.vue

* Update home.vue
2019-02-14 13:39:42 +09:00
Lynx Kotoura
77fb9eb2be Fix title of robot icon in user.header of desktop view (#4243) 2019-02-14 03:01:42 +09:00
syuilo
aa7fc7c893 10.84.2 2019-02-14 01:27:22 +09:00
syuilo
8fc170109f Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2019-02-14 01:13:28 +09:00
syuilo
ad12d00d7e ハッシュタグの集計期間を短くした
Resolve #4238 ?
2019-02-14 01:13:19 +09:00
Acid Chicken (硫酸鶏)
fa5ea45726 Docker: Remove unnecessary workaround for BusyBox's "free" (#4199) (#4213)
systeminformation gets incorrect memory information due to BusyBox's
"free" issue.(#3409)
A workaround for avoiding it was made.

But it never works because the runner container has no effect.
It should be deleted.
2019-02-13 23:45:58 +09:00
Acid Chicken (硫酸鶏)
4b6c113251 Add prelude function for URL Query (#4135)
* Update string.ts

* Refactor

* Update string.ts

* Update wrap-url.ts

* Update string.ts

* Update get-static-image-url.ts

* Use querystring.stringify

* Update outbox.ts

* Back to the urlQuery

* Update followers.ts

* Update following.ts

* Update outbox.ts

* Update string.ts

* Update get-static-image-url.ts

* Update string.ts

* Update string.ts

* Separate prelude files
2019-02-13 23:45:35 +09:00
Acid Chicken (硫酸鶏)
3548290ff2 Fix tslint.json styles (#4219) 2019-02-13 23:43:55 +09:00
syuilo
b165b90c40 Update dependencies 2019-02-13 21:57:00 +09:00
syuilo
4ffe9c908b Make ignore pointer events 2019-02-13 21:48:20 +09:00
syuilo
a135f75e71 🎨 2019-02-13 21:47:53 +09:00
Acid Chicken (硫酸鶏)
cbc61ba03d Add GIF badge (#4241) 2019-02-13 21:43:58 +09:00
syuilo
5aa58da918 Migrate cafy to 14.0 (#4240) 2019-02-13 16:33:07 +09:00
MeiMei
b083430011 Sort ISSUE_TEMPLATE (#4236) 2019-02-13 15:59:51 +09:00
Acid Chicken (硫酸鶏)
a8946b0404 Update nodeinfo.ts (#4239)
* Update nodeinfo.ts

* Update nodeinfo.ts

* Update nodeinfo.ts

* Update nodeinfo.ts

* Update nodeinfo.ts
2019-02-13 15:56:45 +09:00
dependabot[bot]
0303bccc61 Update vue-i18n requirement from 8.8.0 to 8.8.1 (#4235)
Updates the requirements on [vue-i18n](https://github.com/kazupon/vue-i18n) to permit the latest version.
- [Release notes](https://github.com/kazupon/vue-i18n/releases)
- [Changelog](https://github.com/kazupon/vue-i18n/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/kazupon/vue-i18n/commits/v8.8.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-13 14:46:41 +09:00
MeiMei
f3ce8564ea よく話すユーザーからサスペンドされたユーザーを隠すなど (#4228)
* Resolve #4226

* fix

* Fix: anonymousでアクセスするとサスペンドユーザーが隠れない

* fix
2019-02-12 17:19:15 +09:00
306 changed files with 4874 additions and 4995 deletions

View File

@@ -16,19 +16,8 @@ executors:
working_directory: /tmp/workspace
docker:
- image: docker:latest
alpine:
working_directory: /tmp/workspace
docker:
- image: alpine:latest
jobs:
ok:
executor: alpine
steps:
- run:
name: OK
command: |
echo -e '\033[0;32mOK\033[0;39m'
build:
executor: default
steps:
@@ -64,8 +53,6 @@ jobs:
key: yarn-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "yarn.lock" }}
paths:
- node_modules
# - store_artifacts:
# path: built
- persist_to_workspace:
root: .
paths:
@@ -134,58 +121,73 @@ workflows:
version: 2
nodejs:
jobs:
- ok:
- hold:
name: manual-build-trigger
type: approval
filters:
branches:
only:
- l10n_develop
- imgbot
- patch-autogen
ignore: master
- build:
name: manual-build
requires:
- manual-build-trigger
filters:
branches:
ignore:
- l10n_develop
- imgbot
- patch-autogen
ignore: master
- build:
name: auto-build
filters:
branches:
only: master
- test:
name: manual-test-with-redis
executor: with-redis
requires:
- build
- manual-build
filters:
branches:
ignore:
# - master
- l10n_develop
- imgbot
- patch-autogen
ignore: master
- test:
name: auto-test-without-redis
executor: with-redis
requires:
- auto-build
filters:
branches:
only: master
- test:
name: manual-test-with-redis
without_redis: true
requires:
- build
- manual-build
filters:
# branches:
# only: master
branches:
ignore:
# - master
- l10n_develop
- imgbot
- patch-autogen
ignore: master
- test:
name: auto-test-without-redis
without_redis: true
requires:
- auto-build
filters:
branches:
only: master
docker:
jobs:
- hold:
name: manual-build-trigger
type: approval
filters:
branches:
ignore: master
- docker:
name: manual-build
requires:
- hold
- manual-build-trigger
filters:
branches:
ignore: master
- docker:
name: auto-build
with_deploy: true
filters:
branches:

View File

@@ -1,6 +1,79 @@
ChangeLog
=========
10.87.3
----------
* 開発モードでビルドしてもスクリプトが404になる問題を修正
* 拡張子判別だとアイコンやバナー設定で対応していないと表示される問題を修正
* フォローリクエスト数がおかしい場合の応急処置APIを追加
* デザインの調整
10.87.2
----------
* みつけるの人気のタグを第2ソートで連合含めたユーザー数にしたりユーザーのタグ以外は除外するように
* デザインの調整
10.87.1
----------
* ハッシュタグ検索で大文字小文字が区別されてしまう問題を修正
10.87.0
----------
* ハッシュタグでユーザー検索できるように
* Exploreページに新規ユーザー一覧を追加
* デッキ使用中にホーム扱いで開かれた時にタイムラインボタン等がない問題を修正
* デッキ使用中に / 以外でリロードした際にホームモードになる問題を修正
10.86.2
----------
* 別タブでルートより下を開いたときにはデッキにしないように
* 横のナビゲーションバーの改善
* MIDIファイルがオーディオ扱いになる問題を修正
* ミュートワードで正規表現を使えるように
* デッキで無効になったタイムラインに警告を表示するように
* デザインの調整
* その他細かな修正
10.86.1
----------
* ナビゲーションバーの「ホーム」を「タイムライン」に改称
* モバイル版でユーザーページが二重に描画される問題を修正
* ユーザー一覧の「もっと読み込む」の動作がおかしい問題を修正
* デザインの調整
10.86.0
----------
* Exploreページを実装
* UIを改良
* その他細かな修正
10.85.2
----------
* デッキから フォロー/フォロワー ページに行けるように
* ナビゲーションが発生したときに最上部までスクロールように
* 検索結果でページ遷移が発生する問題を修正
* デザインの調整
10.85.1
----------
* ローカルのみ投稿をログイン画面のタイムラインに表示しないように
* ナビゲーションバーを横にしてるとデッキに行けない問題を修正
10.85.0
----------
* デスクトップ版のUIを改良
* 投稿ハイライトページを実装
* 無効化されているタイムラインのフォールバック
* 既にフォローされている場合はフォローリクエストを生成しないように
* その他細かな修正
10.84.2
----------
* GIF画像にGIFバッジを表示
* よく話すユーザーからサスペンドされたユーザーを隠すなど
* nodeinfoが重い問題を修正
* ハッシュタグクラウド取得が重い問題を軽減
10.84.1
----------
* deckにフォローされていますマークを追加

View File

@@ -8,7 +8,6 @@ WORKDIR /misskey
FROM base AS builder
RUN unlink /usr/bin/free
RUN apk add --no-cache \
autoconf \
automake \
@@ -20,7 +19,6 @@ RUN apk add --no-cache \
make \
nasm \
pkgconfig \
procps \
python \
zlib-dev
RUN npm i -g yarn
@@ -34,6 +32,7 @@ FROM base AS runner
RUN apk add --no-cache \
ffmpeg \
tini
RUN npm i -g web-push
ENTRYPOINT ["/sbin/tini", "--"]
COPY --from=builder /misskey/node_modules ./node_modules

View File

@@ -94,7 +94,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<!-- PATREON_START -->
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/1?token-time=2145916800&token-hash=WeuDzzz24cRXJogyIkU-mxARqkdyms-rcZKbO-GpGjw%3D" alt="weep" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=prtYqPOiSHBulhM7NU0VzMaWx39-9ntdq25b6kafDNA%3D" alt="negao" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/12059069" alt="naga_rus" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/3?token-time=2145916800&token-hash=c8HeVqLtmdgH-gSBJg8i10gmOcwllM87MDHeznl3el0%3D" alt="Melilot" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/3?token-time=2145916800&token-hash=LtV2lRi3L2jOWMLwccr9qWYfPrFlzIo2jYZHKzHEb6k%3D" alt="Xeltica" width="100"></td>
@@ -102,7 +102,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=1FlxS9MEgmNGH_RHUVHbO5hIXB5I1z0lvA33CTvYvjA%3D" alt="gutfuckllc" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/weepjp">weep</a></td>
<td><a href="https://www.patreon.com/negao">negao</a></td>
<td><a href="https://www.patreon.com/user?u=12059069">naga_rus</a></td>
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
<td><a href="https://www.patreon.com/Xeltica">Xeltica</a></td>
@@ -115,6 +115,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=2PsbFNw0tnubZzgSXD01R6hIgncfiElG7H7HX2Y3dyo%3D" alt="nemu" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3?token-time=2145916800&token-hash=9JtETp0X8gI280Ne1E8bxn6j4Lw5o2k4mJkICx97V_k%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4?token-time=2145916800&token-hash=SbdZeN5SmsuT9stD6v0jN1z0hftg0FmRiCTxysU0Ihw%3D" alt="Damillora" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8241184/39e18850e87a449e9c9a71acb3310ebd/3?token-time=2145916800&token-hash=gMq30aylxu5v3G8pRhWR5jeRBbYWEoRKjGbNeiCQz5g%3D" alt="Acid Chicken" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/2?token-time=2145916800&token-hash=zcwFxb2zopzWwksKVU1YpfAEjsl4yKT02aQ6yiAFRiQ%3D" alt="natalie" width="100"></td>
@@ -125,6 +126,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
<td><a href="https://www.patreon.com/acid_chicken">Acid Chicken</a></td>
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
@@ -142,7 +144,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Wed, 06 Feb 2019 18:18:05 UTC
**Last updated:** Fri, 15 Feb 2019 19:12:06 UTC
<!-- PATREON_END -->
:four_leaf_clover: Copyright

View File

@@ -122,6 +122,8 @@ CentOSで1024以下のポートを使用してMisskeyを使用する場合は`Ex
4. `npm run build`
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
なにか問題が発生した場合は、`npm run clean`すると直る場合があります。
----------------------------------------------------------------
なにかお困りのことがありましたらお気軽にご連絡ください。

View File

@@ -1591,13 +1591,13 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "キーワード"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue:

View File

@@ -1591,13 +1591,13 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "Schlagwörter"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue:

View File

@@ -1591,13 +1591,13 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "キーワード"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue:

View File

@@ -1591,13 +1591,13 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "キーワード"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue:

View File

@@ -28,6 +28,8 @@ common:
load-more: "もっと読み込む"
enter-password: "パスワードを入力してください"
2fa: "二段階認証"
customize-home: "ホームをカスタマイズ"
featured-notes: "ハイライト"
got-it: "わかった"
customization-tips:
@@ -58,6 +60,15 @@ common:
trash: "ゴミ箱"
drive: "ドライブ"
messaging: "トーク"
deck: "デッキ"
timeline: "タイムライン"
explore: "みつける"
following: "フォロー中"
followers: "フォロワー"
empty-timeline-info:
follow-users-to-make-your-timeline: "ユーザーをフォローすると投稿がタイムラインに表示されます。"
explore: "ユーザーを探索する"
weekday-short:
sunday: "日"
@@ -213,6 +224,19 @@ auth/views/index.vue:
error: "セッションが存在しません。"
sign-in: "サインインしてください"
common/views/pages/explore.vue:
verified-users: "公式アカウント"
popular-users: "人気のユーザー"
recently-updated-users: "最近投稿したユーザー"
recently-registered-users: "新規ユーザー"
popular-tags: "人気のタグ"
federated: "連合"
explore: "{host}を探索"
users-info: "現在{users}ユーザーが登録されています"
common/views/components/user-list.vue:
no-users: "ユーザーがいません"
common/views/components/games/reversi/reversi.vue:
matching:
waiting-for: "{}を待っています"
@@ -761,13 +785,6 @@ desktop/views/components/following-window.vue:
desktop/views/components/following.vue:
empty: "フォロー中のユーザーはいないようです。"
desktop/views/components/friends-maker.vue:
title: "気になるユーザーをフォロー:"
empty: "おすすめのユーザーは見つかりませんでした。"
fetching: "読み込んでいます"
refresh: "もっと見る"
close: "閉じる"
desktop/views/components/game-window.vue:
game: "リバーシ"
@@ -862,6 +879,9 @@ desktop/views/components/renote-form.vue:
desktop/views/components/renote-form-window.vue:
title: "この投稿をRenoteしますか"
desktop/views/components/timeline.core.vue:
empty: "投稿がありません"
desktop/views/pages/user-following-or-followers.vue:
following: "{user}のフォロー"
followers: "{user}のフォロワー"
@@ -893,14 +913,10 @@ desktop/views/components/settings.vue:
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
auto-popout: "ウィンドウの自動ポップアウト"
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
deck-nav: "デッキ内ナビゲーション"
deck-nav-desc: "デッキを使用しているとき、ナビゲーションが発生する際にページ遷移を行わずに一時的なカラムで受けるようにします。"
keep-cw: "CW保持"
keep-cw-desc: "投稿にリプライする際、リプライ元の投稿にCWが設定されていたとき、デフォルトで同じCWを設定するようにします。"
deck-default: "デッキをデフォルトのUIにする"
display: "デザインと表示"
customize: "ホームをカスタマイズ"
wallpaper: "壁紙"
choose-wallpaper: "壁紙を選択"
delete-wallpaper: "壁紙を削除"
@@ -1076,15 +1092,12 @@ desktop/views/components/ui.header.account.vue:
favorites: "お気に入り"
lists: "リスト"
follow-requests: "フォロー申請"
customize: "ホームのカスタマイズ"
admin: "管理"
settings: "設定"
signout: "サインアウト"
dark: "闇に飲まれる"
desktop/views/components/ui.header.nav.vue:
home: "ホーム"
deck: "デッキ"
game: "ゲーム"
desktop/views/components/ui.header.notifications.vue:
@@ -1447,9 +1460,6 @@ desktop/views/pages/welcome.vue:
desktop/views/pages/drive.vue:
title: "Misskey Drive"
desktop/views/pages/home-customize.vue:
title: "ホームのカスタマイズ"
desktop/views/pages/note.vue:
prev: "前の投稿"
next: "次の投稿"
@@ -1490,10 +1500,6 @@ desktop/views/pages/user/user.photos.vue:
loading: "読み込み中"
no-photos: "写真はありません"
desktop/views/pages/user/user.profile.vue:
follows-you: "フォローされています"
menu: "メニュー"
desktop/views/pages/user/user.header.vue:
posts: "投稿"
following: "フォロー"
@@ -1503,6 +1509,7 @@ desktop/views/pages/user/user.header.vue:
year: "年"
month: "月"
day: "日"
follows-you: "フォローされています"
desktop/views/pages/user/user.timeline.vue:
default: "投稿"
@@ -1586,13 +1593,6 @@ common/views/components/follow-button.vue:
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "気になるユーザーをフォロー"
empty: "おすすめのユーザーは見つかりませんでした。"
fetching: "読み込んでいます"
refresh: "もっと見る"
close: "閉じる"
mobile/views/components/note.vue:
private: "この投稿は非公開です"
deleted: "この投稿は削除されました"
@@ -1661,10 +1661,6 @@ mobile/views/components/user-timeline.vue:
no-notes: "このユーザーは投稿していないようです。"
no-notes-with-media: "メディア付き投稿はありません。"
mobile/views/components/users-list.vue:
all: "すべて"
known: "知り合い"
mobile/views/pages/favorites.vue:
title: "お気に入り"
@@ -1689,6 +1685,9 @@ mobile/views/pages/home.vue:
mentions: "あなた宛て"
messages: "メッセージ"
mobile/views/pages/home.timeline.vue:
empty: "投稿がありません"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
@@ -1791,7 +1790,7 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "キーワード"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
@@ -1799,7 +1798,7 @@ mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
@@ -1827,6 +1826,9 @@ deck:
rename: "名前を変更"
stack-left: "左に重ねる"
pop-right: "右に出す"
disabled-timeline:
title: "無効化されたタイムライン"
description: "サーバーの運営者により、このタイムラインは使用できない状態に設定されています。"
deck/deck.tl-column.vue:
is-media-only: "メディア投稿のみ"

View File

@@ -1591,13 +1591,13 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "Nøkkelord"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue:

View File

@@ -1597,7 +1597,7 @@ mobile/views/pages/user/home.vue:
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "Nenhuma mensagem"
mobile/views/pages/user/home.photos.vue:

View File

@@ -1591,13 +1591,13 @@ mobile/views/pages/user/home.vue:
activity: "アクティビティ"
keywords: "キーワード"
domains: "頻出ドメイン"
frequently-replied-users: "よく話すユーザー"
frequently-replied-users: "よく話すユーザー"
followers-you-know: "知り合いのフォロワー"
last-used-at: "最終ログイン"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "知り合いのユーザーはいません"
mobile/views/pages/user/home.friends.vue:
no-users: "よく話すユーザーはいません"
no-users: "よく話すユーザーはいません"
mobile/views/pages/user/home.notes.vue:
no-notes: "投稿はありません"
mobile/views/pages/user/home.photos.vue:

View File

@@ -1,8 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.84.1",
"clientVersion": "2.0.14252",
"version": "10.87.3",
"codename": "nighthike",
"repository": {
"type": "git",
@@ -67,7 +66,7 @@
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.5",
"@types/mongodb": "3.1.19",
"@types/node": "10.12.21",
"@types/node": "10.12.24",
"@types/nodemailer": "4.6.5",
"@types/nprogress": "0.0.29",
"@types/oauth": "0.9.1",
@@ -83,7 +82,7 @@
"@types/request-stats": "3.0.0",
"@types/rimraf": "2.0.2",
"@types/seedrandom": "2.4.27",
"@types/sharp": "0.21.1",
"@types/sharp": "0.21.2",
"@types/showdown": "1.9.2",
"@types/speakeasy": "2.0.3",
"@types/systeminformation": "3.23.1",
@@ -103,14 +102,14 @@
"bcryptjs": "2.4.3",
"bee-queue": "1.2.2",
"bootstrap-vue": "2.0.0-rc.11",
"cafy": "12.1.0",
"cafy": "14.0.1",
"chai": "4.2.0",
"chai-http": "4.2.1",
"chalk": "2.4.2",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "2.1.0",
"cssnano": "4.1.8",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"deep-equal": "1.0.1",
"deepcopy": "0.6.3",
@@ -229,11 +228,11 @@
"uuid": "3.3.2",
"v-animate-css": "0.0.3",
"video-thumbnail-generator": "1.1.3",
"vue": "2.6.5",
"vue": "2.6.6",
"vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "3.0.0",
"vue-i18n": "8.8.0",
"vue-i18n": "8.8.1",
"vue-js-modal": "1.3.28",
"vue-loader": "15.6.2",
"vue-marquee-text-component": "1.1.1",
@@ -242,7 +241,7 @@
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.10",
"vue-template-compiler": "2.6.5",
"vue-template-compiler": "2.6.6",
"vuedraggable": "2.17.0",
"vuewordcloud": "18.7.11",
"vuex": "3.1.0",

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title"><fa :icon="faExclamationCircle"/> {{ $t('title') }}</div>
<template #title><fa :icon="faExclamationCircle"/> {{ $t('title') }}</template>
<section class="fit-top">
<sequential-entrance animation="entranceFromTop" delay="25">
<div v-for="report in userReports" :key="report.id" class="haexwsjc">

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title"><fa icon="broadcast-tower"/> {{ $t('announcements') }}</div>
<template #title><fa icon="broadcast-tower"/> {{ $t('announcements') }}</template>
<section v-for="(announcement, i) in announcements" class="fit-top">
<ui-input v-model="announcement.title" @change="save">
<span>{{ $t('title') }}</span>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title"><fa :icon="faTerminal"/> {{ $t('operation') }}</div>
<template #title><fa :icon="faTerminal"/> {{ $t('operation') }}</template>
<section class="fit-top">
<ui-input v-model="target" type="text">
<span>{{ $t('fileid-or-url') }}</span>
@@ -17,18 +17,18 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="faCloud"/> {{ $t('@.drive') }}</div>
<template #title><fa :icon="faCloud"/> {{ $t('@.drive') }}</template>
<section class="fit-top">
<ui-horizon-group inputs>
<ui-select v-model="sort">
<span slot="label">{{ $t('sort.title') }}</span>
<template #label>{{ $t('sort.title') }}</template>
<option value="-createdAt">{{ $t('sort.createdAtAsc') }}</option>
<option value="+createdAt">{{ $t('sort.createdAtDesc') }}</option>
<option value="-size">{{ $t('sort.sizeAsc') }}</option>
<option value="+size">{{ $t('sort.sizeDesc') }}</option>
</ui-select>
<ui-select v-model="origin">
<span slot="label">{{ $t('origin.title') }}</span>
<template #label>{{ $t('origin.title') }}</template>
<option value="combined">{{ $t('origin.combined') }}</option>
<option value="local">{{ $t('origin.local') }}</option>
<option value="remote">{{ $t('origin.remote') }}</option>

View File

@@ -1,20 +1,20 @@
<template>
<div>
<ui-card>
<div slot="title"><fa icon="plus"/> {{ $t('add-emoji.title') }}</div>
<template #title><fa icon="plus"/> {{ $t('add-emoji.title') }}</template>
<section class="fit-top">
<ui-horizon-group inputs>
<ui-input v-model="name">
<span>{{ $t('add-emoji.name') }}</span>
<span slot="desc">{{ $t('add-emoji.name-desc') }}</span>
<template #desc>{{ $t('add-emoji.name-desc') }}</template>
</ui-input>
<ui-input v-model="aliases">
<span>{{ $t('add-emoji.aliases') }}</span>
<span slot="desc">{{ $t('add-emoji.aliases-desc') }}</span>
<template #desc>{{ $t('add-emoji.aliases-desc') }}</template>
</ui-input>
</ui-horizon-group>
<ui-input v-model="url">
<i slot="icon"><fa icon="link"/></i>
<template #icon><fa icon="link"/></template>
<span>{{ $t('add-emoji.url') }}</span>
</ui-input>
<ui-info>{{ $t('add-emoji.info') }}</ui-info>
@@ -23,7 +23,7 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="faGrin"/> {{ $t('emojis.title') }}</div>
<template #title><fa :icon="faGrin"/> {{ $t('emojis.title') }}</template>
<section v-for="emoji in emojis" class="oryfrbft">
<div>
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
@@ -38,7 +38,7 @@
</ui-input>
</ui-horizon-group>
<ui-input v-model="emoji.url">
<i slot="icon"><fa icon="link"/></i>
<template #icon><fa icon="link"/></template>
<span>{{ $t('add-emoji.url') }}</span>
</ui-input>
<ui-horizon-group class="fit-bottom">

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title"><fa :icon="faTerminal"/> {{ $t('federation') }}</div>
<template #title><fa :icon="faTerminal"/> {{ $t('federation') }}</template>
<section class="fit-top">
<ui-input class="target" v-model="target" type="text" @enter="showInstance()">
<span>{{ $t('host') }}</span>
@@ -74,11 +74,11 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="faServer"/> {{ $t('instances') }}</div>
<template #title><fa :icon="faServer"/> {{ $t('instances') }}</template>
<section class="fit-top">
<ui-horizon-group inputs>
<ui-select v-model="sort">
<span slot="label">{{ $t('sort') }}</span>
<template #label>{{ $t('sort') }}</template>
<option value="-caughtAt">{{ $t('sorts.caughtAtAsc') }}</option>
<option value="+caughtAt">{{ $t('sorts.caughtAtDesc') }}</option>
<option value="-lastCommunicatedAt">{{ $t('sorts.lastCommunicatedAtAsc') }}</option>
@@ -97,7 +97,7 @@
<option value="+driveFiles">{{ $t('sorts.driveFilesDesc') }}</option>
</ui-select>
<ui-select v-model="state">
<span slot="label">{{ $t('state') }}</span>
<template #label>{{ $t('state') }}</template>
<option value="all">{{ $t('states.all') }}</option>
<option value="blocked">{{ $t('states.blocked') }}</option>
<option value="notResponding">{{ $t('states.not-responding') }}</option>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title">{{ $t('hided-tags') }}</div>
<template #title>{{ $t('hided-tags') }}</template>
<section>
<textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hidedTags"></textarea>
<ui-button @click="save">{{ $t('save') }}</ui-button>

View File

@@ -1,20 +1,20 @@
<template>
<div>
<ui-card>
<div slot="title"><fa icon="cog"/> {{ $t('instance') }}</div>
<template #title><fa icon="cog"/> {{ $t('instance') }}</template>
<section class="fit-top fit-bottom">
<ui-input :value="host" readonly>{{ $t('host') }}</ui-input>
<ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
<ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
<ui-input v-model="mascotImageUrl"><i slot="icon"><fa icon="link"/></i>{{ $t('logo-url') }}</ui-input>
<ui-input v-model="bannerUrl"><i slot="icon"><fa icon="link"/></i>{{ $t('banner-url') }}</ui-input>
<ui-input v-model="errorImageUrl"><i slot="icon"><fa icon="link"/></i>{{ $t('error-image-url') }}</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>
<ui-input v-model="mascotImageUrl"><template #icon><fa icon="link"/></template>{{ $t('logo-url') }}</ui-input>
<ui-input v-model="bannerUrl"><template #icon><fa icon="link"/></template>{{ $t('banner-url') }}</ui-input>
<ui-input v-model="errorImageUrl"><template #icon><fa icon="link"/></template>{{ $t('error-image-url') }}</ui-input>
<ui-input v-model="languages"><template #icon><fa icon="language"/></template>{{ $t('languages') }}<template #desc>{{ $t('languages-desc') }}</template></ui-input>
</section>
<section class="fit-bottom">
<header><fa :icon="faHeadset"/> {{ $t('maintainer-config') }}</header>
<ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="farEnvelope"/></i>{{ $t('maintainer-email') }}</ui-input>
<ui-input v-model="maintainerEmail" type="email"><template #icon><fa :icon="farEnvelope"/></template>{{ $t('maintainer-email') }}</ui-input>
</section>
<section class="fit-top fit-bottom">
<ui-input v-model="maxNoteTextLength">{{ $t('max-note-text-length') }}</ui-input>
@@ -27,28 +27,28 @@
</section>
<section class="fit-bottom">
<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
<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" 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" type="number" :disabled="!cacheRemoteFiles">{{ $t('remote-drive-capacity-mb') }}<span slot="suffix">MB</span><span slot="desc">{{ $t('mb') }}</span></ui-input>
<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<template #desc>{{ $t('cache-remote-files-desc') }}</template></ui-switch>
<ui-input v-model="localDriveCapacityMb" type="number">{{ $t('local-drive-capacity-mb') }}<template #suffix>MB</template><template #desc>{{ $t('mb') }}</template></ui-input>
<ui-input v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $t('remote-drive-capacity-mb') }}<template #suffix>MB</template><template #desc>{{ $t('mb') }}</template></ui-input>
</section>
<section class="fit-bottom">
<header><fa :icon="faShieldAlt"/> {{ $t('recaptcha-config') }}</header>
<ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch>
<ui-info>{{ $t('recaptcha-info') }}</ui-info>
<ui-horizon-group inputs>
<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><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>{{ $t('recaptcha-secret-key') }}</ui-input>
<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-site-key') }}</ui-input>
<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa icon="key"/></template>{{ $t('recaptcha-secret-key') }}</ui-input>
</ui-horizon-group>
</section>
<section>
<header><fa :icon="faGhost"/> {{ $t('proxy-account-config') }}</header>
<ui-info>{{ $t('proxy-account-info') }}</ui-info>
<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-input v-model="proxyAccount"><template #prefix>@</template>{{ $t('proxy-account-username') }}<template #desc>{{ $t('proxy-account-username-desc') }}</template></ui-input>
<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
</section>
<section>
<header><fa :icon="farEnvelope"/> {{ $t('email-config') }}</header>
<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<span slot="desc">{{ $t('email-config-info') }}</span></ui-switch>
<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<template #desc>{{ $t('email-config-info') }}</template></ui-switch>
<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input>
<ui-horizon-group inputs>
<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input>
@@ -58,15 +58,15 @@
<ui-input v-model="smtpUser" :disabled="!enableEmail">{{ $t('smtp-user') }}</ui-input>
<ui-input v-model="smtpPass" type="password" :withPasswordToggle="true" :disabled="!enableEmail">{{ $t('smtp-pass') }}</ui-input>
</ui-horizon-group>
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<span slot="desc">{{ $t('smtp-secure-info') }}</span></ui-switch>
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<template #desc>{{ $t('smtp-secure-info') }}</template></ui-switch>
</section>
<section>
<header><fa :icon="faBolt"/> {{ $t('serviceworker-config') }}</header>
<ui-switch v-model="enableServiceWorker">{{ $t('enable-serviceworker') }}<span slot="desc">{{ $t('serviceworker-info') }}</span></ui-switch>
<ui-switch v-model="enableServiceWorker">{{ $t('enable-serviceworker') }}<template #desc>{{ $t('serviceworker-info') }}</template></ui-switch>
<ui-info>{{ $t('vapid-info') }}<br><code>npm i web-push -g<br>web-push generate-vapid-keys</code></ui-info>
<ui-horizon-group inputs class="fit-bottom">
<ui-input v-model="swPublicKey" :disabled="!enableServiceWorker"><i slot="icon"><fa icon="key"/></i>{{ $t('vapid-publickey') }}</ui-input>
<ui-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><i slot="icon"><fa icon="key"/></i>{{ $t('vapid-privatekey') }}</ui-input>
<ui-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-publickey') }}</ui-input>
<ui-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa icon="key"/></template>{{ $t('vapid-privatekey') }}</ui-input>
</ui-horizon-group>
</section>
<section>
@@ -76,8 +76,8 @@
<section>
<header><fa :icon="faUserPlus"/> {{ $t('user-recommendation-config') }}</header>
<ui-switch v-model="enableExternalUserRecommendation">{{ $t('enable-external-user-recommendation') }}</ui-switch>
<ui-input v-model="externalUserRecommendationEngine" :disabled="!enableExternalUserRecommendation">{{ $t('external-user-recommendation-engine') }}<span slot="desc">{{ $t('external-user-recommendation-engine-desc') }}</span></ui-input>
<ui-input v-model="externalUserRecommendationTimeout" type="number" :disabled="!enableExternalUserRecommendation">{{ $t('external-user-recommendation-timeout') }}<span slot="suffix">ms</span><span slot="desc">{{ $t('external-user-recommendation-timeout-desc') }}</span></ui-input>
<ui-input v-model="externalUserRecommendationEngine" :disabled="!enableExternalUserRecommendation">{{ $t('external-user-recommendation-engine') }}<template #desc>{{ $t('external-user-recommendation-engine-desc') }}</template></ui-input>
<ui-input v-model="externalUserRecommendationTimeout" type="number" :disabled="!enableExternalUserRecommendation">{{ $t('external-user-recommendation-timeout') }}<template #suffix>ms</template><template #desc>{{ $t('external-user-recommendation-timeout-desc') }}</template></ui-input>
</section>
<section>
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
@@ -85,7 +85,7 @@
</ui-card>
<ui-card>
<div slot="title">{{ $t('invite') }}</div>
<template #title>{{ $t('invite') }}</template>
<section>
<ui-button @click="invite">{{ $t('invite') }}</ui-button>
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
@@ -93,12 +93,12 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</div>
<template #title><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</template>
<section>
<ui-switch v-model="enableTwitterIntegration">{{ $t('enable-twitter-integration') }}</ui-switch>
<ui-horizon-group>
<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>{{ $t('twitter-integration-consumer-secret') }}</ui-input>
<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-key') }}</ui-input>
<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><fa icon="key"/></template>{{ $t('twitter-integration-consumer-secret') }}</ui-input>
</ui-horizon-group>
<ui-info>{{ $t('twitter-integration-info', { url: `${url}/api/tw/cb` }) }}</ui-info>
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
@@ -106,12 +106,12 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</div>
<template #title><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</template>
<section>
<ui-switch v-model="enableGithubIntegration">{{ $t('enable-github-integration') }}</ui-switch>
<ui-horizon-group>
<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>{{ $t('github-integration-client-secret') }}</ui-input>
<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-id') }}</ui-input>
<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><fa icon="key"/></template>{{ $t('github-integration-client-secret') }}</ui-input>
</ui-horizon-group>
<ui-info>{{ $t('github-integration-info', { url: `${url}/api/gh/cb` }) }}</ui-info>
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
@@ -119,12 +119,12 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</div>
<template #title><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</template>
<section>
<ui-switch v-model="enableDiscordIntegration">{{ $t('enable-discord-integration') }}</ui-switch>
<ui-horizon-group>
<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('discord-integration-client-id') }}</ui-input>
<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('discord-integration-client-secret') }}</ui-input>
<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-id') }}</ui-input>
<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><fa icon="key"/></template>{{ $t('discord-integration-client-secret') }}</ui-input>
</ui-horizon-group>
<ui-info>{{ $t('discord-integration-info', { url: `${url}/api/dc/cb` }) }}</ui-info>
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>

View File

@@ -1,10 +1,10 @@
<template>
<div>
<ui-card>
<div slot="title"><fa icon="plus"/> {{ $t('add-moderator.title') }}</div>
<template #title><fa icon="plus"/> {{ $t('add-moderator.title') }}</template>
<section class="fit-top">
<ui-input v-model="username" type="text">
<span slot="prefix">@</span>
<template #prefix>@</template>
</ui-input>
<ui-horizon-group>
<ui-button @click="add" :disabled="changing">{{ $t('add-moderator.add') }}</ui-button>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title">{{ $t('operation') }}</div>
<template #title>{{ $t('operation') }}</template>
<section>
<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>
</section>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<ui-card>
<div slot="title"><fa :icon="faTerminal"/> {{ $t('operation') }}</div>
<template #title><fa :icon="faTerminal"/> {{ $t('operation') }}</template>
<section class="fit-top">
<ui-input class="target" v-model="target" type="text" @enter="showUser">
<span>{{ $t('username-or-userid') }}</span>
@@ -32,18 +32,18 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="faUsers"/> {{ $t('users.title') }}</div>
<template #title><fa :icon="faUsers"/> {{ $t('users.title') }}</template>
<section class="fit-top">
<ui-horizon-group inputs>
<ui-select v-model="sort">
<span slot="label">{{ $t('users.sort.title') }}</span>
<template #label>{{ $t('users.sort.title') }}</template>
<option value="-createdAt">{{ $t('users.sort.createdAtAsc') }}</option>
<option value="+createdAt">{{ $t('users.sort.createdAtDesc') }}</option>
<option value="-updatedAt">{{ $t('users.sort.updatedAtAsc') }}</option>
<option value="+updatedAt">{{ $t('users.sort.updatedAtDesc') }}</option>
</ui-select>
<ui-select v-model="state">
<span slot="label">{{ $t('users.state.title') }}</span>
<template #label>{{ $t('users.state.title') }}</template>
<option value="all">{{ $t('users.state.all') }}</option>
<option value="admin">{{ $t('users.state.admin') }}</option>
<option value="moderator">{{ $t('users.state.moderator') }}</option>
@@ -52,7 +52,7 @@
<option value="suspended">{{ $t('users.state.suspended') }}</option>
</ui-select>
<ui-select v-model="origin">
<span slot="label">{{ $t('users.origin.title') }}</span>
<template #label>{{ $t('users.origin.title') }}</template>
<option value="combined">{{ $t('users.origin.combined') }}</option>
<option value="local">{{ $t('users.origin.local') }}</option>
<option value="remote">{{ $t('users.origin.remote') }}</option>
@@ -339,7 +339,7 @@ export default Vue.extend({
});
return !confirm.canceled;
}
},
fetchUsers() {
this.$root.api('admin/show-users', {

View File

@@ -138,8 +138,8 @@
const meta = await res.json();
// Compare versions
if (meta.clientVersion != ver) {
localStorage.setItem('v', meta.clientVersion);
if (meta.version != ver) {
localStorage.setItem('v', meta.version);
alert(
'Misskeyの新しいバージョンがあります。ページを再度読み込みします。' +

View File

@@ -1,8 +1,8 @@
import { clientVersion as current } from '../../config';
import { version as current } from '../../config';
export default async function($root: any, force = false, silent = false) {
const meta = await $root.getMeta(force);
const newer = meta.clientVersion;
const newer = meta.version;
if (newer != current) {
localStorage.setItem('should-refresh', 'true');

View File

@@ -1,9 +1,11 @@
import { url as instanceUrl } from '../../config';
import * as url from '../../../../prelude/url';
export function getStaticImageUrl(url: string): string {
const u = new URL(url);
export function getStaticImageUrl(baseUrl: string): string {
const u = new URL(baseUrl);
const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので
let result = `${instanceUrl}/proxy/${dummy}?url=${encodeURIComponent(u.href)}`;
result += '&static=1';
return result;
return `${instanceUrl}/proxy/${dummy}?${url.query({
url: u.href,
static: '1'
})}`;
}

View File

@@ -4,7 +4,8 @@ export default function(me, settings, note) {
const includesMutedWords = (text: string) =>
text
? settings.mutedWords.some(q => q.length > 0 && !q.some(word => !text.includes(word)))
? settings.mutedWords.some(q => q.length > 0 && !q.some(word =>
word.startsWith('/') && word.endsWith('/') ? !(new RegExp(word.substr(1, word.length - 2)).test(text)) : !text.includes(word)))
: false;
return (

View File

@@ -0,0 +1,18 @@
export default {
install(Vue) {
Vue.directive('size', {
inserted(el, binding) {
const query = binding.value;
const width = el.clientWidth;
for (const q of query) {
if (q.lt && (width <= q.lt)) {
el.classList.add(q.class);
}
if (q.gt && (width >= q.gt)) {
el.classList.add(q.class);
}
}
}
});
}
};

View File

@@ -1,5 +1,5 @@
<template>
<div class="mk-activity">
<div>
<div ref="chart"></div>
</div>
</template>
@@ -9,7 +9,17 @@ import Vue from 'vue';
import ApexCharts from 'apexcharts';
export default Vue.extend({
props: ['user'],
props: {
user: {
type: Object,
required: true
},
limit: {
type: Number,
required: false,
default: 21
}
},
data() {
return {
fetching: true,
@@ -21,7 +31,7 @@ export default Vue.extend({
this.$root.api('charts/user/notes', {
userId: this.user.id,
span: 'day',
limit: 21
limit: this.limit
}).then(stats => {
const normal = [];
const reply = [];
@@ -32,7 +42,7 @@ export default Vue.extend({
const m = now.getMonth();
const d = now.getDate();
for (let i = 0; i < 21; i++) {
for (let i = 0; i < this.limit; i++) {
const x = new Date(y, m, d - i);
normal.push([
x,
@@ -99,10 +109,3 @@ export default Vue.extend({
}
});
</script>
<style lang="stylus" scoped>
.mk-activity
max-width 600px
margin 0 auto
</style>

View File

@@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title"><fa icon="key"/> API</div>
<template #title><fa icon="key"/> API</template>
<section class="fit-top">
<ui-input :value="$store.state.i.token" readonly>
@@ -19,7 +19,7 @@
</ui-input>
<ui-textarea v-model="body">
<span>{{ $t('console.parameter') }} (JSON or JSON5)</span>
<span slot="desc">{{ $t('console.credential-info') }}</span>
<template #desc>{{ $t('console.credential-info') }}</template>
</ui-textarea>
<ui-button @click="send" :disabled="sending">
<template v-if="sending">{{ $t('console.sending') }}</template>

View File

@@ -6,7 +6,7 @@
<header v-if="title" v-html="title"></header>
<div class="body" v-if="text" v-html="text"></div>
<ui-input v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></ui-input>
<ui-input v-if="user" v-model="userInputValue" autofocus @keydown="onInputKeydown"><span slot="prefix">@</span></ui-input>
<ui-input v-if="user" v-model="userInputValue" autofocus @keydown="onInputKeydown"><template #prefix>@</template></ui-input>
<ui-select v-if="select" v-model="selectedValue">
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
</ui-select>

View File

@@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title"><fa icon="cloud"/> {{ $t('@.drive') }}</div>
<template #title><fa icon="cloud"/> {{ $t('@.drive') }}</template>
<section v-if="!fetching" class="juakhbxthdewydyreaphkepoxgxvfogn">
<div class="meter"><div :style="meterStyle"></div></div>

View File

@@ -0,0 +1,11 @@
<template>
<div>
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
});
</script>

View File

@@ -1,8 +1,9 @@
<template>
<button class="wfliddvnhxvyusikowhxozkyxyenqxqr"
:class="{ wait, block, mini, active: isFollowing || hasPendingFollowRequestFromYou }"
:class="{ wait, block, inline, mini, active: isFollowing || hasPendingFollowRequestFromYou }"
@click="onClick"
:disabled="wait"
:inline="inline"
>
<template v-if="!wait">
<fa :icon="iconAndText[0]"/> <template v-if="!mini">{{ iconAndText[1] }}</template>
@@ -28,6 +29,11 @@ export default Vue.extend({
required: false,
default: false
},
inline: {
type: Boolean,
required: false,
default: false
},
mini: {
type: Boolean,
required: false,
@@ -128,6 +134,9 @@ export default Vue.extend({
border solid 1px var(--primary)
border-radius 36px
&.inline
display inline-block
&.mini
padding 0
min-width 0

View File

@@ -1,5 +1,6 @@
import Vue from 'vue';
import dummy from './dummy.vue';
import userName from './user-name.vue';
import followButton from './follow-button.vue';
import error from './error.vue';
@@ -32,6 +33,7 @@ import urlPreview from './url-preview.vue';
import fileTypeIcon from './file-type-icon.vue';
import emoji from './emoji.vue';
import welcomeTimeline from './welcome-timeline.vue';
import userList from './user-list.vue';
import uiInput from './ui/input.vue';
import uiButton from './ui/button.vue';
import uiHorizonGroup from './ui/horizon-group.vue';
@@ -46,6 +48,7 @@ import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
Vue.component('mfm', misskeyFlavoredMarkdown);
Vue.component('mk-dummy', dummy);
Vue.component('mk-user-name', userName);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-error', error);
@@ -77,6 +80,7 @@ Vue.component('mk-url-preview', urlPreview);
Vue.component('mk-file-type-icon', fileTypeIcon);
Vue.component('mk-emoji', emoji);
Vue.component('mk-welcome-timeline', welcomeTimeline);
Vue.component('mk-user-list', userList);
Vue.component('ui-input', uiInput);
Vue.component('ui-button', uiButton);
Vue.component('ui-horizon-group', uiHorizonGroup);

View File

@@ -1,6 +1,6 @@
<template>
<ui-card v-if="enableTwitterIntegration || enableDiscordIntegration || enableGithubIntegration">
<div slot="title"><fa icon="share-alt"/> {{ $t('title') }}</div>
<template #title><fa icon="share-alt"/> {{ $t('title') }}</template>
<section v-if="enableTwitterIntegration">
<header><fa :icon="['fab', 'twitter']"/> Twitter</header>

View File

@@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title"><fa icon="language"/> {{ $t('title') }}</div>
<template #title><fa icon="language"/> {{ $t('title') }}</template>
<section class="fit-top">
<ui-select v-model="lang" :placeholder="$t('pick-language')">

View File

@@ -5,7 +5,7 @@
<b>{{ $t('sensitive') }}</b>
<span>{{ $t('click-to-show') }}</span>
</div>
<div class="audio" v-else-if="media.type.startsWith('audio')">
<div class="audio" v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'">
<audio class="audio"
:src="media.url"
:title="media.name"

View File

@@ -10,7 +10,9 @@
:style="style"
:title="image.name"
@click.prevent="onClick"
></a>
>
<div v-if="image.type === 'image/gif'">GIF</div>
</a>
</template>
<script lang="ts">
@@ -76,6 +78,20 @@ export default Vue.extend({
background-size contain
background-repeat no-repeat
> div
background-color var(--text)
border-radius var(--round)
color var(--secondary)
display inline-block
font-size 14px
font-weight bold
left 12px
opacity .5
padding 0 6px
text-align center
top 12px
pointer-events none
.qjewsnkgzzxlxtzncydssfbgjibiehcy
display flex
justify-content center

View File

@@ -40,7 +40,11 @@ export default Vue.component('misskey-flavored-markdown', {
},
customEmojis: {
required: false,
}
},
isNote: {
type: Boolean,
default: true
},
},
render(createElement) {
@@ -204,7 +208,7 @@ export default Vue.component('misskey-flavored-markdown', {
return [createElement('router-link', {
key: Math.random(),
attrs: {
to: `/tags/${encodeURIComponent(token.node.props.hashtag)}`,
to: this.isNote ? `/tags/${encodeURIComponent(token.node.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.node.props.hashtag)}`,
style: 'color:var(--mfmHashtag);'
}
}, `#${token.node.props.hashtag}`)];

View File

@@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title"><fa icon="ban"/> {{ $t('mute-and-block') }}</div>
<template #title><fa icon="ban"/> {{ $t('mute-and-block') }}</template>
<section>
<header>{{ $t('mute') }}</header>
@@ -25,7 +25,7 @@
<section>
<header>{{ $t('word-mute') }}</header>
<ui-textarea v-model="mutedWords">
{{ $t('muted-words') }}<span slot="desc">{{ $t('muted-words-description') }}</span>
{{ $t('muted-words') }}<template #desc>{{ $t('muted-words-description') }}</template>
</ui-textarea>
<ui-button @click="save">{{ $t('save') }}</ui-button>
</section>

View File

@@ -1,9 +1,9 @@
<template>
<ui-card>
<div slot="title"><fa :icon="['far', 'bell']"/> {{ $t('title') }}</div>
<template #title><fa :icon="['far', 'bell']"/> {{ $t('title') }}</template>
<section>
<ui-switch v-model="$store.state.i.settings.autoWatch" @change="onChangeAutoWatch">
{{ $t('auto-watch') }}<span slot="desc">{{ $t('auto-watch-desc') }}</span>
{{ $t('auto-watch') }}<template #desc>{{ $t('auto-watch-desc') }}</template>
</ui-switch>
<section>
<ui-button @click="readAllNotifications">{{ $t('mark-as-read-all-notifications') }}</ui-button>

View File

@@ -1,6 +1,6 @@
<template>
<ui-card>
<div slot="title"><fa icon="user"/> {{ $t('title') }}</div>
<template #title><fa icon="user"/> {{ $t('title') }}</template>
<section class="esokaraujimuwfttfzgocmutcihewscl">
<div class="header" :style="bannerStyle">
@@ -14,41 +14,41 @@
<ui-input v-model="username" readonly>
<span>{{ $t('account') }}</span>
<span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</ui-input>
<ui-input v-model="location">
<span>{{ $t('location') }}</span>
<span slot="prefix"><fa icon="map-marker-alt"/></span>
<template #prefix><fa icon="map-marker-alt"/></template>
</ui-input>
<ui-input v-model="birthday" type="date">
<span slot="title">{{ $t('birthday') }}</span>
<span slot="prefix"><fa icon="birthday-cake"/></span>
<template #title>{{ $t('birthday') }}</template>
<template #prefix><fa icon="birthday-cake"/></template>
</ui-input>
<ui-textarea v-model="description" :max="500">
<span>{{ $t('description') }}</span>
<span slot="desc">{{ $t('you-can-include-hashtags') }}</span>
<template #desc>{{ $t('you-can-include-hashtags') }}</template>
</ui-textarea>
<ui-select v-model="lang">
<span slot="label">{{ $t('language') }}</span>
<span slot="icon"><fa icon="language"/></span>
<template #label>{{ $t('language') }}</template>
<template #icon><fa icon="language"/></template>
<option v-for="lang in unique(Object.values(langmap).map(x => x.nativeName)).map(name => Object.keys(langmap).find(k => langmap[k].nativeName == name))" :value="lang" :key="lang">{{ langmap[lang].nativeName }}</option>
</ui-select>
<ui-input type="file" @change="onAvatarChange">
<span>{{ $t('avatar') }}</span>
<span slot="icon"><fa icon="image"/></span>
<span slot="desc" v-if="avatarUploading">{{ $t('uploading') }}<mk-ellipsis/></span>
<template #icon><fa icon="image"/></template>
<template #desc v-if="avatarUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
</ui-input>
<ui-input type="file" @change="onBannerChange">
<span>{{ $t('banner') }}</span>
<span slot="icon"><fa icon="image"/></span>
<span slot="desc" v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></span>
<template #icon><fa icon="image"/></template>
<template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
</ui-input>
<ui-button @click="save(true)">{{ $t('save') }}</ui-button>

View File

@@ -3,16 +3,16 @@
<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">
<span>{{ $t('username') }}</span>
<span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</ui-input>
<ui-input v-model="password" type="password" :with-password-toggle="true" required styl="fill">
<span>{{ $t('password') }}</span>
<span slot="prefix"><fa icon="lock"/></span>
<template #prefix><fa icon="lock"/></template>
</ui-input>
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill">
<span>{{ $t('@.2fa') }}</span>
<span slot="prefix"><fa icon="gavel"/></span>
<template #prefix><fa icon="gavel"/></template>
</ui-input>
<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('signin') }}</ui-button>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`">{{ $t('signin-with-twitter') }}</a></p>

View File

@@ -3,37 +3,37 @@
<template v-if="meta">
<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill">
<span>{{ $t('invitation-code') }}</span>
<span slot="prefix"><fa icon="id-card-alt"/></span>
<p slot="desc" v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainer.email)"></p>
<template #prefix><fa icon="id-card-alt"/></template>
<template #desc v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainer.email)"></template>
</ui-input>
<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill">
<span>{{ $t('username') }}</span>
<span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span>
<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/> {{ $t('available') }}</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/> {{ $t('error') }}</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/> {{ $t('too-short') }}</p>
<p slot="desc" v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-long') }}</p>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
<template #desc v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner" pulse fixed-width/> {{ $t('checking') }}</template>
<template #desc v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</template>
<template #desc v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</template>
<template #desc v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</template>
<template #desc v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('invalid-format') }}</template>
<template #desc v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-short') }}</template>
<template #desc v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('too-long') }}</template>
</ui-input>
<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill">
<span>{{ $t('password') }}</span>
<span slot="prefix"><fa icon="lock"/></span>
<div slot="desc">
<template #prefix><fa icon="lock"/></template>
<template #desc>
<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/> {{ $t('normal-password') }}</p>
<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('strong-password') }}</p>
</div>
</template>
</ui-input>
<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill">
<span>{{ $t('password') }} ({{ $t('retype') }})</span>
<span slot="prefix"><fa icon="lock"/></span>
<div slot="desc">
<template #prefix><fa icon="lock"/></template>
<template #desc>
<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/> {{ $t('password-not-matched') }}</p>
</div>
</template>
</ui-input>
<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>
<ui-button type="submit">{{ $t('create') }}</ui-button>

View File

@@ -1,10 +1,10 @@
<template>
<ui-card>
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
<template #title><fa icon="palette"/> {{ $t('theme') }}</template>
<section class="nicnklzforebnpfgasiypmpdaaglujqm fit-top">
<label>
<ui-select v-model="light" :placeholder="$t('light-theme')">
<span slot="label"><fa :icon="faSun"/> {{ $t('light-theme') }}</span>
<template #label><fa :icon="faSun"/> {{ $t('light-theme') }}</template>
<optgroup :label="$t('light-themes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
@@ -16,7 +16,7 @@
<label>
<ui-select v-model="dark" :placeholder="$t('dark-theme')">
<span slot="label"><fa :icon="faMoon"/> {{ $t('dark-theme') }}</span>
<template #label><fa :icon="faMoon"/> {{ $t('dark-theme') }}</template>
<optgroup :label="$t('dark-themes')">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>

View File

@@ -359,7 +359,7 @@ root(fill)
display none
> *
display block
display inline-block
min-width 16px
max-width 150px
overflow hidden

View File

@@ -1,7 +1,7 @@
<template>
<div class="cudqjmnl">
<ui-card>
<div slot="title"><fa :icon="faList"/> {{ list.title }}</div>
<template #title><fa :icon="faList"/> {{ list.title }}</template>
<section>
<ui-button @click="rename"><fa :icon="faICursor"/> {{ $t('rename') }}</ui-button>
@@ -10,7 +10,7 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="faUsers"/> {{ $t('users') }}</div>
<template #title><fa :icon="faUsers"/> {{ $t('users') }}</template>
<section>
<sequential-entrance animation="entranceFromTop" delay="25">

View File

@@ -0,0 +1,171 @@
<template>
<ui-container :body-togglable="true">
<template #header><slot></slot></template>
<mk-error v-if="!fetching && !inited" @retry="init()"/>
<div class="efvhhmdq" v-size="[{ lt: 500, class: 'narrow' }]">
<div class="no-users" v-if="inited && us.length == 0">
<p>{{ $t('no-users') }}</p>
</div>
<div class="user" v-for="user in us">
<mk-avatar class="avatar" :user="user"/>
<div class="body">
<div class="name">
<router-link class="name" :to="user | userPage" v-user-preview="user.id"><mk-user-name :user="user"/></router-link>
<p class="username">@{{ user | acct }}</p>
</div>
<div class="description" v-if="user.description" :title="user.description">
<mfm :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis" :should-break="false"/>
</div>
</div>
</div>
<button class="more" :class="{ fetching: fetchingMoreUsers }" v-if="cursor != null" @click="fetchMoreUsers()" :disabled="fetchingMoreUsers">
<template v-if="fetchingMoreUsers"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreUsers ? $t('@.loading') : $t('@.load-more') }}
</button>
</div>
</ui-container>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('common/views/components/user-list.vue'),
props: {
makePromise: {
required: true
},
iconOnly: {
type: Boolean,
default: false
}
},
data() {
return {
fetching: true,
fetchingMoreUsers: false,
us: [],
inited: false,
cursor: null
};
},
created() {
this.init();
},
methods: {
init() {
this.fetching = true;
this.makePromise().then(x => {
if (Array.isArray(x)) {
this.us = x;
} else {
this.us = x.users;
this.cursor = x.cursor;
}
this.inited = true;
this.fetching = false;
}, e => {
this.fetching = false;
});
},
fetchMoreUsers() {
this.fetchingMoreUsers = true;
this.makePromise(this.cursor).then(x => {
this.us = this.us.concat(x.users);
this.cursor = x.cursor;
this.fetchingMoreUsers = false;
}, e => {
this.fetchingMoreUsers = false;
});
}
}
});
</script>
<style lang="stylus" scoped>
.efvhhmdq
&.narrow
> .user > .body > .name
width 100%
> .user > .body > .description
display none
> .no-users
text-align center
color var(--text)
> .user
display flex
padding 16px
border-bottom solid 1px var(--faceDivider)
&:last-child
border-bottom none
> .avatar
display block
flex-shrink 0
margin 0 12px 0 0
width 42px
height 42px
border-radius 8px
> .body
display flex
width calc(100% - 54px)
> .name
width 45%
> .name
margin 0
font-size 16px
line-height 24px
color var(--text)
> .username
display block
margin 0
font-size 15px
line-height 16px
color var(--text)
opacity 0.7
> .description
width 55%
color var(--text)
line-height 42px
white-space nowrap
overflow hidden
text-overflow ellipsis
opacity 0.7
font-size 14px
> .more
display block
width 100%
padding 16px
color var(--text)
border-top solid var(--lineWidth) rgba(#000, 0.05)
&:hover
background rgba(#000, 0.025)
&:active
background rgba(#000, 0.05)
&.fetching
cursor wait
> [data-icon]
margin-right 4px
</style>

View File

@@ -76,6 +76,7 @@ export default Vue.extend({
if (note.replyId != null) return;
if (note.renoteId != null) return;
if (note.poll != null) return;
if (note.localOnly) return;
this.notes.unshift(note);
},

View File

@@ -0,0 +1,181 @@
<template>
<div>
<ui-container :show-header="false">
<div class="kpdsmpnk" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }">
<div>
<b>{{ $t('explore', { host: meta.name }) }}</b>
<span>{{ $t('users-info', { users: num(stats.originalUsersCount) }) }}</span>
</div>
</div>
</ui-container>
<mk-user-list v-if="tag != null" :make-promise="tagUsers" :key="`${tag}-local`">
<fa :icon="faHashtag" fixed-width/>{{ tag }}
</mk-user-list>
<mk-user-list v-if="tag != null" :make-promise="tagRemoteUsers" :key="`${tag}-remote`">
<fa :icon="faHashtag" fixed-width/>{{ tag }} ({{ $t('federated') }})
</mk-user-list>
<ui-container :body-togglable="true">
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
<div class="vxjfqztj">
<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="tag.tag" class="local">{{ tag.tag }}</router-link>
<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="tag.tag">{{ tag.tag }}</router-link>
</div>
</ui-container>
<template v-if="tag == null">
<mk-user-list :make-promise="verifiedUsers">
<fa :icon="faBookmark" fixed-width/>{{ $t('verified-users') }}
</mk-user-list>
<mk-user-list :make-promise="popularUsers">
<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
</mk-user-list>
<mk-user-list :make-promise="recentlyUpdatedUsers">
<fa :icon="faCommentAlt" fixed-width/>{{ $t('recently-updated-users') }}
</mk-user-list>
<mk-user-list :make-promise="recentlyRegisteredUsers">
<fa :icon="faPlus" fixed-width/>{{ $t('recently-registered-users') }}
</mk-user-list>
</template>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import { faChartLine, faPlus, faHashtag } from '@fortawesome/free-solid-svg-icons';
import { faBookmark, faCommentAlt } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/pages/explore.vue'),
props: {
tag: {
type: String,
required: false
}
},
data() {
return {
verifiedUsers: () => this.$root.api('users', {
state: 'verified',
origin: 'local',
sort: '+follower',
limit: 10
}),
popularUsers: () => this.$root.api('users', {
state: 'alive',
origin: 'local',
sort: '+follower',
limit: 10
}),
recentlyUpdatedUsers: () => this.$root.api('users', {
origin: 'local',
sort: '+updatedAt',
limit: 10
}),
recentlyRegisteredUsers: () => this.$root.api('users', {
origin: 'local',
state: 'alive',
sort: '+createdAt',
limit: 10
}),
tagsLocal: [],
tagsRemote: [],
stats: null,
meta: null,
num: Vue.filter('number'),
faBookmark, faChartLine, faCommentAlt, faPlus, faHashtag
};
},
computed: {
tagUsers(): () => Promise<any> {
return () => this.$root.api('hashtags/users', {
tag: this.tag,
state: 'alive',
origin: 'local',
sort: '+follower',
limit: 30
});
},
tagRemoteUsers(): () => Promise<any> {
return () => this.$root.api('hashtags/users', {
tag: this.tag,
state: 'alive',
origin: 'remote',
sort: '+follower',
limit: 30
});
},
},
created() {
this.$root.api('hashtags/list', {
sort: '+attachedLocalUsers',
attachedToLocalUserOnly: true,
limit: 30
}).then(tags => {
this.tagsLocal = tags;
});
this.$root.api('hashtags/list', {
sort: '+attachedRemoteUsers',
attachedToRemoteUserOnly: true,
limit: 30
}).then(tags => {
this.tagsRemote = tags;
});
this.$root.api('stats').then(stats => {
this.stats = stats;
});
this.$root.getMeta().then(meta => {
this.meta = meta;
});
}
});
</script>
<style lang="stylus" scoped>
.vxjfqztj
padding 16px
> *
margin-right 16px
&.local
font-weight bold
.kpdsmpnk
min-height 100px
padding 16px
background-position center
background-size cover
&:before
content ""
display block
position absolute
top 0
left 0
width 100%
height 100%
background rgba(0, 0, 0, 0.3)
> div
color #fff
text-shadow 0 0 8px #00
> b
display block
font-size 20px
font-weight bold
> span
font-size 14px
opacity 0.8
</style>

View File

@@ -12,7 +12,7 @@
</router-link>
<span class="username">@{{ user | acct }}</span>
<div class="description">
<mfm v-if="user.description" :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
</div>
</div>
</main>

View File

@@ -0,0 +1,30 @@
<template>
<div>
<mk-user-list :make-promise="makePromise">{{ $t('@.followers') }}</mk-user-list>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import parseAcct from '../../../../../misc/acct/parse';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n(''),
data() {
return {
makePromise: cursor => this.$root.api('users/followers', {
...parseAcct(this.$route.params.user),
limit: 30,
cursor: cursor ? cursor : undefined
}).then(x => {
return {
users: x.users,
cursor: x.next
};
}),
};
},
});
</script>

View File

@@ -0,0 +1,27 @@
<template>
<div>
<mk-user-list :make-promise="makePromise">{{ $t('@.following') }}</mk-user-list>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import parseAcct from '../../../../../misc/acct/parse';
export default Vue.extend({
data() {
return {
makePromise: cursor => this.$root.api('users/following', {
...parseAcct(this.$route.params.user),
limit: 30,
cursor: cursor ? cursor : undefined
}).then(x => {
return {
users: x.users,
cursor: x.next
};
}),
};
},
});
</script>

View File

@@ -1,10 +1,10 @@
<template>
<div class="mkw-analog-clock">
<mk-widget-container :naked="props.style % 2 === 0" :show-header="false">
<ui-container :naked="props.style % 2 === 0" :show-header="false">
<div class="mkw-analog-clock--body">
<mk-analog-clock :dark="$store.state.device.darkmode" :smooth="props.style < 2"/>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<div class="anltbovirfeutcigvwgmgxipejaeozxi">
<mk-widget-container :show-header="false" :naked="props.design == 1">
<ui-container :show-header="false" :naked="props.design == 1">
<div class="anltbovirfeutcigvwgmgxipejaeozxi-body"
:data-found="announcements && announcements.length != 0"
:data-melt="props.design == 1"
@@ -23,7 +23,7 @@
</p>
<a v-if="announcements.length > 1" @click="next">{{ $t('next') }} &gt;&gt;</a>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<div class="mkw-calendar" :data-special="special" :data-mobile="platform == 'mobile'">
<mk-widget-container :naked="props.design == 1" :show-header="false">
<ui-container :naked="props.design == 1" :show-header="false">
<div class="mkw-calendar--body">
<div class="calendar" :data-is-holiday="isHoliday">
<p class="month-and-year">
@@ -31,7 +31,7 @@
</div>
</div>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,12 +1,12 @@
<template>
<div class="mkw-hashtags">
<mk-widget-container :show-header="!props.compact">
<template slot="header"><fa icon="hashtag"/>{{ $t('title') }}</template>
<ui-container :show-header="!props.compact">
<template #header><fa icon="hashtag"/>{{ $t('title') }}</template>
<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
<mk-trends/>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -13,6 +13,7 @@ import wSlideshow from './slideshow.vue';
import wTips from './tips.vue';
import wNav from './nav.vue';
import wHashtags from './hashtags.vue';
import wInstance from './instance.vue';
Vue.component('mkw-analog-clock', wAnalogClock);
Vue.component('mkw-nav', wNav);
@@ -27,3 +28,4 @@ Vue.component('mkw-memo', wMemo);
Vue.component('mkw-rss', wRss);
Vue.component('mkw-version', wVersion);
Vue.component('mkw-hashtags', wHashtags);
Vue.component('mkw-instance', wInstance);

View File

@@ -0,0 +1,14 @@
<template>
<div class="mkw-instance">
<ui-container>
<mk-instance/>
</ui-container>
</div>
</template>
<script lang="ts">
import define from '../../../common/define-widget';
export default define({
name: 'instance'
});
</script>

View File

@@ -1,13 +1,13 @@
<template>
<div class="mkw-memo">
<mk-widget-container :show-header="!props.compact">
<template slot="header"><fa :icon="['far', 'sticky-note']"/>{{ $t('title') }}</template>
<ui-container :show-header="!props.compact">
<template #header><fa :icon="['far', 'sticky-note']"/>{{ $t('title') }}</template>
<div class="mkw-memo--body">
<textarea v-model="text" :placeholder="$t('placeholder')" @input="onChange"></textarea>
<button @click="saveMemo" :disabled="!changed">{{ $t('save') }}</button>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,10 +1,10 @@
<template>
<div class="mkw-nav">
<mk-widget-container>
<ui-container>
<div class="mkw-nav--body">
<mk-nav/>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div class="mkw-photo-stream" :class="$style.root" :data-melt="props.design == 2">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="camera"/>{{ $t('title') }}</template>
<ui-container :show-header="props.design == 0" :naked="props.design == 2">
<template #header><fa icon="camera"/>{{ $t('title') }}</template>
<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">
@@ -13,7 +13,7 @@
></div>
</div>
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div class="mkw-posts-monitor">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="chart-line"/>{{ $t('title') }}</template>
<button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
<ui-container :show-header="props.design == 0" :naked="props.design == 2">
<template #header><fa icon="chart-line"/>{{ $t('title') }}</template>
<template #func><button @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button></template>
<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">
@@ -64,7 +64,7 @@
<text x="1" y="5">Fedi</text>
</svg>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div class="mkw-rss">
<mk-widget-container :show-header="!props.compact">
<template slot="header"><fa icon="rss-square"/>RSS</template>
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
<ui-container :show-header="!props.compact">
<template #header><fa icon="rss-square"/>RSS</template>
<template #func><button title="設定" @click="setting"><fa icon="cog"/></button></template>
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
@@ -10,7 +10,7 @@
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
</div>
</div>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<div class="mkw-server">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="server"/>{{ $t('title') }}</template>
<button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
<ui-container :show-header="props.design == 0" :naked="props.design == 2">
<template #header><fa icon="server"/>{{ $t('title') }}</template>
<template #func><button @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button></template>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-if="!fetching">
@@ -13,7 +13,7 @@
<x-uptimes v-show="props.view == 4" :connection="connection"/>
<x-info v-show="props.view == 5" :connection="connection" :meta="meta"/>
</template>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -3,7 +3,7 @@
</template>
<script lang="ts">
import { clientVersion as version, codename } from '../../../config';
import { version, codename } from '../../../config';
import define from '../../../common/define-widget';
export default define({
name: 'version'

View File

@@ -1,7 +1,6 @@
declare const _LANGS_: string[];
declare const _COPYRIGHT_: string;
declare const _VERSION_: string;
declare const _CLIENT_VERSION_: string;
declare const _CODENAME_: string;
declare const _ENV_: string;
@@ -17,6 +16,5 @@ export const langs = _LANGS_;
export const locale = JSON.parse(localStorage.getItem('locale'));
export const copyright = _COPYRIGHT_;
export const version = _VERSION_;
export const clientVersion = _CLIENT_VERSION_;
export const codename = _CODENAME_;
export const env = _ENV_;

View File

@@ -1,20 +1,10 @@
import { apiUrl, locale } from '../../config';
import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue';
export default ($root: any) => {
const cropImage = file => new Promise((resolve, reject) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) {
$root.dialog({
title: locale['desktop']['invalid-filetype'],
text: null
});
return reject('invalid-filetype');
}
const cropImage = file => new Promise(async (resolve, reject) => {
const CropWindow = await import('../views/components/crop-window.vue').then(x => x.default);
const w = $root.new(CropWindow, {
image: file,
title: locale['desktop']['avatar-crop-title'],

View File

@@ -1,20 +1,10 @@
import { apiUrl, locale } from '../../config';
import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue';
export default ($root: any) => {
const cropImage = file => new Promise((resolve, reject) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) {
$root.dialog({
title: locale['desktop']['invalid-filetype'],
text: null
});
return reject('invalid-filetype');
}
const cropImage = file => new Promise(async (resolve, reject) => {
const CropWindow = await import('../views/components/crop-window.vue').then(x => x.default);
const w = $root.new(CropWindow, {
image: file,
title: locale['desktop']['banner-crop-title'],

View File

@@ -12,19 +12,12 @@ import init from '../init';
import fuckAdBlock from '../common/scripts/fuck-ad-block';
import composeNotification from '../common/scripts/compose-notification';
import MkIndex from './views/pages/index.vue';
import MkHome from './views/pages/home.vue';
import MkDeck from './views/pages/deck/deck.vue';
import MkUser from './views/pages/user/user.vue';
import MkHome from './views/home/home.vue';
import MkDeck from './views/deck/deck.vue';
import MkUserFollowingOrFollowers from './views/pages/user-following-or-followers.vue';
import MkFavorites from './views/pages/favorites.vue';
import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue';
import MkHomeCustomize from './views/pages/home-customize.vue';
import MkMessagingRoom from './views/pages/messaging-room.vue';
import MkNote from './views/pages/note.vue';
import MkSearch from './views/pages/search.vue';
import MkTag from './views/pages/tag.vue';
import MkReversi from './views/pages/games/reversi.vue';
import MkShare from './views/pages/share.vue';
import MkFollow from '../common/views/pages/follow.vue';
@@ -36,6 +29,7 @@ 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 MkHomeTimeline from './views/home/timeline.vue';
import Notification from './views/components/ui-notification.vue';
import { url } from '../config';
@@ -44,7 +38,7 @@ import MiOS from '../mios';
/**
* init
*/
init(async (launch) => {
init(async (launch, os) => {
Vue.mixin({
methods: {
$contextmenu(e, menu, opts?) {
@@ -130,35 +124,64 @@ init(async (launch) => {
require('./views/components');
require('./views/widgets');
os.store.commit('device/set', {
key: 'inDeckMode',
value: os.store.getters.isSignedIn && os.store.state.device.deckMode
&& (document.location.pathname === '/' || window.performance.navigation.type === 1)
});
// Init router
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', name: 'index', component: MkIndex },
{ path: '/home', name: 'home', component: MkHome },
{ path: '/deck', name: 'deck', component: MkDeck },
{ path: '/i/customize-home', component: MkHomeCustomize },
{ path: '/i/favorites', component: MkFavorites },
os.store.state.device.inDeckMode
? { path: '/', name: 'index', component: MkDeck, children: [
{ path: '/@:user', component: () => import('./views/deck/deck.user-column.vue').then(m => m.default), children: [
{ path: '', name: 'user', component: () => import('./views/deck/deck.user-column.home.vue').then(m => m.default) },
{ path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) },
{ path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) },
]},
{ path: '/notes/:note', name: 'note', component: () => import('./views/deck/deck.note-column.vue').then(m => m.default) },
{ path: '/search', component: () => import('./views/deck/deck.search-column.vue').then(m => m.default) },
{ path: '/tags/:tag', name: 'tag', component: () => import('./views/deck/deck.hashtag-column.vue').then(m => m.default) },
{ path: '/featured', name: 'featured', component: () => import('./views/deck/deck.featured-column.vue').then(m => m.default) },
{ path: '/explore', name: 'explore', component: () => import('./views/deck/deck.explore-column.vue').then(m => m.default) },
{ path: '/explore/tags/:tag', name: 'explore-tag', props: true, component: () => import('./views/deck/deck.explore-column.vue').then(m => m.default) },
{ path: '/i/favorites', component: () => import('./views/deck/deck.favorites-column.vue').then(m => m.default) }
]}
: { path: '/', component: MkHome, children: [
{ path: '', name: 'index', component: MkHomeTimeline },
{ path: '/@:user', component: () => import('./views/home/user/index.vue').then(m => m.default), children: [
{ path: '', name: 'user', component: () => import('./views/home/user/user.home.vue').then(m => m.default) },
{ path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) },
{ path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) },
]},
{ path: '/notes/:note', name: 'note', component: () => import('./views/home/note.vue').then(m => m.default) },
{ path: '/search', component: () => import('./views/home/search.vue').then(m => m.default) },
{ path: '/tags/:tag', name: 'tag', component: () => import('./views/home/tag.vue').then(m => m.default) },
{ path: '/featured', name: 'featured', component: () => import('./views/home/featured.vue').then(m => m.default) },
{ path: '/explore', name: 'explore', component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
{ path: '/explore/tags/:tag', name: 'explore-tag', props: true, component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
{ path: '/i/favorites', component: () => import('./views/home/favorites.vue').then(m => m.default) },
]},
{ path: '/i/messaging/:user', component: MkMessagingRoom },
{ path: '/i/drive', component: MkDrive },
{ path: '/i/drive/folder/:folder', component: MkDrive },
{ path: '/i/settings', component: MkSettings },
{ path: '/selectdrive', component: MkSelectDrive },
{ path: '/search', component: MkSearch },
{ path: '/tags/:tag', name: 'tag', component: MkTag },
{ path: '/share', component: MkShare },
{ path: '/games/reversi/:game?', component: MkReversi },
{ 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: '/authorize-follow', component: MkFollow },
{ path: '/deck', redirect: '/' },
{ path: '*', component: MkNotFound }
]
],
scrollBehavior(to, from, savedPosition) {
return { x: 0, y: 0 };
}
});
// Launch the app
const [app, os] = launch(router);
const [app, _] = launch(router);
if (os.store.getters.isSignedIn) {
/**

View File

@@ -1,15 +1,15 @@
<template>
<div class="mk-activity">
<mk-widget-container :show-header="design == 0" :naked="design == 2">
<template slot="header"><fa icon="chart-bar"/>{{ $t('title') }}</template>
<button slot="func" :title="$t('toggle')" @click="toggle"><fa icon="sort"/></button>
<ui-container :show-header="design == 0" :naked="design == 2">
<template #header><fa icon="chart-bar"/>{{ $t('title') }}</template>
<template #func><button :title="$t('toggle')" @click="toggle"><fa icon="sort"/></button></template>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-else>
<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
<x-chart v-show="view == 1" :data="[].concat(activity)"/>
</template>
</mk-widget-container>
</ui-container>
</div>
</template>

View File

@@ -1,9 +1,11 @@
<template>
<mk-window ref="window" is-modal width="800px" height="500px" @closed="destroyDom">
<span slot="header" class="jqiaciqv">
<span class="title">{{ $t('choose-prompt') }}</span>
<span class="count" v-if="multiple && files.length > 0">({{ $t('chosen-files', { count: files.length }) }})</span>
</span>
<template #header>
<span class="jqiaciqv">
<span class="title">{{ $t('choose-prompt') }}</span>
<span class="count" v-if="multiple && files.length > 0">({{ $t('chosen-files', { count: files.length }) }})</span>
</span>
</template>
<div class="rqsvbumu">
<x-drive

View File

@@ -1,8 +1,8 @@
<template>
<mk-window ref="window" is-modal width="800px" height="500px" @closed="destroyDom">
<span slot="header">
<template #header>
<span>{{ $t('choose-prompt') }}</span>
</span>
</template>
<div class="hllkpxxu">
<x-drive

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" is-modal width="800px" :can-close="false">
<span slot="header"><fa icon="crop"/>{{ title }}</span>
<template #header><fa icon="crop"/>{{ title }}</template>
<div class="body">
<vue-cropper ref="cropper"
:src="image.url"

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" @closed="destroyDom" width="800px" height="500px" :popout-url="popout">
<template slot="header">
<template #header>
<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> {{ $t('used') }}</p>
<span :class="$style.title"><fa icon="cloud"/>{{ $t('@.drive') }}</span>
</template>

View File

@@ -1,165 +0,0 @@
<template>
<div class="mk-friends-maker">
<p class="title">{{ $t('title') }}</p>
<div class="users" v-if="!fetching && users.length > 0">
<div class="user" v-for="user in users" :key="user.id">
<mk-avatar class="avatar" :user="user" target="_blank"/>
<div class="body">
<router-link class="name" :to="user | userPage" v-user-preview="user.id">
<mk-user-name :user="user"/>
</router-link>
<p class="username">@{{ user | acct }}</p>
</div>
</div>
</div>
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('empty') }}</p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
<a class="refresh" @click="refresh">{{ $t('refresh') }}</a>
<button class="close" @click="destroyDom()" :title="$t('title')"><fa icon="times"/></button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/friends-maker.vue'),
data() {
return {
users: [],
fetching: true,
limit: 6,
page: 0
};
},
mounted() {
this.fetch();
},
methods: {
fetch() {
this.fetching = true;
this.users = [];
this.$root.api('users/recommendation', {
limit: this.limit,
offset: this.limit * this.page
}).then(users => {
this.users = users;
this.fetching = false;
});
},
refresh() {
if (this.users.length < this.limit) {
this.page = 0;
} else {
this.page++;
}
this.fetch();
}
}
});
</script>
<style lang="stylus" scoped>
.mk-friends-maker
padding 24px
> .title
margin 0 0 12px 0
font-size 1em
font-weight bold
color #888
> .users
&:after
content ""
display block
clear both
> .user
padding 16px
width 238px
float left
&:after
content ""
display block
clear both
> .avatar
display block
float left
margin 0 12px 0 0
width 42px
height 42px
border-radius 8px
> .body
float left
width calc(100% - 54px)
> .name
margin 0
font-size 16px
line-height 24px
color #555
> .username
margin 0
font-size 15px
line-height 16px
color #ccc
> .mk-follow-button
position absolute
top 16px
right 16px
> .empty
margin 0
padding 16px
text-align center
color var(--text)
> .fetching
margin 0
padding 16px
text-align center
color var(--text)
> [data-icon]
margin-right 4px
> .refresh
display block
margin 0 8px 0 0
text-align right
font-size 0.9em
color #999
> .close
cursor pointer
display block
position absolute
top 6px
right 6px
z-index 1
margin 0
padding 0
font-size 1.2em
color #999
border none
outline none
background transparent
&:hover
color #555
&:active
color #222
> [data-icon]
padding 14px
</style>

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="gamepad"/>{{ $t('game') }}</span>
<template #header><fa icon="gamepad"/> {{ $t('game') }}</template>
<x-reversi :class="$style.content" @gamed="g => game = g"/>
</mk-window>
</template>
@@ -31,10 +31,6 @@ export default Vue.extend({
</script>
<style lang="stylus" module>
.header
> [data-icon]
margin-right 4px
.content
height 100%
overflow auto

View File

@@ -1,396 +0,0 @@
<template>
<div class="mk-home" :data-customize="customize">
<div class="customize" v-if="customize">
<router-link to="/"><fa icon="check"/>{{ $t('done') }}</router-link>
<div>
<div class="adder">
<p>{{ $t('add-widget') }}</p>
<select v-model="widgetAdderSelected">
<option value="profile">{{ $t('@.widgets.profile') }}</option>
<option value="analog-clock">{{ $t('@.widgets.analog-clock') }}</option>
<option value="calendar">{{ $t('@.widgets.calendar') }}</option>
<option value="timemachine">{{ $t('@.widgets.timemachine') }}</option>
<option value="activity">{{ $t('@.widgets.activity') }}</option>
<option value="rss">{{ $t('@.widgets.rss') }}</option>
<option value="trends">{{ $t('@.widgets.trends') }}</option>
<option value="photo-stream">{{ $t('@.widgets.photo-stream') }}</option>
<option value="slideshow">{{ $t('@.widgets.slideshow') }}</option>
<option value="version">{{ $t('@.widgets.version') }}</option>
<option value="broadcast">{{ $t('@.widgets.broadcast') }}</option>
<option value="notifications">{{ $t('@.widgets.notifications') }}</option>
<option value="users">{{ $t('@.widgets.users') }}</option>
<option value="polls">{{ $t('@.widgets.polls') }}</option>
<option value="post-form">{{ $t('@.widgets.post-form') }}</option>
<option value="messaging">{{ $t('@.widgets.messaging') }}</option>
<option value="memo">{{ $t('@.widgets.memo') }}</option>
<option value="hashtags">{{ $t('@.widgets.hashtags') }}</option>
<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
<option value="server">{{ $t('@.widgets.server') }}</option>
<option value="nav">{{ $t('@.widgets.nav') }}</option>
<option value="tips">{{ $t('@.widgets.tips') }}</option>
</select>
<button @click="addWidget">{{ $t('add') }}</button>
</div>
<div class="trash">
<x-draggable v-model="trash" :options="{ group: 'x' }" @add="onTrash"></x-draggable>
<p>{{ $t('@.trash') }}</p>
</div>
</div>
</div>
<div class="main" :class="{ side: widgets.left.length == 0 || widgets.right.length == 0 }">
<template v-if="customize">
<x-draggable v-for="place in ['left', 'right']"
:list="widgets[place]"
:class="place"
:data-place="place"
:options="{ group: 'x', animation: 150 }"
@sort="onWidgetSort"
:key="place"
>
<div v-for="widget in widgets[place]" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" platform="desktop"/>
</div>
</x-draggable>
<div class="main">
<a @click="hint">{{ $t('@.customization-tips.title') }}</a>
<div>
<mk-post-form v-if="$store.state.settings.showPostFormOnTopOfTl"/>
<mk-timeline ref="tl" @loaded="onTlLoaded"/>
</div>
</div>
</template>
<template v-else>
<div v-for="place in ['left', 'right']" :class="place">
<component v-for="widget in widgets[place]" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" @chosen="warp" platform="desktop"/>
</div>
<div class="main">
<mk-post-form class="form" v-if="$store.state.settings.showPostFormOnTopOfTl"/>
<mk-timeline class="tl" ref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
</div>
</template>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import * as XDraggable from 'vuedraggable';
import * as uuid from 'uuid';
const defaultDesktopHomeWidgets = {
left: [
'profile',
'calendar',
'activity',
'rss',
'hashtags',
'photo-stream',
'version'
],
right: [
'broadcast',
'notifications',
'users',
'polls',
'server',
'nav',
'tips'
]
};
//#region Construct home data
const _defaultDesktopHomeWidgets = [];
for (const widget of defaultDesktopHomeWidgets.left) {
_defaultDesktopHomeWidgets.push({
name: widget,
id: uuid(),
place: 'left',
data: {}
});
}
for (const widget of defaultDesktopHomeWidgets.right) {
_defaultDesktopHomeWidgets.push({
name: widget,
id: uuid(),
place: 'right',
data: {}
});
}
//#endregion
export default Vue.extend({
i18n: i18n('desktop/views/components/home.vue'),
components: {
XDraggable
},
props: {
customize: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'timeline'
}
},
data() {
return {
connection: null,
widgetAdderSelected: null,
trash: []
};
},
computed: {
home(): any[] {
return this.$store.state.settings.home || [];
},
left(): any[] {
return this.home.filter(w => w.place == 'left');
},
right(): any[] {
return this.home.filter(w => w.place == 'right');
},
widgets(): any {
return {
left: this.left,
right: this.right
};
}
},
created() {
if (this.$store.state.settings.home == null) {
this.$root.api('i/update_home', {
home: _defaultDesktopHomeWidgets
}).then(() => {
this.$store.commit('settings/setHome', _defaultDesktopHomeWidgets);
});
}
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
hint() {
this.$root.dialog({
title: this.$t('@.customization-tips.title'),
text: this.$t('@.customization-tips.paragraph')
});
},
onTlLoaded() {
this.$emit('loaded');
},
onWidgetContextmenu(widgetId) {
const w = (this.$refs[widgetId] as any)[0];
if (w.func) w.func();
},
onWidgetSort() {
this.saveHome();
},
onTrash(evt) {
this.saveHome();
},
addWidget() {
this.$store.dispatch('settings/addHomeWidget', {
name: this.widgetAdderSelected,
id: uuid(),
place: 'left',
data: {}
});
},
saveHome() {
const left = this.widgets.left;
const right = this.widgets.right;
this.$store.commit('settings/setHome', left.concat(right));
for (const w of left) w.place = 'left';
for (const w of right) w.place = 'right';
this.$root.api('i/update_home', {
home: this.home
});
},
warp(date) {
(this.$refs.tl as any).warp(date);
},
focus() {
(this.$refs.tl as any).focus();
}
}
});
</script>
<style lang="stylus" scoped>
.mk-home
display block
&[data-customize]
padding-top 48px
background-image url('/assets/desktop/grid.svg')
> .main > .main
> a
display block
margin-bottom 8px
text-align center
> div
cursor not-allowed !important
> *
pointer-events none
&:not([data-customize])
> .main > *:empty
display none
> .customize
position fixed
z-index 1000
top 0
left 0
width 100%
height 48px
color var(--text)
background var(--desktopHeaderBg)
box-shadow 0 1px 1px rgba(#000, 0.075)
> a
display block
position absolute
z-index 1001
top 0
right 0
padding 0 16px
line-height 48px
text-decoration none
color var(--primaryForeground)
background var(--primary)
transition background 0.1s ease
&:hover
background var(--primaryLighten10)
&:active
background var(--primaryDarken10)
transition background 0s ease
> [data-icon]
margin-right 8px
> div
display flex
margin 0 auto
max-width 1220px - 32px
> div
width 50%
&.adder
> p
display inline
line-height 48px
&.trash
border-left solid 1px var(--faceDivider)
> div
width 100%
height 100%
> p
position absolute
top 0
left 0
width 100%
line-height 48px
margin 0
text-align center
pointer-events none
> .main
display flex
justify-content center
margin 0 auto
max-width 1240px
> *
.customize-container
cursor move
border-radius 6px
&:hover
box-shadow 0 0 8px rgba(64, 120, 200, 0.3)
> *
pointer-events none
> .main
padding 16px
width calc(100% - 280px * 2)
order 2
> .form
margin-bottom 16px
box-shadow var(--shadow)
border-radius var(--round)
&.side
> .main
width calc(100% - 280px)
max-width 680px
> *:not(.main)
width 280px
padding 16px 0 16px 0
> *:not(:last-child)
margin-bottom 16px
> .left
padding-left 16px
order 1
> .right
padding-right 16px
order 3
&.side
@media (max-width 1000px)
> *:not(.main)
display none
> .main
width 100%
max-width 700px
margin 0 auto
&:not(.side)
@media (max-width 1200px)
> *:not(.main)
display none
> .main
width 100%
max-width 700px
margin 0 auto
</style>

View File

@@ -2,8 +2,6 @@ import Vue from 'vue';
import ui from './ui.vue';
import uiNotification from './ui-notification.vue';
import home from './home.vue';
import timeline from './timeline.vue';
import notes from './notes.vue';
import subNoteContent from './sub-note-content.vue';
import window from './window.vue';
@@ -17,15 +15,12 @@ import notePreview from './note-preview.vue';
import noteDetail from './note-detail.vue';
import calendar from './calendar.vue';
import activity from './activity.vue';
import friendsMaker from './friends-maker.vue';
import userCard from './user-card.vue';
import userListTimeline from './user-list-timeline.vue';
import widgetContainer from './widget-container.vue';
import uiContainer from './ui-container.vue';
Vue.component('mk-ui', ui);
Vue.component('mk-ui-notification', uiNotification);
Vue.component('mk-home', home);
Vue.component('mk-timeline', timeline);
Vue.component('mk-notes', notes);
Vue.component('mk-sub-note-content', subNoteContent);
Vue.component('mk-window', window);
@@ -39,7 +34,6 @@ Vue.component('mk-note-preview', notePreview);
Vue.component('mk-note-detail', noteDetail);
Vue.component('mk-calendar', calendar);
Vue.component('mk-activity', activity);
Vue.component('mk-friends-maker', friendsMaker);
Vue.component('mk-user-card', userCard);
Vue.component('mk-user-list-timeline', userListTimeline);
Vue.component('mk-widget-container', widgetContainer);
Vue.component('ui-container', uiContainer);

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="comments"/>{{ $t('title') }} <mk-user-name :user="user"/></span>
<template #header><fa icon="comments"/> {{ $t('title') }} <mk-user-name :user="user"/></template>
<x-messaging-room :user="user" :class="$style.content"/>
</mk-window>
</template>
@@ -26,10 +26,6 @@ export default Vue.extend({
</script>
<style lang="stylus" module>
.header
> [data-icon]
margin-right 4px
.content
height 100%
overflow auto

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="500px" height="560px" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="comments"/>{{ $t('title') }}</span>
<template #header :class="$style.header"><fa icon="comments"/>{{ $t('title') }}</template>
<x-messaging :class="$style.content" @navigate="navigate"/>
</mk-window>
</template>

View File

@@ -1,10 +1,12 @@
<template>
<div class="mk-notes">
<slot name="header"></slot>
<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
<slot name="empty" v-if="notes.length == 0 && !fetching && inited"></slot>
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
<mk-error v-if="!fetching && !inited" @retry="init()"/>
<div class="placeholder" v-if="fetching">
<template v-for="i in 10">
@@ -23,8 +25,8 @@
</template>
</component>
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<footer v-if="cursor != null">
<button @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
@@ -43,24 +45,25 @@ const displayLimit = 30;
export default Vue.extend({
i18n: i18n(),
components: {
XNote
},
props: {
more: {
type: Function,
required: false
makePromise: {
required: true
}
},
data() {
return {
requestInitPromise: null as () => Promise<any[]>,
notes: [],
queue: [],
fetching: true,
moreFetching: false
moreFetching: false,
inited: false,
cursor: null
};
},
@@ -76,6 +79,10 @@ export default Vue.extend({
}
},
created() {
this.init();
},
mounted() {
window.addEventListener('scroll', this.onScroll, { passive: true });
},
@@ -97,27 +104,41 @@ export default Vue.extend({
Vue.set((this as any).notes, i, note);
},
init(promiseGenerator: () => Promise<any[]>) {
this.requestInitPromise = promiseGenerator;
this.resolveInitPromise();
},
resolveInitPromise() {
reload() {
this.queue = [];
this.notes = [];
this.init();
},
init() {
this.fetching = true;
const promise = this.requestInitPromise();
promise.then(notes => {
this.notes = notes;
this.requestInitPromise = null;
this.makePromise().then(x => {
if (Array.isArray(x)) {
this.notes = x;
} else {
this.notes = x.notes;
this.cursor = x.cursor;
}
this.inited = true;
this.fetching = false;
this.$emit('inited');
}, e => {
this.fetching = false;
});
},
more() {
if (this.cursor == null || this.moreFetching) return;
this.moreFetching = true;
this.makePromise(this.cursor).then(x => {
this.notes = this.notes.concat(x.notes);
this.cursor = x.cursor;
this.moreFetching = false;
}, e => {
this.moreFetching = false;
});
},
prepend(note, silent = false) {
// 弾く
if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
@@ -151,10 +172,6 @@ export default Vue.extend({
this.notes.push(note);
},
tail() {
return this.notes[this.notes.length - 1];
},
releaseQueue() {
for (const n of this.queue) {
this.prepend(n, true);
@@ -162,15 +179,6 @@ export default Vue.extend({
this.queue = [];
},
async loadMore() {
if (this.more == null) return;
if (this.moreFetching) return;
this.moreFetching = true;
await this.more();
this.moreFetching = false;
},
onScroll() {
if (this.isScrollTop()) {
this.releaseQueue();
@@ -178,7 +186,7 @@ export default Vue.extend({
if (this.$store.state.settings.fetchOnScroll !== false) {
const current = window.scrollY + window.innerHeight;
if (current > document.body.offsetHeight - 8) this.loadMore();
if (current > document.body.offsetHeight - 8) this.more();
}
}
}
@@ -187,6 +195,11 @@ export default Vue.extend({
<style lang="stylus" scoped>
.mk-notes
background var(--face)
box-shadow var(--shadow)
border-radius var(--round)
overflow hidden
.transition
.mk-notes-enter
.mk-notes-leave-to

View File

@@ -1,12 +1,14 @@
<template>
<mk-window class="mk-post-form-window" ref="window" is-modal @closed="onWindowClosed" :animation="animation">
<span slot="header" class="mk-post-form-window--header">
<span class="icon" v-if="geo"><fa icon="map-marker-alt"/></span>
<span v-if="!reply">{{ $t('note') }}</span>
<span v-if="reply">{{ $t('reply') }}</span>
<span class="count" v-if="files.length != 0">{{ this.$t('attaches').replace('{}', files.length) }}</span>
<span class="count" v-if="uploadings.length != 0">{{ this.$t('uploading-media').replace('{}', uploadings.length) }}<mk-ellipsis/></span>
</span>
<template #header>
<span class="mk-post-form-window--header">
<span class="icon" v-if="geo"><fa icon="map-marker-alt"/></span>
<span v-if="!reply">{{ $t('note') }}</span>
<span v-if="reply">{{ $t('reply') }}</span>
<span class="count" v-if="files.length != 0">{{ this.$t('attaches').replace('{}', files.length) }}</span>
<span class="count" v-if="uploadings.length != 0">{{ this.$t('uploading-media').replace('{}', uploadings.length) }}<mk-ellipsis/></span>
</span>
</template>
<div class="mk-post-form-window--body">
<mk-note-preview v-if="reply" class="notePreview" :note="reply"/>

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" :is-modal="false" :can-close="false" width="500px" @closed="destroyDom">
<span slot="header">{{ title }}<mk-ellipsis/></span>
<template #header>{{ title }}<mk-ellipsis/></template>
<div :class="$style.body">
<p :class="$style.init" v-if="isNaN(value)">{{ $t('waiting') }}<mk-ellipsis/></p>
<p :class="$style.percentage" v-if="!isNaN(value)">{{ Math.floor((value / max) * 100) }}</p>

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" is-modal width="450px" height="500px" @closed="destroyDom">
<span slot="header"><fa :icon="['far', 'envelope']"/> {{ $t('title') }}</span>
<template #header><fa :icon="['far', 'envelope']"/> {{ $t('title') }}</template>
<div class="slpqaxdoxhvglersgjukmvizkqbmbokc">
<div v-for="req in requests">

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" is-modal @closed="onWindowClosed" :animation="animation">
<span slot="header" :class="$style.header"><fa icon="retweet"/>{{ $t('title') }}</span>
<template #header :class="$style.header"><fa icon="retweet"/>{{ $t('title') }}</template>
<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled" v-hotkey.global="keymap"/>
</mk-window>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" is-modal width="700px" height="550px" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="cog"/>{{ $t('settings') }}</span>
<template #header :class="$style.header"><fa icon="cog"/>{{ $t('settings') }}</template>
<x-settings :initial-page="initialPage" @done="close"/>
</mk-window>
</template>

View File

@@ -22,20 +22,17 @@
<x-theme class="theme" v-show="page == 'theme'"/>
<ui-card class="web" v-show="page == 'web'">
<div slot="title"><fa icon="sliders-h"/> {{ $t('behaviour') }}</div>
<template #title><fa icon="sliders-h"/> {{ $t('behaviour') }}</template>
<section>
<ui-switch v-model="fetchOnScroll">{{ $t('fetch-on-scroll') }}
<span slot="desc">{{ $t('fetch-on-scroll-desc') }}</span>
<template #desc>{{ $t('fetch-on-scroll-desc') }}</template>
</ui-switch>
<ui-switch v-model="autoPopout">{{ $t('auto-popout') }}
<span slot="desc">{{ $t('auto-popout-desc') }}</span>
</ui-switch>
<ui-switch v-model="deckNav">{{ $t('deck-nav') }}
<span slot="desc">{{ $t('deck-nav-desc') }}</span>
<template #desc>{{ $t('auto-popout-desc') }}</template>
</ui-switch>
<ui-switch v-model="keepCw">{{ $t('keep-cw') }}
<span slot="desc">{{ $t('keep-cw-desc') }}</span>
<template #desc>{{ $t('keep-cw-desc') }}</template>
</ui-switch>
</section>
@@ -65,12 +62,12 @@
<section>
<header>{{ $t('web-search-engine') }}</header>
<ui-input v-model="webSearchEngine">{{ $t('web-search-engine') }}<span slot="desc">{{ $t('web-search-engine-desc') }}</span></ui-input>
<ui-input v-model="webSearchEngine">{{ $t('web-search-engine') }}<template #desc>{{ $t('web-search-engine-desc') }}</template></ui-input>
</section>
</ui-card>
<ui-card class="web" v-show="page == 'web'">
<div slot="title"><fa icon="desktop"/> {{ $t('display') }}</div>
<template #title><fa icon="desktop"/> {{ $t('display') }}</template>
<section>
<ui-switch v-model="showPostFormOnTopOfTl">{{ $t('post-form-on-timeline') }}</ui-switch>
@@ -89,9 +86,6 @@
<ui-radio v-model="navbar" value="left">{{ $t('navbar-position-left') }}</ui-radio>
<ui-radio v-model="navbar" value="right">{{ $t('navbar-position-right') }}</ui-radio>
</section>
<section>
<ui-switch v-model="deckDefault">{{ $t('deck-default') }}</ui-switch>
</section>
<section>
<ui-switch v-model="darkmode">{{ $t('dark-mode') }}</ui-switch>
<ui-switch v-model="useShadow">{{ $t('use-shadow') }}</ui-switch>
@@ -141,11 +135,11 @@
</ui-card>
<ui-card class="web" v-show="page == 'web'">
<div slot="title"><fa icon="volume-up"/> {{ $t('sound') }}</div>
<template #title><fa icon="volume-up"/> {{ $t('sound') }}</template>
<section>
<ui-switch v-model="enableSounds">{{ $t('enable-sounds') }}
<span slot="desc">{{ $t('enable-sounds-desc') }}</span>
<template #desc>{{ $t('enable-sounds-desc') }}</template>
</ui-switch>
<label>{{ $t('volume') }}</label>
<input type="range"
@@ -161,7 +155,7 @@
<x-language-settings v-show="page == 'web'"/>
<ui-card class="web" v-show="page == 'web'">
<div slot="title"><fa :icon="['far', 'trash-alt']"/> {{ $t('cache') }}</div>
<template #title><fa :icon="['far', 'trash-alt']"/> {{ $t('cache') }}</template>
<section>
<ui-button @click="clean">{{ $t('clean-cache') }}</ui-button>
<div class="none ui info warn">
@@ -177,7 +171,7 @@
</div>
<ui-card class="hashtags" v-show="page == 'hashtags'">
<div slot="title"><fa icon="hashtag"/> {{ $t('tags') }}</div>
<template #title><fa icon="hashtag"/> {{ $t('tags') }}</template>
<section>
<x-tags/>
</section>
@@ -188,28 +182,28 @@
</div>
<ui-card class="apps" v-show="page == 'apps'">
<div slot="title"><fa icon="puzzle-piece"/> {{ $t('apps') }}</div>
<template #title><fa icon="puzzle-piece"/> {{ $t('apps') }}</template>
<section>
<x-apps/>
</section>
</ui-card>
<ui-card class="password" v-show="page == 'security'">
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
<template #title><fa icon="unlock-alt"/> {{ $t('password') }}</template>
<section>
<x-password-settings/>
</section>
</ui-card>
<ui-card class="2fa" v-show="page == 'security'">
<div slot="title"><fa icon="mobile-alt"/> {{ $t('@.2fa') }}</div>
<template #title><fa icon="mobile-alt"/> {{ $t('@.2fa') }}</template>
<section>
<x-2fa/>
</section>
</ui-card>
<ui-card class="signin" v-show="page == 'security'">
<div slot="title"><fa icon="sign-in-alt"/> {{ $t('signin') }}</div>
<template #title><fa icon="sign-in-alt"/> {{ $t('signin') }}</template>
<section>
<x-signins/>
</section>
@@ -220,14 +214,14 @@
</div>
<ui-card class="other" v-show="page == 'other'">
<div slot="title"><fa icon="info-circle"/> {{ $t('about') }}</div>
<template #title><fa icon="info-circle"/> {{ $t('about') }}</template>
<section>
<p v-if="meta">{{ $t('operator') }}: <i><a :href="'mailto:' + meta.maintainer.email" target="_blank">{{ meta.maintainer.name }}</a></i></p>
</section>
</ui-card>
<ui-card class="other" v-show="page == 'other'">
<div slot="title"><fa icon="sync-alt"/> {{ $t('update') }}</div>
<template #title><fa icon="sync-alt"/> {{ $t('update') }}</template>
<section>
<p>
<span>{{ $t('version') }} <i>{{ version }}</i></span>
@@ -243,20 +237,20 @@
<details>
<summary>{{ $t('update-settings') }}</summary>
<ui-switch v-model="preventUpdate">
{{ $t('prevent-update') }}<span slot="desc">{{ $t('prevent-update-desc') }}</span>
{{ $t('prevent-update') }}<template #desc>{{ $t('prevent-update-desc') }}</template>
</ui-switch>
</details>
</section>
</ui-card>
<ui-card class="other" v-show="page == 'other'">
<div slot="title"><fa icon="cogs"/> {{ $t('advanced-settings') }}</div>
<template #title><fa icon="cogs"/> {{ $t('advanced-settings') }}</template>
<section>
<ui-switch v-model="debug">
{{ $t('debug-mode') }}<span slot="desc">{{ $t('debug-mode-desc') }}</span>
{{ $t('debug-mode') }}<template #desc>{{ $t('debug-mode-desc') }}</template>
</ui-switch>
<ui-switch v-model="enableExperimentalFeatures">
{{ $t('experimental') }}<span slot="desc">{{ $t('experimental-desc') }}</span>
{{ $t('experimental') }}<template #desc>{{ $t('experimental-desc') }}</template>
</ui-switch>
</section>
</ui-card>
@@ -281,7 +275,7 @@ import XApiSettings from '../../../common/views/components/api-settings.vue';
import XLanguageSettings from '../../../common/views/components/language-settings.vue';
import XNotificationSettings from '../../../common/views/components/notification-settings.vue';
import { url, clientVersion as version } from '../../../config';
import { url, version } from '../../../config';
import checkForUpdate from '../../../common/scripts/check-for-update';
export default Vue.extend({
@@ -337,11 +331,6 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'autoPopout', value }); }
},
deckNav: {
get() { return this.$store.state.settings.deckNav; },
set(value) { this.$store.commit('settings/set', { key: 'deckNav', value }); }
},
keepCw: {
get() { return this.$store.state.settings.keepCw; },
set(value) { this.$store.commit('settings/set', { key: 'keepCw', value }); }
@@ -367,11 +356,6 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'deckColumnWidth', value }); }
},
deckDefault: {
get() { return this.$store.state.device.deckDefault; },
set(value) { this.$store.commit('device/set', { key: 'deckDefault', value }); }
},
enableSounds: {
get() { return this.$store.state.device.enableSounds; },
set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); }
@@ -534,8 +518,7 @@ export default Vue.extend({
},
methods: {
customizeHome() {
this.$router.push('/i/customize-home');
this.$emit('done');
location.href = '/?customize';
},
updateWallpaper() {
this.$chooseDriveFile({

View File

@@ -0,0 +1,117 @@
<template>
<div class="kedshtep" :class="{ naked, inDeck }">
<header v-if="showHeader">
<div class="title"><slot name="header"></slot></div>
<slot name="func"></slot>
<button v-if="bodyTogglable" @click="() => showBody = !showBody">
<template v-if="showBody"><fa icon="angle-up"/></template>
<template v-else><fa icon="angle-down"/></template>
</button>
</header>
<div v-show="showBody">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
showHeader: {
type: Boolean,
default: true
},
naked: {
type: Boolean,
default: false
},
bodyTogglable: {
type: Boolean,
default: false
},
},
inject: {
inDeck: {
default: false
}
},
data() {
return {
showBody: true
};
}
});
</script>
<style lang="stylus" scoped>
.kedshtep
overflow hidden
&:not(.inDeck)
background var(--face)
box-shadow var(--shadow)
border-radius var(--round)
& + .kedshtep
margin-top 16px
&.naked
background transparent !important
box-shadow none !important
> header
background var(--faceHeader)
> .title
z-index 1
margin 0
padding 0 16px
line-height 42px
font-size 0.9em
font-weight bold
color var(--faceHeaderText)
box-shadow 0 var(--lineWidth) rgba(#000, 0.07)
> [data-icon]
margin-right 6px
&:empty
display none
> button
position absolute
z-index 2
top 0
right 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color var(--faceTextButton)
&:hover
color var(--faceTextButtonHover)
&:active
color var(--faceTextButtonActive)
&.inDeck
background var(--face)
> header
margin 0
padding 8px 16px
font-size 12px
color var(--text)
background var(--deckColumnBg)
> button
position absolute
top 0
right 8px
padding 8px 6px
font-size 14px
color var(--text)
</style>

View File

@@ -44,13 +44,6 @@
</li>
</ul>
<ul>
<li>
<router-link to="/i/customize-home">
<i><fa icon="wrench"/></i>
<span>{{ $t('customize') }}</span>
<i><fa icon="angle-right"/></i>
</router-link>
</li>
<li>
<router-link to="/i/settings">
<i><fa icon="cog"/></i>
@@ -67,6 +60,13 @@
</li>
</ul>
<ul>
<li @click="toggleDeckMode">
<p>
<span>{{ $t('@.deck') }}</span>
<template v-if="$store.state.device.inDeckMode"><i><fa :icon="faHome"/></i></template>
<template v-else><i><fa :icon="faColumns"/></i></template>
</p>
</li>
<li @click="dark">
<p>
<span>{{ $t('dark') }}</span>
@@ -97,12 +97,14 @@ import MkFollowRequestsWindow from './received-follow-requests-window.vue';
import MkSettingsWindow from './settings-window.vue';
import MkDriveWindow from './drive-window.vue';
import contains from '../../../common/scripts/contains';
import { faHome, faColumns } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('desktop/views/components/ui.header.account.vue'),
data() {
return {
isOpen: false
isOpen: false,
faHome, faColumns
};
},
computed: {
@@ -161,7 +163,11 @@ export default Vue.extend({
key: 'darkmode',
value: !this.$store.state.device.darkmode
});
}
},
toggleDeckMode() {
this.$store.commit('device/set', { key: 'deckMode', value: !this.$store.state.device.inDeckMode });
location.replace('/');
},
}
});
</script>

View File

@@ -0,0 +1,68 @@
<template>
<div class="toltmoik">
<button @click="open()" :title="$t('@.messaging')">
<i class="bell"><fa :icon="faComments"/></i>
<i class="circle" v-if="hasUnreadMessagingMessage"><fa icon="circle"/></i>
</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import MkMessagingWindow from './messaging-window.vue';
import { faComments } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n(),
data() {
return {
faComments
};
},
computed: {
hasUnreadMessagingMessage(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadMessagingMessage;
}
},
methods: {
open() {
this.$root.new(MkMessagingWindow);
},
}
});
</script>
<style lang="stylus" scoped>
.toltmoik
> button
display block
margin 0
padding 0
width 32px
color var(--desktopHeaderFg)
border none
background transparent
cursor pointer
*
pointer-events none
&:hover
&[data-active='true']
color var(--desktopHeaderHoverFg)
> i.bell
font-size 1.2em
line-height 48px
> i.circle
margin-left -5px
vertical-align super
font-size 10px
color var(--notificationIndicator)
</style>

View File

@@ -1,38 +1,22 @@
<template>
<div class="nav">
<ul>
<template v-if="$store.getters.isSignedIn">
<template v-if="$store.state.device.deckDefault">
<li class="deck" :class="{ active: $route.name == 'deck' || $route.name == 'index' }" @click="goToTop">
<router-link to="/"><fa icon="columns"/><p>{{ $t('deck') }}</p></router-link>
</li>
<li class="home" :class="{ active: $route.name == 'home' }" @click="goToTop">
<router-link to="/home"><fa icon="home"/><p>{{ $t('home') }}</p></router-link>
</li>
</template>
<template v-else>
<li class="home" :class="{ active: $route.name == 'home' || $route.name == 'index' }" @click="goToTop">
<router-link to="/"><fa icon="home"/><p>{{ $t('home') }}</p></router-link>
</li>
<li class="deck" :class="{ active: $route.name == 'deck' }" @click="goToTop">
<router-link to="/deck"><fa icon="columns"/><p>{{ $t('deck') }}</p></router-link>
</li>
</template>
<li class="messaging">
<a @click="messaging">
<fa icon="comments"/>
<p>{{ $t('@.messaging') }}</p>
<template v-if="hasUnreadMessagingMessage"><fa icon="circle"/></template>
</a>
</li>
<li class="game">
<a @click="game">
<fa icon="gamepad"/>
<p>{{ $t('game') }}</p>
<template v-if="hasGameInvitations"><fa icon="circle"/></template>
</a>
</li>
</template>
<li class="timeline" :class="{ active: $route.name == 'index' }" @click="goToTop">
<router-link to="/"><fa icon="home"/><p>{{ $t('@.timeline') }}</p></router-link>
</li>
<li class="featured" :class="{ active: $route.name == 'featured' }">
<router-link to="/featured"><fa :icon="faNewspaper"/><p>{{ $t('@.featured-notes') }}</p></router-link>
</li>
<li class="explore" :class="{ active: $route.name == 'explore' || $route.name == 'explore-tag' }">
<router-link to="/explore"><fa :icon="faHashtag"/><p>{{ $t('@.explore') }}</p></router-link>
</li>
<li class="game">
<a @click="game">
<fa icon="gamepad"/>
<p>{{ $t('game') }}</p>
<template v-if="hasGameInvitations"><fa icon="circle"/></template>
</a>
</li>
</ul>
</div>
</template>
@@ -40,22 +24,18 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import MkMessagingWindow from './messaging-window.vue';
import MkGameWindow from './game-window.vue';
import { faNewspaper, faHashtag } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('desktop/views/components/ui.header.nav.vue'),
data() {
return {
hasGameInvitations: false,
connection: null
connection: null,
faNewspaper, faHashtag
};
},
computed: {
hasUnreadMessagingMessage(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.hasUnreadMessagingMessage;
}
},
mounted() {
if (this.$store.getters.isSignedIn) {
this.connection = this.$root.stream.useSharedConnection('main');
@@ -78,10 +58,6 @@ export default Vue.extend({
this.hasGameInvitations = false;
},
messaging() {
this.$root.new(MkMessagingWindow);
},
game() {
this.$root.new(MkGameWindow);
},
@@ -126,7 +102,7 @@ export default Vue.extend({
display inline-block
z-index 1
height 100%
padding 0 24px
padding 0 20px
font-size 13px
font-variant small-caps
color var(--desktopHeaderFg)

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