Compare commits

...

60 Commits

Author SHA1 Message Date
cb0673b1ec 10.51.0 2018-11-15 06:26:15 +09:00
cd018db945 Update src/client/app/admin/views/index.vue 2018-11-15 06:23:40 +09:00
50fe67b99b [Client] Improve admin panel 2018-11-15 06:21:31 +09:00
1dba82aae5 [API] Add /instances 2018-11-15 06:21:13 +09:00
17c6d64750 [Client] Prevent cache locale file 2018-11-15 05:20:25 +09:00
b4c04efa23 Improve usability 2018-11-15 05:00:30 +09:00
152dd74abf 10.50.0 2018-11-15 04:26:33 +09:00
0985f7f609 [Client] Fix bugs 2018-11-15 04:24:40 +09:00
56d571c0f0 Moderator system
Closes #2357
2018-11-15 04:15:42 +09:00
dc9a19b9c7 [Client] Add missing icon 2018-11-15 03:17:48 +09:00
88a2c7715a [Client] Add missing icon 2018-11-15 03:14:52 +09:00
2fa8cb1b73 10.49.7 2018-11-15 01:46:01 +09:00
5f8a66fdb9 🎨 2018-11-15 01:45:13 +09:00
57320a94f9 [Client] Add missing icon 2018-11-15 01:43:26 +09:00
89f045d624 🎨 2018-11-15 01:43:06 +09:00
1a77dea7ed [Client] Fix icon 2018-11-15 01:09:50 +09:00
d063d59a91 [Client] Improve UI 2018-11-15 00:01:49 +09:00
90429b787c 10.49.6 2018-11-14 20:40:21 +09:00
7a2ef04ec3 [Client] Improve UI 2018-11-14 20:36:15 +09:00
76a9ea8d3d 10.49.5 2018-11-14 20:27:12 +09:00
0a05a2d060 🎨 2018-11-14 20:23:51 +09:00
a7e2ee3b0c Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-14 20:21:45 +09:00
40efa90dd5 🎨 2018-11-14 20:21:35 +09:00
4ca0a22bfc Fix: default order of users/notes (#3234) 2018-11-14 20:18:47 +09:00
20a943b193 🎨 2018-11-14 20:17:12 +09:00
552df8737d [Client] Add missing icon 2018-11-14 18:07:38 +09:00
860f622d79 10.49.4 2018-11-14 18:04:30 +09:00
e76bf5707a [Client] Fix icons 2018-11-14 18:03:38 +09:00
bf37a72f59 🎨 2018-11-14 18:00:23 +09:00
840ad75830 [Client] Better thumbnail generation 2018-11-14 18:00:15 +09:00
4c7dd7228f 10.49.3 2018-11-14 16:39:27 +09:00
46a51addad ServiceWorkerはメンテナンスされていないのでとりあえず無効化 2018-11-14 16:39:04 +09:00
0a5fe37025 New Crowdin translations (#3205)
* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)
2018-11-14 16:35:55 +09:00
00bb403497 Fix #3228 (#3230)
* Update index.ts

* fix sw.js path

* Revert "Update index.ts"

This reverts commit e0b1d4239f16b65512f5dbdd93c8765f708b5282.
2018-11-14 16:35:00 +09:00
11afa8140c [Client] Improve alert component 2018-11-14 16:30:58 +09:00
850396e9da [Client] Add missing icons 2018-11-14 14:57:59 +09:00
5ee75be49e 🎨 2018-11-14 14:47:18 +09:00
879116a20c Update vue-sweetalert2 requirement from 1.5.7 to 1.5.9 (#3229)
Updates the requirements on [vue-sweetalert2](https://github.com/avil13/vue-sweetalert2) to permit the latest version.
- [Release notes](https://github.com/avil13/vue-sweetalert2/releases)
- [Commits](https://github.com/avil13/vue-sweetalert2/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-14 13:27:37 +09:00
e509b1f488 Update github-bot.ts 2018-11-14 05:33:48 +09:00
468ff7037f 10.49.2 2018-11-14 03:07:55 +09:00
df23504ccf [Client] Fix #3227 2018-11-14 03:05:13 +09:00
66e3cb8eda Update src/client/app/init.ts 2018-11-14 02:04:24 +09:00
6ddd2389dc [Client] Add missing icons 2018-11-14 01:45:15 +09:00
402efb8c50 [Client] Fix imports 2018-11-14 01:40:29 +09:00
7b6eae0ce4 Fix error 2018-11-14 01:32:38 +09:00
26ce9725ce [Client] Fix #3009 2018-11-14 01:31:36 +09:00
ebfaa18f12 Create setup.fr.md (#3225) 2018-11-14 00:15:33 +09:00
cc81d41a05 [Client] Some optimizations 2018-11-13 23:33:30 +09:00
212176ee5c Use terser instead of uglifyjs 2018-11-13 23:10:51 +09:00
a63ec05e41 [Client] Some optimizations 2018-11-13 23:06:31 +09:00
0dcb527bf3 [Client] Fix bug 2018-11-13 22:45:28 +09:00
54710f17fc [Client] Some performance optimizations 2018-11-13 22:43:09 +09:00
e58a6593c0 [Client] Fix bug 2018-11-13 22:42:31 +09:00
62132570e1 [Client] Split components to reduce bundle size 2018-11-13 20:21:52 +09:00
9f0b8ba2f8 Fix: notes/create hangs when rejected (#3221) 2018-11-13 19:34:09 +09:00
adbe0fbcd1 10.49.1 2018-11-13 16:33:21 +09:00
7896242f57 [Client] Fix #3213 2018-11-13 16:19:46 +09:00
4a6722b9e9 Merge branch 'master' into develop 2018-11-13 15:01:52 +09:00
7c9fb5228b Improve usability 2018-11-13 15:01:05 +09:00
81805b01cc sort user timeline by date (#3210) 2018-11-13 01:17:59 +09:00
143 changed files with 1690 additions and 1467 deletions

126
docs/setup.fr.md Normal file
View File

@ -0,0 +1,126 @@
Guide d'installation et de configuration de Misskey
================================================================
Nous vous remerçions de l'intrêt que vous manifestez pour l'installation de votre propre instance Misskey !
Ce guide décrit les étapes à suivre afin d'installer et de configurer une instance Misskey.
[La version en japonnais est également disponible sur - 日本語版もあります](./setup.ja.md)
----------------------------------------------------------------
*1.* Création de l'utilisateur Misskey
----------------------------------------------------------------
Lancer misskey en tant qu'utilisateur est une mauvaise idée, nous avons besoin de créer un utilisateur dédié.
Sur Debian, à titre d'exemple :
```
adduser --disabled-password --disabled-login misskey
```
*2.* Installation des dépendances
----------------------------------------------------------------
Installez les paquets suivants :
#### Dépendences :package:
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
##### Optionnels
* [Redis](https://redis.io/)
* Redis est optionnel mais nous vous recommandons vivement de l'installer
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche
*3.* Paramètrage de MongoDB
----------------------------------------------------------------
En mode root :
1. `mongo` Accédez au shell de mango
2. `use misskey` Utilisez la base de données misskey
3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db.
4. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` Créez l'utilisateur misskey.
5. `exit` Vous avez terminé !
*4.* Installation de Misskey
----------------------------------------------------------------
1. `su - misskey` Basculez vers l'utilisateur misskey.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey.
3. `cd misskey` Accédez au dossier misskey.
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Télécharge la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Installez les dépendances de misskey.
*(optionnel)* Génération des clés VAPID
----------------------------------------------------------------
Si vous désirez activer ServiceWorker, vous devez générer les clés VAPID :
Unless you have set your global node_modules location elsewhere, vous devez lancer ceci en mode root.
``` shell
npm install web-push -g
web-push generate-vapid-keys
```
*5.* Création du fichier de configuration
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le `default.yml`.
2. Editez le fichier `default.yml`
*6.* Construction de Misskey
----------------------------------------------------------------
Construisez Misskey comme ceci :
`npm run build`
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential`, `python`.
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
1. `npm install -g node-gyp`
2. `node-gyp configure`
3. `node-gyp build`
4. `npm run build`
*7.* C'est tout.
----------------------------------------------------------------
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
### Lancement conventionnel
Lancez tout simplement `npm start`. Bonne chance et amusez-vous bien !
### Démarrage avec systemd
1. Créez une service systemd sur : `/etc/systemd/system/misskey.service`
2. Editez-le puis copiez et coller ceci dans le fichier :
```
[Unit]
Description=Misskey daemon
[Service]
Type=simple
User=misskey
ExecStart=/usr/bin/npm start
WorkingDirectory=/home/misskey/misskey
TimeoutSec=60
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=misskey
Restart=always
[Install]
WantedBy=multi-user.target
```
3. `systemctl daemon-reload ; systemctl enable misskey` Redémarre systemd et active le service misskey.
4. `systemctl start misskey` Démarre le service misskey.
Vous pouvez vérifier si le service a démarré en utilisant la commande `systemctl status misskey`.
### Méthode de mise à jour vers la plus récente version de Misskey
1. `git fetch`
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
3. `npm install`
4. `npm run build`
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.
----------------------------------------------------------------
Si vous rencontrez des difficultés ou avez d'autres questions, n'hésitez pas à nous contacter !

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Diese Abstimmung löschen"
common/views/components/reaction-picker.vue:
choose-reaction: "Wähle eine Reaktion aus"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Benutzername"
password: "Passwort"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "Folge ich"
follow: "Folgen"
request-pending: "Ausstehend"
follow-processing: "フォロー処理中"
follow-request: "Follower-Anfragen"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Discard the poll"
common/views/components/reaction-picker.vue:
choose-reaction: "Send a reaction"
common/views/components/emoji-picker.vue:
custom-emoji: "Custom Emoji"
people: "People"
animals-and-nature: "Animals & Nature"
food-and-drink: "Food & drink"
activity: "Activity"
travel-and-places: "Travel & Places"
objects: "Objects"
symbols: "Symbols"
flags: "Flags"
common/views/components/signin.vue:
username: "Username"
password: "Password"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "The content is NSFW"
click-to-show: "Click to show"
desktop/views/components/follow-button.vue:
following: "Following"
follow: "Follow"
request-pending: "Pending follow request"
follow-processing: "Processing follow"
follow-request: "Follow request"
desktop/views/components/followers-window.vue:
followers: "{}'s followers"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "Blocking"
no-muted-users: "No muted users"
no-blocked-users: "No blocked users"
word-mute: "Word mute"
muted-words: "Muted keywords"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "Save"
common/views/components/password-settings.vue:
reset: "Change password"
enter-current-password: "Enter the current password"
@ -1161,11 +1169,11 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "The content is NSFW"
click-to-show: "Click to show"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "Following"
follow: "Follow"
request-pending: "Pending follow request"
follow-processing: "Processing follow"
request-pending: "Pending"
follow-processing: "Processing"
follow-request: "Follow request"
mobile/views/components/friends-maker.vue:
title: "Let's follow them"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Cancelar la encuesta"
common/views/components/reaction-picker.vue:
choose-reaction: "Escoge una reacción"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Usuario"
password: "Contraseña"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "Este contenido no es apropiado para ver en el trabajo"
click-to-show: "Click para mostrar"
desktop/views/components/follow-button.vue:
following: "Siguiendo"
follow: "Sigue"
request-pending: "Pendiente de aprobación"
follow-processing: "フォロー処理中"
follow-request: "Solicitud de seguir"
desktop/views/components/followers-window.vue:
followers: "{} seguidores"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -71,7 +71,7 @@ common:
friday: "Vendredi"
saturday: "Samedi"
reactions:
like: "J'aime"
like: "Bien"
love: "Adore"
laugh: "Rire"
hmm: "Hmm … ?"
@ -85,8 +85,8 @@ common:
public: "Public"
home: "Principal"
home-desc: "Publier sur le fil principal uniquement"
followers: "Abonnés·es"
followers-desc: "Publier à vos abonnés·es uniquement"
followers: "Abonné·e·s"
followers-desc: "Publier à vos abonné·e·s uniquement"
specified: "Direct"
specified-desc: "Publier uniquement aux utilisateurs·rices mentionnés·es"
private: "Privé"
@ -99,7 +99,7 @@ common:
f: "En attente de vos écrits"
search: "Recherche"
delete: "Supprimer"
loading: "Chargement"
loading: "Chargement en cours"
ok: "OK"
update-available-title: "Mise à jour disponible"
update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour."
@ -117,8 +117,8 @@ common:
this-setting-is-this-device-only: "Uniquement sur cet appareil"
use-os-default-emojis: "Utiliser les émojis standards du système"
do-not-use-in-production: 'Il sagit dune version de développement. Ne pas utiliser dans un environnement de production.'
is-remote-user: "Ces informations utilisateur ont été copiées."
is-remote-post: "Ceci est une publication distante"
is-remote-user: "Ces informations appartiennent à un·e utilisateur·rice distant·e."
is-remote-post: "Ceci est une publication distante."
view-on-remote: "Consulter le profil complet"
error:
title: 'Une erreur est survenue'
@ -151,7 +151,7 @@ common:
notifications: "Notifications"
users: "Utilisateur·rice·s"
polls: "Sondages"
post-form: "Formulaire de publication"
post-form: "Champs de publication"
server: "Info sur le serveur"
donation: "Dons"
nav: "Navigation"
@ -166,7 +166,7 @@ auth/views/form.vue:
account-write: "Modifications des informations du compte :"
note-write: "Publier."
like-write: "Réagir aux publications."
following-write: "S'abonner et se désabonner."
following-write: "Sabonner et se désabonner."
drive-read: "Lire votre Drive"
drive-write: "Téléverser/supprimer des fichiers dans votre Drive."
notification-read: "Lire vos notifications."
@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Annuler ce sondage"
common/views/components/reaction-picker.vue:
choose-reaction: "Choisissez votre réaction"
common/views/components/emoji-picker.vue:
custom-emoji: "Émoji personnalisé"
people: "Personnes"
animals-and-nature: "Animaux et nature"
food-and-drink: "Nourriture et boisson"
activity: "Activités"
travel-and-places: "Lieux et voyages"
objects: "Objets"
symbols: "Symboles"
flags: "Drapeaux"
common/views/components/signin.vue:
username: "Nom d'utilisateur·rice"
password: "Mot de passe"
@ -491,8 +501,8 @@ common/views/pages/follow.vue:
following: "Suit"
follow: "Suivre"
request-pending: "Demande d'abonnement en attente"
follow-processing: "En cours dabonnement"
follow-request: "Demande d'abonnement"
follow-processing: "Demande en attente"
follow-request: "Demande dabonnement"
desktop:
banner-crop-title: "Découpez la partie qui apparaitra comme bannière"
banner: "Bannière"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher"
desktop/views/components/follow-button.vue:
following: "Abonné·e"
follow: "Suivre"
request-pending: "En attente d'approbation"
follow-processing: "Continuer labonnement"
follow-request: "Demande d'abonnement"
desktop/views/components/followers-window.vue:
followers: "{} abonné·e·s"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "En cours blocage"
no-muted-users: "Aucun utilisateur·rice nest mis·e en sourdine"
no-blocked-users: "Aucun utilisateur·rice nest bloqué·e"
word-mute: "Filtre de mots"
muted-words: "Mots masqués"
muted-words-description: "Description des mots mis en sourdine"
save: "Enregistrer"
common/views/components/password-settings.vue:
reset: "Modifier le mot de passe"
enter-current-password: "Entrez votre mot de passe actuel"
@ -1067,7 +1075,7 @@ desktop/views/pages/user-list.users.vue:
desktop/views/pages/user/user.followers-you-know.vue:
title: "Abonné·e·s que vous connaissez"
loading: "Chargement en cours"
no-users: "Pas d'utilisateurs"
no-users: "Aucun abonné connu"
desktop/views/pages/user/user.friends.vue:
title: "Mentions fréquentes"
loading: "Chargement en cours"
@ -1079,8 +1087,8 @@ desktop/views/pages/user/user.photos.vue:
desktop/views/pages/user/user.profile.vue:
follows-you: "Vous suit"
stalk: "Traquer"
stalking: "ストーキングしています"
unstalk: "ストーク解除"
stalking: "Entrain de poursuivre"
unstalk: "Cesser la poursuite"
mute: "Mettre en sourdine"
muted: "Muting"
unmute: "Enlever la sourdine"
@ -1161,12 +1169,12 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "Abonné·e"
follow: "Suivre"
request-pending: "En attente d'approbation"
follow-processing: "En cours dabonnement"
follow-request: "Demande d'abonnement"
follow: "Sabonner"
request-pending: "Demande en attente"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "Abonnez-vous aux utilisateurs"
empty: "Impossible de trouver des utilisateurs·trices à recommander."
@ -1219,7 +1227,7 @@ mobile/views/components/ui.header.vue:
mobile/views/components/ui.nav.vue:
timeline: "Fil d'actualité"
notifications: "Notifications"
follow-requests: "Demandes d'abonnement"
follow-requests: "Demandes dabonnement"
search: "Rechercher"
favorites: "Favoris"
user-lists: "Listes"
@ -1267,8 +1275,8 @@ mobile/views/pages/widgets/activity.vue:
mobile/views/pages/share.vue:
share-with: "Partager avec {name}"
mobile/views/pages/received-follow-requests.vue:
title: "Demandes d'abonnement"
accept: "Approuver"
title: "Demandes dabonnement"
accept: "Accepter"
reject: "Refuser"
mobile/views/pages/note.vue:
title: "Post"
@ -1363,7 +1371,7 @@ mobile/views/pages/user/home.vue:
followers-you-know: "Abonné·e·s que vous connaissez"
last-used-at: "Dernière connexion il y a"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "Pas d'utilisateurs"
no-users: "Aucun utilisateur connu"
mobile/views/pages/user/home.friends.vue:
no-users: "Pass d'utilisateurs"
mobile/views/pages/user/home.notes.vue:

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -668,13 +668,6 @@ desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
@ -1041,6 +1034,7 @@ admin/views/index.vue:
dashboard: "ダッシュボード"
instance: "インスタンス"
emoji: "カスタム絵文字"
moderators: "モデレーター"
users: "ユーザー"
update: "更新"
announcements: "お知らせ"
@ -1140,6 +1134,12 @@ admin/views/users.vue:
unverify: "公式アカウントを解除する"
unverified: "公式アカウントを解除しました"
admin/views/moderators.vue:
add-moderator:
title: "モデレーターの登録"
add: "登録"
added: "モデレーターを登録しました"
admin/views/emoji.vue:
add-emoji:
title: "絵文字の登録"
@ -1336,7 +1336,7 @@ mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートをほかそ"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクション、どれにするんや?"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "ちょっと見せられへんわ"
click-to-show: "クリックして見せるで"
desktop/views/components/follow-button.vue:
following: "フォローしとる"
follow: "フォロー"
request-pending: "フォローの許し待っとる"
follow-processing: "今フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしとるユーザーはおらんで"
no-blocked-users: "ブロックしとるユーザーはおらんで"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワード変える"
enter-current-password: "今のパスワードを入れてや"
@ -1161,12 +1169,12 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "ちょっと見せられへんわ"
click-to-show: "押してみ、見せたるわ"
mobile/views/components/follow-button.vue:
following: "フォローしとる"
common/views/components/follow-button.vue:
following: "フォロー"
follow: "フォロー"
request-pending: "フォローの許し待っとる"
follow-processing: "フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "おもろそうやな"
empty: "おすすめのユーザーはおらん。"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Deze peiling vernietigen"
common/views/components/reaction-picker.vue:
choose-reaction: "Kies een reactie"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Gebruikersnaam"
password: "Wachtwoord"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "Volgen"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "Volgers van {}"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,9 +1169,9 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "Volgen"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Brukernavn"
password: "Passord"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "Innholdet er NSFW"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "Følger"
follow: "Følg"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,9 +1169,9 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "Innholdet er NSFW"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
following: "Følger"
follow: "Følg"
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Usuń tę ankietę"
common/views/components/reaction-picker.vue:
choose-reaction: "Wybierz reakcję"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Nazwa użytkownika"
password: "Hasło"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "To jest zawartość NSFW"
click-to-show: "Naciśnij aby wyświetlić"
desktop/views/components/follow-button.vue:
following: "Śledzisz"
follow: "Śledź"
request-pending: "Oczekiwanie na pozwolenie"
follow-processing: "フォロー処理中"
follow-request: "Poproś o śledzenie"
desktop/views/components/followers-window.vue:
followers: "Śledzący"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,12 +1169,12 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "To jest zawartość NSFW"
click-to-show: "Naciśnij aby wyświetlić"
mobile/views/components/follow-button.vue:
following: "Śledzisz"
follow: "Śledź"
request-pending: "Oczekiwanie na pozwolenie"
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "Poproś o śledzenie"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "Zacznij śledzić ludzi takich jak Ty"
empty: "Nie znaleziono podobnych użytkowników."

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -1161,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"

View File

@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.49.0",
"clientVersion": "2.0.11740",
"version": "10.51.0",
"clientVersion": "2.0.11800",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@ -176,6 +176,7 @@
"pug": "2.0.3",
"punycode": "2.1.1",
"qrcode": "1.3.2",
"randomcolor": "0.5.3",
"ratelimiter": "3.2.0",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.10",
@ -198,6 +199,7 @@
"summaly": "2.2.0",
"systeminformation": "3.47.0",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.1.0",
"textarea-caret": "3.1.0",
"tinycolor2": "1.4.1",
"tmp": "0.0.33",
@ -217,10 +219,10 @@
"vue-i18n": "8.3.1",
"vue-js-modal": "1.3.26",
"vue-loader": "15.4.2",
"vue-marquee-text-component": "1.1.0",
"vue-router": "3.0.1",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.1",
"vue-sweetalert2": "1.5.7",
"vue-template-compiler": "2.5.17",
"vuedraggable": "2.16.0",
"vuewordcloud": "18.7.11",

View File

@ -41,22 +41,22 @@ export default Vue.extend({
methods: {
add() {
this.announcements.push({
this.announcements.unshift({
title: '',
text: ''
});
},
remove(i) {
this.$swal({
this.$root.alert({
type: 'warning',
text: this.$t('_remove.are-you-sure').replace('$1', this.announcements.find((_, j) => j == i).title),
showCancelButton: true
}).then(res => {
if (!res.value) return;
if (!res) return;
this.announcements = this.announcements.filter((_, j) => j !== i);
this.save(true);
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('_remove.removed')
});
@ -68,13 +68,13 @@ export default Vue.extend({
broadcasts: this.announcements
}).then(() => {
if (!silent) {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});
}
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});

View File

@ -3,17 +3,17 @@
<table>
<thead>
<tr>
<th><fa icon="exchange-alt"/> In/Out</th>
<th><fa :icon="faExchangeAlt"/> In/Out</th>
<th><fa :icon="faBolt"/> Activity</th>
<th><fa icon="server"/> Host</th>
<th><fa icon="bolt"/> Activity</th>
<th><fa icon="user"/> Actor</th>
</tr>
</thead>
<tbody>
<tr v-for="log in logs" :key="log.id">
<td :class="log.direction">{{ log.direction == 'in' ? '<' : '>' }} {{ log.direction }}</td>
<td>{{ log.host }}</td>
<td>{{ log.activity }}</td>
<td>{{ log.host }}</td>
<td>@{{ log.actor }}</td>
</tr>
</tbody>
@ -23,12 +23,14 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faExchangeAlt } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
data() {
return {
logs: [],
connection: null
connection: null,
faBolt, faExchangeAlt
};
},

View File

@ -37,7 +37,7 @@
</div>
<div>
<div>
<div><fa icon="database"/></div>
<div><fa :icon="faDatabase"/></div>
<div>
<span>{{ $t('drive') }}</span>
<b>{{ stats.driveUsageLocal | bytes }}</b>
@ -83,9 +83,11 @@ import i18n from '../../i18n';
import XCpuMemory from "./cpu-memory.vue";
import XCharts from "./charts.vue";
import XApLog from "./ap-log.vue";
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/dashboard.vue'),
components: {
XCpuMemory,
XCharts,
@ -96,7 +98,8 @@ export default Vue.extend({
return {
stats: null,
connection: null,
meta: null
meta: null,
faDatabase
};
},

View File

@ -23,7 +23,7 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="['far', 'grin']"/> {{ $t('emojis.title') }}</div>
<div slot="title"><fa :icon="faGrin"/> {{ $t('emojis.title') }}</div>
<section v-for="emoji in emojis">
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
<ui-horizon-group inputs>
@ -50,6 +50,7 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { faGrin } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/emoji.vue'),
@ -58,7 +59,8 @@ export default Vue.extend({
name: '',
url: '',
aliases: '',
emojis: []
emojis: [],
faGrin
};
},
@ -73,13 +75,13 @@ export default Vue.extend({
url: this.url,
aliases: this.aliases.split(' ').filter(x => x.length > 0)
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('add-emoji.added')
});
this.fetchEmojis();
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});
@ -101,12 +103,12 @@ export default Vue.extend({
url: emoji.url,
aliases: emoji.aliases.split(' ').filter(x => x.length > 0)
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('updated')
});
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});
@ -114,23 +116,23 @@ export default Vue.extend({
},
removeEmoji(emoji) {
this.$swal({
this.$root.alert({
type: 'warning',
text: this.$t('remove-emoji.are-you-sure').replace('$1', emoji.name),
showCancelButton: true
}).then(res => {
if (!res.value) return;
if (!res) return;
this.$root.api('admin/emoji/remove', {
id: emoji.id
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('remove-emoji.removed')
});
this.fetchEmojis();
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});

View File

@ -20,8 +20,9 @@
<ul>
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="['far', 'grin']" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
@ -29,21 +30,27 @@
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">{{ $t('update') }}</li> -->
</ul>
<div class="back-to-misskey">
<a href="/"><fa icon="arrow-left"/> {{ $t('back-to-misskey') }}</a>
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
</div>
<div class="version">
<small>Misskey {{ version }}</small>
</div>
</nav>
<main>
<marquee-text v-if="instances.length > 0" class="instances" :repeat="10" :duration="10">
<span v-for="instance in instances" class="instance"><b :style="{ background: instance.bg }">{{ instance.host }}</b>{{ instance.notesCount | number }}</span>
</marquee-text>
<div class="page">
<div v-if="page == 'dashboard'"><x-dashboard/></div>
<div v-if="page == 'instance'"><x-instance/></div>
<div v-if="page == 'moderators'"><x-moderators/></div>
<div v-if="page == 'users'"><x-users/></div>
<div v-if="page == 'emoji'"><x-emoji/></div>
<div v-if="page == 'announcements'"><x-announcements/></div>
<div v-if="page == 'hashtags'"><x-hashtags/></div>
<div v-if="page == 'drive'"></div>
<div v-if="page == 'update'"></div>
</div>
</main>
</div>
</template>
@ -54,10 +61,15 @@ import i18n from '../../i18n';
import { version } from '../../config';
import XDashboard from "./dashboard.vue";
import XInstance from "./instance.vue";
import XModerators from "./moderators.vue";
import XEmoji from "./emoji.vue";
import XAnnouncements from "./announcements.vue";
import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue";
import { faHeadset, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { faGrin } from '@fortawesome/free-regular-svg-icons';
import MarqueeText from 'vue-marquee-text-component';
import randomColor from 'randomcolor';
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
@ -68,10 +80,12 @@ export default Vue.extend({
components: {
XDashboard,
XInstance,
XModerators,
XEmoji,
XAnnouncements,
XHashtags,
XUsers
XUsers,
MarqueeText
},
provide: {
isMobile
@ -81,9 +95,24 @@ export default Vue.extend({
page: 'dashboard',
version,
isMobile,
navOpend: !isMobile
navOpend: !isMobile,
instances: [],
faGrin,
faArrowLeft,
faHeadset
};
},
created() {
this.$root.api('instances').then(instances => {
instances.forEach(i => {
i.bg = randomColor({
seed: i.host,
luminosity: 'dark'
});
});
this.instances = instances;
});
},
methods: {
nav(page: string) {
this.page = page;
@ -92,7 +121,7 @@ export default Vue.extend({
});
</script>
<style lang="stylus">
<style lang="stylus" scoped>
.mk-admin
$headerHeight = 48px
@ -253,6 +282,22 @@ export default Vue.extend({
> main
width 100%
padding 0 0 0 250px
> .instances
padding 8px
background rgba(0, 0, 0, 0.7)
color #fff
font-size 14px
>>> .instance
margin 0 10px
> b
padding 0px 6px
margin-right 4px
border-radius 4px
> .page
max-width 1300px
&.isMobile

View File

@ -10,7 +10,7 @@
<ui-input v-model="languages"><i slot="icon"><fa icon="language"/></i>{{ $t('languages') }}<span slot="desc">{{ $t('languages-desc') }}</span></ui-input>
</section>
<section class="fit-bottom">
<header><fa icon="headset"/> {{ $t('maintainer-config') }}</header>
<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="['far', 'envelope']"/></i>{{ $t('maintainer-email') }}</ui-input>
</section>
@ -24,14 +24,14 @@
<ui-input v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $t('remote-drive-capacity-mb') }}<span slot="suffix">MB</span><span slot="desc">{{ $t('mb') }}</span></ui-input>
</section>
<section class="fit-bottom">
<header><fa icon="shield-alt"/> {{ $t('recaptcha-config') }}</header>
<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-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>
</section>
<section>
<header><fa icon="ghost"/> {{ $t('proxy-account-config') }}</header>
<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-info warn>{{ $t('proxy-account-warn') }}</ui-info>
@ -84,9 +84,11 @@ import Vue from 'vue';
import i18n from '../../i18n';
import { host } from '../../config';
import { toUnicode } from 'punycode';
import { faHeadset, faShieldAlt, faGhost } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/instance.vue'),
data() {
return {
host: toUnicode(host),
@ -113,6 +115,7 @@ export default Vue.extend({
githubClientSecret: null,
proxyAccount: null,
inviteCode: null,
faHeadset, faShieldAlt, faGhost
};
},
@ -146,7 +149,7 @@ export default Vue.extend({
this.$root.api('admin/invite').then(x => {
this.inviteCode = x.code;
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});
@ -178,12 +181,12 @@ export default Vue.extend({
githubClientId: this.githubClientId,
githubClientSecret: this.githubClientSecret,
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});

View File

@ -0,0 +1,61 @@
<template>
<div class="jnhmugbb">
<ui-card>
<div slot="title"><fa icon="plus"/> {{ $t('add-moderator.title') }}</div>
<section class="fit-top">
<ui-input v-model="username" type="text">
<span slot="prefix">@</span>
</ui-input>
<ui-button @click="add" :disabled="adding">{{ $t('add-moderator.add') }}</ui-button>
</section>
</ui-card>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import parseAcct from "../../../../misc/acct/parse";
export default Vue.extend({
i18n: i18n('admin/views/moderators.vue'),
data() {
return {
username: '',
adding: false
};
},
methods: {
async add() {
this.adding = true;
const process = async () => {
const user = await this.$root.api('users/show', parseAcct(this.username));
await this.$root.api('admin/moderators/add', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('add-moderator.added')
});
};
await process().catch(e => {
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.adding = false;
},
}
});
</script>
<style lang="stylus" scoped>
.jnhmugbb
@media (min-width 500px)
padding 16px
</style>

View File

@ -49,6 +49,7 @@ import parseAcct from "../../../../misc/acct/parse";
export default Vue.extend({
i18n: i18n('admin/views/users.vue'),
data() {
return {
verifyUsername: null,
@ -67,13 +68,19 @@ export default Vue.extend({
this.verifying = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.verifyUsername));
await this.$root.os.api('admin/verify-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('verified') });
const user = await this.$root.api('users/show', parseAcct(this.verifyUsername));
await this.$root.api('admin/verify-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('verified')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.verifying = false;
@ -83,13 +90,19 @@ export default Vue.extend({
this.unverifying = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.unverifyUsername));
await this.$root.os.api('admin/unverify-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('unverified') });
const user = await this.$root.api('users/show', parseAcct(this.unverifyUsername));
await this.$root.api('admin/unverify-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('unverified')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.unverifying = false;
@ -99,13 +112,19 @@ export default Vue.extend({
this.suspending = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.suspendUsername));
await this.$root.os.api('admin/suspend-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('suspended') });
const user = await this.$root.api('users/show', parseAcct(this.suspendUsername));
await this.$root.api('admin/suspend-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('suspended')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.suspending = false;
@ -115,13 +134,19 @@ export default Vue.extend({
this.unsuspending = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.unsuspendUsername));
await this.$root.os.api('admin/unsuspend-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('unsuspended') });
const user = await this.$root.api('users/show', parseAcct(this.unsuspendUsername));
await this.$root.api('admin/unsuspend-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('unsuspended')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.unsuspending = false;

View File

@ -123,29 +123,3 @@ pre
[data-icon]
display inline-block
.swal2-container
z-index 10000 !important
&.swal2-shown
background-color rgba(0, 0, 0, 0.5) !important
.swal2-popup
background var(--face) !important
.swal2-content
color var(--text) !important
.swal2-confirm
background-color var(--primary) !important
border-left-color var(--primary) !important
border-right-color var(--primary) !important
color var(--primaryForeground) !important
&:hover
background-image none !important
background-color var(--primaryDarken5) !important
&:active
background-image none !important
background-color var(--primaryDarken5) !important

View File

@ -9,14 +9,11 @@ import './style.styl';
import init from '../init';
import Index from './views/index.vue';
import * as config from '../config';
/**
* init
*/
init(launch => {
document.title = `${config.name} | %i18n:common.application-authorization%`;
// Init router
const router = new VueRouter({
mode: 'history',

View File

@ -43,6 +43,9 @@
if (`${url.pathname}/`.startsWith('/admin/')) app = 'admin';
//#endregion
// Script version
const ver = localStorage.getItem('v') || VERSION;
//#region Detect the user language
let lang = null;
@ -67,7 +70,7 @@
let locale = localStorage.getItem('locale');
if (locale == null) {
const locale = await fetch(`/assets/locales/${lang}.json`)
const locale = await fetch(`/assets/locales/${lang}.json?ver=${ver}`)
.then(response => response.json());
localStorage.setItem('locale', JSON.stringify(locale));
@ -98,9 +101,6 @@
app = isMobile ? 'mobile' : 'desktop';
}
// Script version
const ver = localStorage.getItem('v') || VERSION;
// Get salt query
const salt = localStorage.getItem('salt')
? `?salt=${localStorage.getItem('salt')}`

View File

@ -66,7 +66,7 @@ export default function<T extends object>(data: {
this.bakeProps();
(this as any).api('i/update_widget', {
this.$root.api('i/update_widget', {
id: this.id,
data: this.props
});

View File

@ -22,7 +22,7 @@ export default async function($root: any, force = false, silent = false) {
}
if (!silent) {
$root.$dialog({
$root.alert({
title: $root.$t('@.update-available-title'),
text: $root.$t('@.update-available', { newer, current })
});

View File

@ -4,12 +4,9 @@ export default ($root: any) => {
require('fuckadblock');
function adBlockDetected() {
$root.$dialog({
$root.alert({
title: $root.$t('@.adblock.detected'),
text: $root.$t('@.adblock.warning'),
actins: [{
text: 'OK'
}]
text: $root.$t('@.adblock.warning')
});
}

View File

@ -3,7 +3,6 @@ import { sum } from '../../../../prelude/array';
import shouldMuteNote from './should-mute-note';
import MkNoteMenu from '../views/components/note-menu.vue';
import MkReactionPicker from '../views/components/reaction-picker.vue';
import Ok from '../views/components/ok.vue';
function focus(el, fn) {
const target = fn(el);
@ -142,7 +141,10 @@ export default (opts: Opts = {}) => ({
this.$root.api('notes/favorites/create', {
noteId: this.appearNote.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
});
},

View File

@ -0,0 +1,197 @@
<template>
<div class="felqjxyj" :class="{ splash }">
<div class="bg" ref="bg" @click="onBgClick"></div>
<div class="main" ref="main">
<div class="icon" :class="type"><fa :icon="icon"/></div>
<header v-if="title" v-html="title"></header>
<div class="body" v-if="text" v-html="text"></div>
<ui-horizon-group no-grow class="buttons" v-if="!splash">
<ui-button @click="ok" primary autofocus>OK</ui-button>
<ui-button @click="cancel" v-if="showCancelButton">Cancel</ui-button>
</ui-horizon-group>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
import { faTimesCircle, faQuestionCircle } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
props: {
type: {
type: String,
required: false,
default: 'info'
},
title: {
type: String,
required: false
},
text: {
type: String,
required: false
},
showCancelButton: {
type: Boolean,
default: false
},
splash: {
type: Boolean,
default: false
}
},
computed: {
icon(): any {
switch (this.type) {
case 'success': return 'check';
case 'error': return faTimesCircle;
case 'warning': return 'exclamation-triangle';
case 'info': return 'info-circle';
case 'question': return faQuestionCircle;
}
}
},
mounted() {
this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto';
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 100,
easing: 'linear'
});
anime({
targets: this.$refs.main,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
if (this.splash) {
setTimeout(() => {
this.close();
}, 1000);
}
});
},
methods: {
ok() {
this.$emit('ok');
this.close();
},
cancel() {
this.$emit('cancel');
this.close();
},
close() {
(this.$refs.bg as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
(this.$refs.main as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.main,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [0, 0.5, 0.5, 1],
complete: () => this.destroyDom()
});
},
onBgClick() {
this.cancel();
}
}
});
</script>
<style lang="stylus" scoped>
.felqjxyj
display flex
align-items center
justify-content center
position fixed
z-index 30000
top 0
left 0
width 100%
height 100%
&.splash
&, *
pointer-events none !important
> .main
min-width 0
width initial
> .bg
display block
position fixed
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
pointer-events none
> .main
display block
position fixed
margin auto
padding 32px
min-width 320px
max-width 480px
width calc(100% - 32px)
text-align center
background var(--face)
border-radius 8px
color var(--faceText)
opacity 0
> .icon
font-size 32px
&.success
color #37ec92
&.error
color #ec4137
&.warning
color #ecb637
> *
display block
margin 0 auto
> header
margin 16px 0 8px 0
font-weight bold
font-size 20px
& + .body
margin-top 8px
> .body
margin 16px 0
> .buttons
margin-top 16px
</style>

View File

@ -37,6 +37,8 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import { lib } from 'emojilib';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice } from '@fortawesome/free-solid-svg-icons';
import { faHeart, faFlag } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/components/emoji-picker.vue'),
@ -48,7 +50,7 @@ export default Vue.extend({
categories: [{
ref: 'customEmojiSection',
text: this.$t('custom-emoji'),
icon: ['fas', 'asterisk'],
icon: faAsterisk,
isActive: true
}, {
name: 'people',
@ -60,43 +62,43 @@ export default Vue.extend({
name: 'animals_and_nature',
ref: 'animalsAndNatureSection',
text: this.$t('animals-and-nature'),
icon: ['fas', 'leaf'],
icon: faLeaf,
isActive: false
}, {
name: 'food_and_drink',
ref: 'foodAndDrinkSection',
text: this.$t('food-and-drink'),
icon: ['fas', 'utensils'],
icon: faUtensils,
isActive: false
}, {
name: 'activity',
ref: 'activitySection',
text: this.$t('activity'),
icon: ['fas', 'futbol'],
icon: faFutbol,
isActive: false
}, {
name: 'travel_and_places',
ref: 'travelAndPlacesSection',
text: this.$t('travel-and-places'),
icon: ['fas', 'city'],
icon: faCity,
isActive: false
}, {
name: 'objects',
ref: 'objectsSection',
text: this.$t('objects'),
icon: ['fas', 'poo-storm'],
icon: faDice,
isActive: false
}, {
name: 'symbols',
ref: 'symbolsSection',
text: this.$t('symbols'),
icon: ['far', 'heart'],
icon: faHeart,
isActive: false
}, {
name: 'flags',
ref: 'flagsSection',
text: this.$t('flags'),
icon: ['far', 'flag'],
icon: faFlag,
isActive: false
}]
}

View File

@ -0,0 +1,184 @@
<template>
<button class="wfliddvnhxvyusikowhxozkyxyenqxqr"
:class="{ wait, block, mini, active: isFollowing || hasPendingFollowRequestFromYou }"
@click="onClick"
:disabled="wait"
>
<template v-if="!wait">
<fa :icon="iconAndText[0]"/> <template v-if="!mini">{{ iconAndText[1] }}</template>
</template>
<template v-else><fa icon="spinner" pulse fixed-width/></template>
</button>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('common/views/components/follow-button.vue'),
props: {
user: {
type: Object,
required: true
},
block: {
type: Boolean,
required: false,
default: false
},
mini: {
type: Boolean,
required: false,
default: false
}
},
data() {
return {
isFollowing: this.user.isFollowing,
hasPendingFollowRequestFromYou: this.user.hasPendingFollowRequestFromYou,
wait: false,
connection: null
};
},
computed: {
iconAndText(): any[] {
return (
(this.hasPendingFollowRequestFromYou && this.user.isLocked) ? ['hourglass-half', this.$t('request-pending')] :
(this.hasPendingFollowRequestFromYou && !this.user.isLocked) ? ['hourglass-start', this.$t('follow-processing')] :
(this.isFollowing) ? ['minus', this.$t('following')] :
(!this.isFollowing && this.user.isLocked) ? ['plus', this.$t('follow-request')] :
(!this.isFollowing && !this.user.isLocked) ? ['plus', this.$t('follow')] :
[]
);
}
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onFollowChange(user) {
if (user.id == this.user.id) {
this.isFollowing = user.isFollowing;
this.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
}
},
async onClick() {
this.wait = true;
try {
if (this.isFollowing) {
await this.$root.api('following/delete', {
userId: this.user.id
});
} else {
if (this.hasPendingFollowRequestFromYou) {
await this.$root.api('following/requests/cancel', {
userId: this.user.id
});
} else if (this.user.isLocked) {
await this.$root.api('following/create', {
userId: this.user.id
});
this.hasPendingFollowRequestFromYou = true;
} else {
await this.$root.api('following/create', {
userId: this.user.id
});
this.hasPendingFollowRequestFromYou = true;
}
}
} catch (e) {
console.error(e);
} finally {
this.wait = false;
}
}
}
});
</script>
<style lang="stylus" scoped>
.wfliddvnhxvyusikowhxozkyxyenqxqr
display block
user-select none
cursor pointer
padding 0 16px
margin 0
min-width 100px
line-height 36px
font-size 14px
font-weight bold
color var(--primary)
background transparent
outline none
border solid 1px var(--primary)
border-radius 36px
&.mini
padding 0
min-width 0
width 32px
height 32px
font-size 16px
border-radius 4px
line-height 32px
&:focus
&:after
border-radius 8px
&.block
width 100%
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid var(--primaryAlpha03)
border-radius 36px
&:hover
background var(--primaryAlpha01)
&:active
background var(--primaryAlpha02)
&.active
color var(--primaryForeground)
background var(--primary)
&:hover
background var(--primaryLighten10)
border-color var(--primaryLighten10)
&:active
background var(--primaryDarken10)
border-color var(--primaryDarken10)
&.wait
cursor wait !important
opacity 0.7
*
pointer-events none
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="mk-github-setting">
<p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-github`" target="_blank">{{ $t('detail') }}</a></p>
<p>{{ $t('description') }}</p>
<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
<p>
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? this.$t('reconnect') : this.$t('connect') }}</a>
@ -14,15 +14,14 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, docsUrl } from '../../../config';
import { apiUrl } from '../../../config';
export default Vue.extend({
i18n: i18n('common/views/components/github-setting.vue'),
data() {
return {
form: null,
apiUrl,
docsUrl
apiUrl
};
},
mounted() {

View File

@ -1,13 +1,8 @@
import Vue from 'vue';
import muteAndBlock from './mute-and-block.vue';
import followButton from './follow-button.vue';
import error from './error.vue';
import apiSettings from './api-settings.vue';
import passwordSettings from './password-settings.vue';
import driveSettings from './drive-settings.vue';
import profileEditor from './profile-editor.vue';
import noteSkeleton from './note-skeleton.vue';
import theme from './theme.vue';
import instance from './instance.vue';
import cwButton from './cw-button.vue';
import tagCloud from './tag-cloud.vue';
@ -27,7 +22,6 @@ import pollEditor from './poll-editor.vue';
import reactionIcon from './reaction-icon.vue';
import reactionsViewer from './reactions-viewer.vue';
import time from './time.vue';
import timer from './timer.vue';
import mediaList from './media-list.vue';
import uploader from './uploader.vue';
import streamIndicator from './stream-indicator.vue';
@ -51,14 +45,9 @@ import uiInfo from './ui/info.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
Vue.component('mk-mute-and-block', muteAndBlock);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-error', error);
Vue.component('mk-api-settings', apiSettings);
Vue.component('mk-password-settings', passwordSettings);
Vue.component('mk-drive-settings', driveSettings);
Vue.component('mk-profile-editor', profileEditor);
Vue.component('mk-note-skeleton', noteSkeleton);
Vue.component('mk-theme', theme);
Vue.component('mk-instance', instance);
Vue.component('mk-cw-button', cwButton);
Vue.component('mk-tag-cloud', tagCloud);
@ -78,7 +67,6 @@ Vue.component('mk-poll-editor', pollEditor);
Vue.component('mk-reaction-icon', reactionIcon);
Vue.component('mk-reactions-viewer', reactionsViewer);
Vue.component('mk-time', time);
Vue.component('mk-timer', timer);
Vue.component('mk-media-list', mediaList);
Vue.component('mk-uploader', uploader);
Vue.component('mk-stream-indicator', streamIndicator);

View File

@ -8,7 +8,7 @@
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>{{ $t('no-history') }}</p>
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
</button>
<template v-for="(message, i) in _messages">
<x-message :message="message" :key="message.id"/>
@ -20,7 +20,7 @@
<footer>
<transition name="fade">
<div class="new-message" v-show="showIndicator">
<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>{{ $t('new-message') }}</button>
<button @click="onIndicatorClick"><i><fa :icon="faArrowCircleDown"/></i>{{ $t('new-message') }}</button>
</div>
</transition>
<x-form :user="user" ref="form"/>
@ -34,6 +34,7 @@ import i18n from '../../../i18n';
import XMessage from './messaging-room.message.vue';
import XForm from './messaging-room.form.vue';
import { url } from '../../../config';
import { faArrowCircleDown } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/components/messaging-room.vue'),
@ -52,7 +53,8 @@ export default Vue.extend({
existMoreMessages: false,
connection: null,
showIndicator: false,
timer: null
timer: null,
faArrowCircleDown
};
},

View File

@ -45,7 +45,7 @@
</template>
</div>
<p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
</div>
</template>

View File

@ -9,7 +9,6 @@ import Vue from 'vue';
import i18n from '../../../i18n';
import { url } from '../../../config';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import Ok from './ok.vue';
import { concat, intersperse } from '../../../../../prelude/array';
export default Vue.extend({
@ -56,7 +55,7 @@ export default Vue.extend({
}
] : []
], [
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin ? [{
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [{
icon: ['far', 'trash-alt'],
text: this.$t('delete'),
action: this.del
@ -79,7 +78,10 @@ export default Vue.extend({
this.$root.api('i/pin', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
this.destroyDom();
});
},
@ -93,19 +95,29 @@ export default Vue.extend({
},
del() {
if (!window.confirm(this.$t('delete-confirm'))) return;
this.$root.alert({
type: 'warning',
text: this.$t('delete-confirm'),
showCancelButton: true
}).then(res => {
if (!res) return;
this.$root.api('notes/delete', {
noteId: this.note.id
}).then(() => {
this.destroyDom();
});
});
},
favorite() {
this.$root.api('notes/favorites/create', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
this.destroyDom();
});
},
@ -114,7 +126,10 @@ export default Vue.extend({
this.$root.api('notes/favorites/delete', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
this.destroyDom();
});
},

View File

@ -1,175 +0,0 @@
<template>
<div class="yvbkymdqeusiqucuuloahhiqflzinufs">
<div class="bg" ref="bg"></div>
<div class="body" ref="body">
<div class="icon">
<div class="circle left"></div>
<span class="check tip"></span>
<span class="check long"></span>
<div class="ring"></div>
<div class="fix"></div>
<div class="circle right"></div>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
export default Vue.extend({
mounted() {
this.$nextTick(() => {
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 300,
easing: 'linear'
});
anime({
targets: this.$refs.body,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
});
setTimeout(() => {
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
anime({
targets: this.$refs.body,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [0.5, 0, 1, 0.5],
complete: () => this.destroyDom()
});
}, 1250);
}
});
</script>
<style lang="stylus" scoped>
.yvbkymdqeusiqucuuloahhiqflzinufs
pointer-events none
> .bg
display block
position fixed
z-index 10000
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
> .body
position fixed
z-index 10000
top 0
right 0
left 0
bottom 0
margin auto
width 150px
height 150px
background var(--face)
border-radius 8px
opacity 0
> .icon
display flex
justify-content center
position absolute
top 0
right 0
left 0
bottom 0
width 5em
height 5em
margin auto
border .25em solid transparent
border-radius 50%
line-height 5em
cursor default
box-sizing content-box
user-select none
zoom normal
border-color #a5dc86
> .circle
position absolute
width 3.75em
height 7.5em
transform rotate(45deg)
border-radius 50%
background var(--face)
&.left
top -.4375em
left -2.0635em
transform rotate(-45deg)
transform-origin 3.75em 3.75em
border-radius 7.5em 0 0 7.5em
&.right
top -.6875em
left 1.875em
transform rotate(-45deg)
transform-origin 0 3.75em
border-radius 0 7.5em 7.5em 0
animation swal2-rotate-success-circular-line 4.25s ease-in
> .check
display block
position absolute
height .3125em
border-radius .125em
background-color #a5dc86
z-index 2
&.tip
top 2.875em
left .875em
width 1.5625em
transform rotate(45deg)
animation swal2-animate-success-line-tip .75s
&.long
top 2.375em
right .5em
width 2.9375em
transform rotate(-45deg)
animation swal2-animate-success-line-long .75s
> .fix
position absolute
top .5em
left 1.625em
width .4375em
height 5.625em
transform rotate(-45deg)
z-index 1
background var(--face)
> .ring
position absolute
top -.25em
left -.25em
width 100%
height 100%
border .25em solid rgba(165,220,134,.3)
border-radius 50%
z-index 2
box-sizing content-box
</style>

View File

@ -25,12 +25,9 @@ export default Vue.extend({
type: 'password'
}).then(newPassword2 => {
if (newPassword !== newPassword2) {
this.$dialog({
this.$root.alert({
title: null,
text: this.$t('not-match'),
actions: [{
text: 'OK'
}]
text: this.$t('not-match')
});
return;
}

View File

@ -193,7 +193,7 @@ export default Vue.extend({
this.$store.state.i.bannerUrl = i.bannerUrl;
if (notify) {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});
@ -223,6 +223,5 @@ export default Vue.extend({
width 72px
height 72px
margin auto
box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
</style>

View File

@ -10,7 +10,7 @@
<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 == '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>

View File

@ -1,11 +1,11 @@
<template>
<div class="mk-stream-indicator">
<p v-if="stream.state == 'initializing'">
<fa icon="spinner .pulse"/>
<fa icon="spinner" pulse/>
<span>{{ $t('connecting') }}<mk-ellipsis/></span>
</p>
<p v-if="stream.state == 'reconnecting'">
<fa icon="spinner .pulse"/>
<fa icon="spinner" pulse/>
<span>{{ $t('reconnecting') }}<mk-ellipsis/></span>
</p>
<p v-if="stream.state == 'connected'">

View File

@ -1,6 +1,6 @@
<template>
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
<div v-else>
<vue-word-cloud

View File

@ -1,7 +1,7 @@
<template>
<div class="nicnklzforebnpfgasiypmpdaaglujqm">
<label>
<span>{{ $t('light-theme') }}</span>
<span><fa :icon="faSun"/> {{ $t('light-theme') }}</span>
<ui-select v-model="light" :placeholder="$t('light-theme')">
<optgroup :label="$t('light-themes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@ -13,7 +13,7 @@
</label>
<label>
<span>{{ $t('dark-theme') }}</span>
<span><fa :icon="faMoon"/> {{ $t('dark-theme') }}</span>
<ui-select v-model="dark" :placeholder="$t('dark-theme')">
<optgroup :label="$t('dark-themes')">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@ -104,6 +104,7 @@ import { Chrome } from 'vue-color';
import * as uuid from 'uuid';
import * as tinycolor from 'tinycolor2';
import * as JSON5 from 'json5';
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
// 後方互換性のため
function convertOldThemedefinition(t) {
@ -135,7 +136,8 @@ export default Vue.extend({
myThemeDesc: '',
myThemePrimary: lightTheme.vars.primary,
myThemeSecondary: lightTheme.vars.secondary,
myThemeText: lightTheme.vars.text
myThemeText: lightTheme.vars.text,
faMoon, faSun
};
},
@ -221,7 +223,7 @@ export default Vue.extend({
try {
theme = JSON5.parse(code);
} catch (e) {
this.$swal({
this.$root.alert({
type: 'error',
text: this.$t('invalid-theme')
});
@ -234,7 +236,7 @@ export default Vue.extend({
}
if (theme.id == null) {
this.$swal({
this.$root.alert({
type: 'error',
text: this.$t('invalid-theme')
});
@ -242,7 +244,7 @@ export default Vue.extend({
}
if (this.$store.state.device.themes.some(t => t.id == theme.id)) {
this.$swal({
this.$root.alert({
type: 'info',
text: this.$t('already-installed')
});
@ -254,7 +256,7 @@ export default Vue.extend({
key: 'themes', value: themes
});
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('installed').replace('{}', theme.name)
});
@ -267,7 +269,7 @@ export default Vue.extend({
key: 'themes', value: themes
});
this.$swal({
this.$root.alert({
type: 'info',
text: this.$t('uninstalled').replace('{}', theme.name)
});
@ -304,7 +306,7 @@ export default Vue.extend({
const theme = this.myTheme;
if (theme.name == null || theme.name.trim() == '') {
this.$swal({
this.$root.alert({
type: 'warning',
text: this.$t('theme-name-required')
});
@ -318,7 +320,7 @@ export default Vue.extend({
key: 'themes', value: themes
});
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});

View File

@ -1,49 +0,0 @@
<template>
<time class="mk-time">
{{ hh }}:{{ mm }}:{{ ss }}
</time>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
time: {
type: [Date, String],
required: true
}
},
data() {
return {
tickId: null,
hh: null,
mm: null,
ss: null
};
},
computed: {
_time(): Date {
return typeof this.time == 'string' ? new Date(this.time) : this.time;
}
},
created() {
this.tick();
this.tickId = setInterval(this.tick, 1000);
},
destroyed() {
clearInterval(this.tickId);
},
methods: {
tick() {
const now = new Date().getTime();
const start = this._time.getTime();
const ago = Math.floor((now - start) / 1000);
this.hh = Math.floor(ago / (60 * 60)).toString().padStart(2, '0');
this.mm = Math.floor(ago / 60).toString().padStart(2, '0');
this.ss = (ago % 60).toString().padStart(2, '0');
}
}
});
</script>

View File

@ -1,6 +1,6 @@
<template>
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
<!-- トランジションを有効にするとなぜかメモリリークする -->
<transition-group v-else tag="div" name="chart">

View File

@ -1,6 +1,6 @@
<template>
<div class="mk-twitter-setting">
<p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-twitter`" target="_blank">{{ $t('detail') }}</a></p>
<p>{{ $t('description') }}</p>
<p class="account" v-if="$store.state.i.twitter" :title="`Twitter ID: ${$store.state.i.twitter.userId}`">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
<p>
<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ $store.state.i.twitter ? this.$t('reconnect') : this.$t('connect') }}</a>
@ -14,15 +14,14 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, docsUrl } from '../../../config';
import { apiUrl } from '../../../config';
export default Vue.extend({
i18n: i18n('common/views/components/twitter-setting.vue'),
data() {
return {
form: null,
apiUrl,
docsUrl
apiUrl
};
},
mounted() {

View File

@ -38,12 +38,24 @@ export default Vue.extend({
type: Boolean,
required: false,
default: false
}
},
autofocus: {
type: Boolean,
required: false,
default: false
},
},
data() {
return {
styl: 'fill'
};
},
mounted() {
if (this.autofocus) {
this.$nextTick(() => {
this.$el.focus();
});
}
}
});
</script>
@ -57,6 +69,7 @@ export default Vue.extend({
text-align center
font-weight normal
font-size 16px
line-height 24px
border none
border-radius 6px
outline none
@ -85,6 +98,7 @@ export default Vue.extend({
&.inline
display inline-block
width auto
min-width 100px
&.primary
font-weight bold

View File

@ -1,5 +1,5 @@
<template>
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze" :class="{ inputs }">
<div class="vnxwkwuf" :class="{ inputs, noGrow }">
<slot></slot>
</div>
</template>
@ -15,21 +15,27 @@ export default Vue.extend({
type: Boolean,
required: false,
default: false
},
noGrow: {
type: Boolean,
required: false,
default: false
}
}
});
</script>
<style lang="stylus" scoped>
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
display flex
.vnxwkwuf
&.inputs
margin 32px 0
&:not(.noGrow)
display flex
> *
flex 1
&:not(:last-child)
> *:not(:last-child)
margin-right 16px
</style>

View File

@ -3,7 +3,7 @@
<ol v-if="uploads.length > 0">
<li v-for="ctx in uploads" :key="ctx.id">
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
<p class="name"><fa icon="spinner" pulse/>{{ ctx.name }}</p>
<p class="status">
<span class="initing" v-if="ctx.progress == undefined">{{ $t('waiting') }}<mk-ellipsis/></span>
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
@ -57,17 +57,11 @@ export default Vue.extend({
return;
}
// Upload if the file didn't exist yet
const buf = new Uint8Array(e.target.result);
let bin = '';
// We use for-of loop instead of apply() to avoid RangeError
// SEE: https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string
for (const byte of buf) bin += String.fromCharCode(byte);
const ctx = {
id: id,
name: file.name || 'untitled',
progress: undefined,
img: 'data:*/*;base64,' + btoa(bin)
img: window.URL.createObjectURL(file)
};
this.uploads.push(ctx);

View File

@ -25,7 +25,7 @@
<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
<template v-else><fa icon="spinner" pulse fixed-width/></template>
</button>
</div>
</template>

View File

@ -3,9 +3,15 @@
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="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>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div :class="$style.stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
<div v-for="image in images"
:class="$style.img"
:style="`background-image: url(${image.thumbnailUrl || image.url})`"
draggable="true"
@dragstart="onDragstart(image, $event)"
@dragend="onDragend"
></div>
</div>
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
</mk-widget-container>
@ -31,6 +37,7 @@ export default define({
connection: null
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
@ -44,9 +51,11 @@ export default define({
this.fetching = false;
});
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onDriveFileCreated(file) {
if (/^image\/.+$/.test(file.type)) {
@ -54,6 +63,7 @@ export default define({
if (this.images.length > 9) this.images.pop();
}
},
func() {
if (this.props.design == 2) {
this.props.design = 0;
@ -62,7 +72,16 @@ export default define({
}
this.save();
}
},
onDragstart(file, e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('mk_drive_file', JSON.stringify(file));
},
onDragend(e) {
this.browser.isDragSource = false;
},
}
});
</script>

View File

@ -5,7 +5,7 @@
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div class="feed" v-else>
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
</div>

View File

@ -2,7 +2,7 @@
<div class="memory">
<x-pie class="pie" :value="usage"/>
<div>
<p><fa icon="flask"/>Memory</p>
<p><fa icon="memory"/>Memory</p>
<p>Total: {{ total | bytes(1) }}</p>
<p>Used: {{ used | bytes(1) }}</p>
<p>Free: {{ free | bytes(1) }}</p>

View File

@ -4,7 +4,7 @@
<template slot="header"><fa icon="server"/>{{ $t('title') }}</template>
<button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-if="!fetching">
<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>

View File

@ -8,12 +8,9 @@ export default ($root: any) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) {
$root.$dialog({
$root.alert({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return reject('invalid-filetype');
}
@ -90,12 +87,9 @@ export default ($root: any) => {
value: i.avatarUrl
});
$root.$dialog({
$root.alert({
title: '%fa:info-circle% %i18n:desktop.avatar-updated%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return i;

View File

@ -10,10 +10,7 @@ export default ($root: any) => {
if (!regex.test(file.name) ) {
$root.dialog({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return reject('invalid-filetype');
}
@ -90,12 +87,9 @@ export default ($root: any) => {
value: i.bannerUrl
});
$root.$dialog({
$root.alert({
title: '%fa:info-circle% %i18n:desktop.banner-updated%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return i;

View File

@ -34,7 +34,6 @@ import PostFormWindow from './views/components/post-form-window.vue';
import RenoteFormWindow from './views/components/renote-form-window.vue';
import MkChooseFileFromDriveWindow from './views/components/choose-file-from-drive-window.vue';
import MkChooseFolderFromDriveWindow from './views/components/choose-folder-from-drive-window.vue';
import Dialog from './views/components/dialog.vue';
import InputDialog from './views/components/input-dialog.vue';
import Notification from './views/components/ui-notification.vue';
@ -114,21 +113,6 @@ init(async (launch) => {
});
},
$dialog(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
});
},
$input(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};

View File

@ -4,7 +4,7 @@
<template slot="header"><fa icon="chart-bar"/>{{ $t('title') }}</template>
<button slot="func" :title="$t('toggle')" @click="toggle"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<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)"/>

View File

@ -1,168 +0,0 @@
<template>
<div class="mk-dialog">
<div class="bg" ref="bg" @click="onBgClick"></div>
<div class="main" ref="main">
<header v-html="title" :class="$style.header"></header>
<div class="body" v-html="text"></div>
<div class="buttons">
<button v-for="button in buttons" @click="click(button)">{{ button.text }}</button>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
export default Vue.extend({
props: {
title: {
type: String,
required: false
},
text: {
type: String,
required: true
},
buttons: {
type: Array,
default: () => {
return [{
text: 'OK'
}];
}
},
modal: {
type: Boolean,
default: false
}
},
mounted() {
this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto';
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 100,
easing: 'linear'
});
anime({
targets: this.$refs.main,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
});
},
methods: {
click(button) {
this.$emit('clicked', button.id);
this.close();
},
close() {
(this.$refs.bg as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
(this.$refs.main as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.main,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [ 0.5, -0.5, 1, 0.5 ],
complete: () => this.destroyDom()
});
},
onBgClick() {
if (!this.modal) {
this.close();
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-dialog
> .bg
display block
position fixed
z-index 8192
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
pointer-events none
> .main
display block
position fixed
z-index 8192
top 20%
left 0
right 0
margin 0 auto 0 auto
padding 32px 42px
width 480px
background #fff
opacity 0
> .body
margin 1em 0
color #888
> .buttons
> button
display inline-block
float right
margin 0
padding 10px 10px
font-size 1.1em
font-weight normal
text-decoration none
color #888
background transparent
outline none
border none
border-radius 0
cursor pointer
transition color 0.1s ease
i
margin 0 0.375em
&:hover
color var(--primary)
&:active
color var(--primaryDarken10)
transition color 0s ease
</style>
<style lang="stylus" module>
.header
margin 1em 0
color var(--primary)
// color #43A4EC
font-weight bold
&:empty
display none
> i
margin-right 0.5em
</style>

View File

@ -170,12 +170,9 @@ export default Vue.extend({
copyUrl() {
copyToClipboard(this.file.url);
this.$dialog({
this.$root.alert({
title: this.$t('contextmenu.copied'),
text: this.$t('contextmenu.copied-url-to-clipboard'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('contextmenu.copied-url-to-clipboard')
});
},

View File

@ -155,12 +155,9 @@ export default Vue.extend({
}).catch(err => {
switch (err) {
case 'detected-circular-definition':
this.$dialog({
this.$root.alert({
title: this.$t('unable-to-process'),
text: this.$t('circular-reference-detected'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('circular-reference-detected')
});
break;
default:

View File

@ -313,12 +313,9 @@ export default Vue.extend({
}).catch(err => {
switch (err) {
case 'detected-circular-definition':
this.$dialog({
this.$root.alert({
title: this.$t('unable-to-process'),
text: this.$t('circular-reference-detected'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('circular-reference-detected')
});
break;
default:
@ -343,12 +340,9 @@ export default Vue.extend({
folderId: this.folder ? this.folder.id : undefined
});
this.$dialog({
this.$root.alert({
title: this.$t('url-upload-requested'),
text: this.$t('may-take-time'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('may-take-time')
});
});
},

View File

@ -1,157 +0,0 @@
<template>
<button class="mk-follow-button"
:class="{ wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou, big: size == 'big' }"
@click="onClick"
:disabled="wait"
>
<template v-if="!wait">
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> {{ $t('request-pending') }}</template></template>
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> {{ $t('follow-processing') }}</template></template>
<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> {{ $t('following') }}</template></template>
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow-request') }}</template></template>
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow') }}</template></template>
</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
</button>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/follow-button.vue'),
props: {
user: {
type: Object,
required: true
},
size: {
type: String,
default: 'compact'
}
},
data() {
return {
u: this.user,
wait: false,
connection: null
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onFollowChange(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
this.$forceUpdate();
}
},
async onClick() {
this.wait = true;
try {
if (this.u.isFollowing) {
this.u = await this.$root.api('following/delete', {
userId: this.u.id
});
} else {
if (this.u.hasPendingFollowRequestFromYou) {
this.u = await this.$root.api('following/requests/cancel', {
userId: this.u.id
});
} else if (this.u.isLocked) {
this.u = await this.$root.api('following/create', {
userId: this.u.id
});
} else {
this.u = await this.$root.api('following/create', {
userId: this.user.id
});
}
}
} catch (e) {
console.error(e);
} finally {
this.wait = false;
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-follow-button
display block
cursor pointer
padding 0
margin 0
width 32px
height 32px
font-size 1em
outline none
border-radius 4px
*
pointer-events none
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid var(--primaryAlpha03)
border-radius 8px
&:not(.active)
color var(--primary)
border solid 1px var(--primary)
&:hover
background var(--primaryAlpha03)
&:active
background var(--primaryAlpha05)
&.active
color var(--primaryForeground)
background var(--primary)
border solid 1px var(--primary)
&:not(:disabled)
font-weight bold
&:hover:not(:disabled)
background var(--primaryLighten5)
border-color var(--primaryLighten5)
&:active:not(:disabled)
background var(--primaryDarken5)
border-color var(--primaryDarken5)
&.wait
cursor wait !important
opacity 0.7
&.big
width 100%
height 38px
line-height 38px
</style>

View File

@ -11,7 +11,7 @@
</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>
<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>

View File

@ -186,12 +186,9 @@ export default Vue.extend({
methods: {
hint() {
this.$dialog({
this.$root.alert({
title: this.$t('@.customization-tips.title'),
text: this.$t('@.customization-tips.paragraph'),
actions: [{
text: this.$t('@.customization-tips.gotit')
}]
text: this.$t('@.customization-tips.paragraph')
});
},

View File

@ -14,10 +14,8 @@ import mediaVideo from './media-video.vue';
import notifications from './notifications.vue';
import noteForm from './post-form.vue';
import renoteForm from './renote-form.vue';
import followButton from './follow-button.vue';
import notePreview from './note-preview.vue';
import noteDetail from './note-detail.vue';
import settings from './settings.vue';
import calendar from './calendar.vue';
import activity from './activity.vue';
import friendsMaker from './friends-maker.vue';
@ -39,10 +37,8 @@ Vue.component('mk-media-video', mediaVideo);
Vue.component('mk-notifications', notifications);
Vue.component('mk-post-form', noteForm);
Vue.component('mk-renote-form', renoteForm);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-note-preview', notePreview);
Vue.component('mk-note-detail', noteDetail);
Vue.component('mk-settings', settings);
Vue.component('mk-calendar', calendar);
Vue.component('mk-activity', activity);
Vue.component('mk-friends-maker', friendsMaker);

View File

@ -8,7 +8,7 @@
:disabled="conversationFetching"
>
<template v-if="!conversationFetching"><fa icon="ellipsis-v"/></template>
<template v-if="conversationFetching"><fa icon="spinner .pulse"/></template>
<template v-if="conversationFetching"><fa icon="spinner" pulse/></template>
</button>
<div class="conversation">
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>

View File

@ -26,7 +26,7 @@
<footer v-if="more">
<button @click="loadMore" :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>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
</footer>
</div>

View File

@ -105,7 +105,7 @@
</component>
</div>
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
<template v-if="fetchingMoreNotifications"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
</button>
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
</div>

View File

@ -41,7 +41,7 @@
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
<button class="kao" :title="$t('insert-a-kao')" @click="kao"><fa :icon="['far', 'smile']"/></button>
<button class="poll" :title="$t('create-poll')" @click="poll = !poll"><fa icon="chart-pie"/></button>
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa icon="eye-slash"/></button>
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa :icon="['far', 'eye-slash']"/></button>
<button class="geo" :title="$t('attach-location-information')" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
<button class="visibility" :title="$t('visibility')" @click="setVisibility" ref="visibilityButton">
<span v-if="visibility === 'public'"><fa icon="globe"/></span>

View File

@ -1,15 +1,21 @@
<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>
<mk-settings :initial-page="initialPage" @done="close"/>
<x-settings :initial-page="initialPage" @done="close"/>
</mk-window>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/settings-window.vue'),
components: {
XSettings: () => import('./settings.vue').then(m => m.default)
},
props: {
initialPage: {
type: String,

View File

@ -15,7 +15,7 @@
</div>
<div class="pages">
<div class="profile" v-show="page == 'profile'">
<mk-profile-editor/>
<x-profile-editor/>
<ui-card>
<div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter') }}</div>
@ -36,7 +36,7 @@
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
<section>
<mk-theme/>
<x-theme/>
</section>
</ui-card>
@ -194,7 +194,7 @@
</ui-card>
<div class="drive" v-if="page == 'drive'">
<mk-drive-settings/>
<x-drive-settings/>
</div>
<ui-card class="hashtags" v-show="page == 'hashtags'">
@ -205,7 +205,7 @@
</ui-card>
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
<mk-mute-and-block/>
<x-mute-and-block/>
</div>
<ui-card class="apps" v-show="page == 'apps'">
@ -218,7 +218,7 @@
<ui-card class="password" v-show="page == 'security'">
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
<section>
<mk-password-settings/>
<x-password-settings/>
</section>
</ui-card>
@ -237,7 +237,7 @@
</ui-card>
<div class="api" v-show="page == 'api'">
<mk-api-settings/>
<x-api-settings/>
</div>
<ui-card class="other" v-show="page == 'other'">
@ -301,7 +301,13 @@ export default Vue.extend({
X2fa,
XApps,
XSignins,
XTags
XTags,
XTheme: () => import('../../../common/views/components/theme.vue').then(m => m.default),
XDriveSettings: () => import('../../../common/views/components/drive-settings.vue').then(m => m.default),
XMuteAndBlock: () => import('../../../common/views/components/mute-and-block.vue').then(m => m.default),
XPasswordSettings: () => import('../../../common/views/components/password-settings.vue').then(m => m.default),
XProfileEditor: () => import('../../../common/views/components/profile-editor.vue').then(m => m.default),
XApiSettings: () => import('../../../common/views/components/api-settings.vue').then(m => m.default),
},
props: {
initialPage: {
@ -543,12 +549,12 @@ export default Vue.extend({
this.checkingForUpdate = false;
this.latestVersion = newer;
if (newer == null) {
this.$dialog({
this.$root.alert({
title: this.$t('no-updates'),
text: this.$t('no-updates-desc')
});
} else {
this.$dialog({
this.$root.alert({
title: this.$t('update-available'),
text: this.$t('update-available-desc')
});
@ -557,7 +563,7 @@ export default Vue.extend({
},
clean() {
localStorage.clear();
this.$dialog({
this.$root.alert({
title: this.$t('cache-cleared'),
text: this.$t('cache-cleared-desc')
});

View File

@ -58,7 +58,7 @@
<i><fa icon="angle-right"/></i>
</p>
</li>
<li v-if="$store.state.i.isAdmin">
<li v-if="$store.state.i.isAdmin || $store.state.i.isModerator">
<a href="/admin">
<i><fa icon="terminal"/></i>
<span>{{ $t('admin') }}</span>

View File

@ -2,7 +2,7 @@
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
<div class="banner" :style="bannerStyle"></div>
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<mk-follow-button :user="user" class="follow"/>
<mk-follow-button :user="user" class="follow" mini/>
<div class="body">
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
<span class="username">@{{ user | acct }}</span>

View File

@ -19,7 +19,7 @@
<p>{{ $t('followers') }}</p><span>{{ u.followersCount }}</span>
</div>
</div>
<mk-follow-button v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u"/>
<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u" mini/>
</template>
</div>
</template>
@ -154,7 +154,7 @@ export default Vue.extend({
font-size 1em
color var(--primary)
> .mk-follow-button
> .follow-button
position absolute
top 92px
right 8px

View File

@ -31,7 +31,7 @@
<footer v-if="more">
<button @click="loadMore" :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>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
</footer>
</div>

View File

@ -17,7 +17,7 @@
</template>
</component>
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
<template v-if="fetchingMoreNotifications"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreNotifications ? this.$t('@.loading') : this.$t('@.load-more') }}
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? this.$t('@.loading') : this.$t('@.load-more') }}
</button>
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
</div>

View File

@ -14,7 +14,7 @@
<header :style="bannerStyle">
<div>
<button class="menu" @click="menu" ref="menu"><fa icon="ellipsis-h"/></button>
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow"/>
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow" mini/>
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<span class="name">{{ user | userName }}</span>
<span class="acct">@{{ user | acct }}</span>
@ -87,7 +87,6 @@ import XNotes from './deck.notes.vue';
import XNote from '../../components/note.vue';
import Menu from '../../../../common/views/components/menu.vue';
import MkUserListsWindow from '../../components/user-lists-window.vue';
import Ok from '../../../../common/views/components/ok.vue';
import { concat } from '../../../../../../prelude/array';
import * as ApexCharts from 'apexcharts';
@ -155,7 +154,8 @@ export default Vue.extend({
this.$root.api('users/notes', {
userId: this.user.id,
fileType: image,
limit: 9
limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => {
notes.forEach(note => {
note.files.forEach(file => {
@ -254,6 +254,7 @@ export default Vue.extend({
this.$root.api('users/notes', {
userId: this.user.id,
limit: fetchLimit + 1,
untilDate: new Date().getTime() + 1000 * 86400 * 365,
withFiles: this.withFiles,
includeMyRenotes: this.$store.state.settings.showMyRenotes,
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
@ -274,7 +275,7 @@ export default Vue.extend({
const promise = this.$root.api('users/notes', {
userId: this.user.id,
limit: fetchLimit + 1,
untilId: (this.$refs.timeline as any).tail().id,
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
withFiles: this.withFiles,
includeMyRenotes: this.$store.state.settings.showMyRenotes,
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
@ -306,7 +307,10 @@ export default Vue.extend({
listId: list.id,
userId: this.user.id
});
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
});
}
}];

View File

@ -1,7 +1,7 @@
<template>
<div class="vahgrswmbzfdlmomxnqftuueyvwaafth">
<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<div v-if="!fetching && users.length > 0">
<router-link v-for="user in users" :to="user | userPage" :key="user.id">
<img :src="user.avatarUrl" :alt="user | userName" v-user-preview="user.id"/>

View File

@ -1,7 +1,7 @@
<template>
<div class="hozptpaliadatkehcmcayizwzwwctpbc">
<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<template v-if="!fetching && users.length != 0">
<div class="user" v-for="friend in users">
<mk-avatar class="avatar" :user="friend"/>
@ -9,7 +9,7 @@
<router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link>
<p class="username">@{{ friend | acct }}</p>
</div>
<mk-follow-button :user="friend"/>
<mk-follow-button class="follow-button" :user="friend"/>
</div>
</template>
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
@ -110,7 +110,7 @@ export default Vue.extend({
color var(--text)
opacity 0.7
> .mk-follow-button
> .follow-button
position absolute
top 16px
right 16px

View File

@ -1,7 +1,7 @@
<template>
<div class="dzsuvbsrrrwobdxifudxuefculdfiaxd">
<p class="title"><fa icon="camera"/>{{ $t('title') }}</p>
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
<div class="stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" class="img"
:style="`background-image: url(${image.thumbnailUrl})`"
@ -27,7 +27,8 @@ export default Vue.extend({
this.$root.api('users/notes', {
userId: this.user.id,
withFiles: true,
limit: 9
limit: 9,
untilDate: new Date().getTime() + 1000 * 86400 * 365
}).then(notes => {
notes.forEach(note => {
note.files.forEach(file => {

View File

@ -1,7 +1,7 @@
<template>
<div class="profile" v-if="$store.getters.isSignedIn">
<div class="friend-form" v-if="$store.state.i.id != user.id">
<mk-follow-button :user="user" size="big"/>
<mk-follow-button :user="user" block/>
<p class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</p>
<p class="stalk" v-if="user.isFollowing">
<span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span>
@ -11,11 +11,11 @@
<div class="action-form">
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
<span v-if="user.isMuted"><fa icon="eye"/> {{ $t('unmute') }}</span>
<span v-else><fa icon="eye-slash"/> {{ $t('mute') }}</span>
<span v-else><fa :icon="['far', 'eye-slash']"/> {{ $t('mute') }}</span>
</ui-button>
<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
<span v-if="user.isBlocking"><fa icon="user"/> {{ $t('unblock') }}</span>
<span v-else><fa icon="user-slash"/> {{ $t('block') }}</span>
<span v-if="user.isBlocking"><fa icon="ban"/> {{ $t('unblock') }}</span>
<span v-else><fa icon="ban"/> {{ $t('block') }}</span>
</ui-button>
<ui-button @click="list"><fa icon="list"/> {{ $t('push-to-a-list') }}</ui-button>
</div>
@ -73,7 +73,13 @@ export default Vue.extend({
},
block() {
if (!window.confirm(this.$t('block-confirm'))) return;
this.$root.alert({
type: 'warning',
text: this.$t('block-confirm'),
showCancelButton: true
}).then(res => {
if (!res) return;
this.$root.api('blocking/create', {
userId: this.user.id
}).then(() => {
@ -81,6 +87,7 @@ export default Vue.extend({
}, () => {
alert('error');
});
});
},
unblock() {
@ -101,7 +108,7 @@ export default Vue.extend({
listId: list.id,
userId: this.user.id
});
this.$dialog({
this.$root.alert({
title: 'Done!',
text: this.$t('list-pushed').replace('{user}', this.user.name).replace('{list}', list.title)
});

View File

@ -3,7 +3,7 @@
<header>
<span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa icon="images"/> {{ $t('with-media') }}</span>
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa :icon="['far', 'images']"/> {{ $t('with-media') }}</span>
</header>
<mk-notes ref="timeline" :more="existMore ? more : null">
<p class="empty" slot="empty"><fa :icon="['far', 'comments']"/>{{ $t('empty') }}</p>
@ -63,7 +63,7 @@ export default Vue.extend({
this.$root.api('users/notes', {
userId: this.user.id,
limit: fetchLimit + 1,
untilDate: this.date ? this.date.getTime() : undefined,
untilDate: this.date ? this.date.getTime() : new Date().getTime() + 1000 * 86400 * 365,
includeReplies: this.mode == 'with-replies',
withFiles: this.mode == 'with-media'
}).then(notes => {
@ -86,7 +86,7 @@ export default Vue.extend({
limit: fetchLimit + 1,
includeReplies: this.mode == 'with-replies',
withFiles: this.mode == 'with-media',
untilId: (this.$refs.timeline as any).tail().id
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
});
promise.then(notes => {

View File

@ -50,7 +50,7 @@
</div>
<div class="photos block">
<header><fa icon="images"/> {{ $t('photos') }}</header>
<header><fa :icon="['far', 'images']"/> {{ $t('photos') }}</header>
<div>
<div v-for="photo in photos" :style="`background-image: url(${photo.thumbnailUrl})`"></div>
</div>

View File

@ -11,7 +11,7 @@
<mk-poll :note="poll"/>
</div>
<p class="empty" v-if="!fetching && poll == null">{{ $t('nothing') }}</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
</div>
</mk-widget-container>
</div>

View File

@ -3,7 +3,10 @@
<mk-widget-container :show-header="props.design == 0">
<template slot="header"><fa icon="pencil-alt"/>{{ $t('title') }}</template>
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body">
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body"
@dragover.stop="onDragover"
@drop.stop="onDrop"
>
<div class="textarea">
<textarea
:disabled="posting"
@ -130,6 +133,33 @@ export default define({
(this.$refs.uploader as any).upload(file);
},
onDragover(e) {
const isFile = e.dataTransfer.items[0].kind == 'file';
const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
if (isFile || isDriveFile) {
e.preventDefault();
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
}
},
onDrop(e): void {
// ファイルだったら
if (e.dataTransfer.files.length > 0) {
e.preventDefault();
Array.from(e.dataTransfer.files).forEach(this.upload);
return;
}
//#region ドライブのファイル
const driveFile = e.dataTransfer.getData('mk_drive_file');
if (driveFile != null && driveFile != '') {
const file = JSON.parse(driveFile);
this.files.push(file);
e.preventDefault();
}
//#endregion
},
async emoji() {
const Picker = await import('../components/emoji-picker-dialog.vue').then(m => m.default);
const button = this.$refs.emoji;

View File

@ -5,7 +5,7 @@
<button slot="func" :title="$t('title')" @click="fetch"><fa icon="sync"/></button>
<div class="mkw-trends--body">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div class="note" v-else-if="note != null">
<p class="text"><router-link :to="note | notePage">{{ note.text }}</router-link></p>
<p class="author"><router-link :to="note.user | userPage">@{{ note.user | acct }}</router-link></p>

View File

@ -5,7 +5,7 @@
<button slot="func" :title="$t('title')" @click="refresh"><fa icon="sync"/></button>
<div class="mkw-users--body">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-else-if="users.length != 0">
<div class="user" v-for="_user in users">
<mk-avatar class="avatar" :user="_user"/>
@ -114,11 +114,6 @@ export default define({
color var(--text)
opacity 0.7
> .mk-follow-button
position absolute
top 16px
right 16px
> .empty
margin 0
padding 16px

View File

@ -7,7 +7,6 @@ import Vuex from 'vuex';
import VueRouter from 'vue-router';
import VAnimateCss from 'v-animate-css';
import VModal from 'vue-js-modal';
import VueSweetalert2 from 'vue-sweetalert2';
import VueI18n from 'vue-i18n';
import VueHotkey from './common/hotkey';
@ -16,6 +15,7 @@ import checkForUpdate from './common/scripts/check-for-update';
import MiOS from './mios';
import { clientVersion as version, codename, lang } from './config';
import { builtinThemes, lightTheme, applyTheme } from './theme';
import Alert from './common/views/components/alert.vue';
if (localStorage.getItem('theme') == null) {
applyTheme(lightTheme);
@ -25,47 +25,126 @@ if (localStorage.getItem('theme') == null) {
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
/* なぜか動かない
import faRetweet from '@fortawesome/free-solid-svg-icons/faRetweet';
import faPlus from '@fortawesome/free-solid-svg-icons/faPlus';
import faUser from '@fortawesome/free-solid-svg-icons/faUser';
import faCog from '@fortawesome/free-solid-svg-icons/faCog';
import faCheck from '@fortawesome/free-solid-svg-icons/faCheck';
import faStar from '@fortawesome/free-solid-svg-icons/faStar';
import faReply from '@fortawesome/free-solid-svg-icons/faReply';
import faEllipsisH from '@fortawesome/free-solid-svg-icons/faEllipsisH';
import faQuoteLeft from '@fortawesome/free-solid-svg-icons/faQuoteLeft';
import faQuoteRight from '@fortawesome/free-solid-svg-icons/faQuoteRight';
import faAngleUp from '@fortawesome/free-solid-svg-icons/faAngleUp';
import faAngleDown from '@fortawesome/free-solid-svg-icons/faAngleDown';
import faAt from '@fortawesome/free-solid-svg-icons/faAt';
import faHashtag from '@fortawesome/free-solid-svg-icons/faHashtag';
import faHome from '@fortawesome/free-solid-svg-icons/faHome';
import faGlobe from '@fortawesome/free-solid-svg-icons/faGlobe';
import faCircle from '@fortawesome/free-solid-svg-icons/faCircle';
import faList from '@fortawesome/free-solid-svg-icons/faList';
import faHeart from '@fortawesome/free-solid-svg-icons/faHeart';
import faUnlock from '@fortawesome/free-solid-svg-icons/faUnlock';
import faRssSquare from '@fortawesome/free-solid-svg-icons/faRssSquare';
import faSort from '@fortawesome/free-solid-svg-icons/faSort';
import faChartPie from '@fortawesome/free-solid-svg-icons/faChartPie';
import faChartBar from '@fortawesome/free-solid-svg-icons/faChartBar';
import faPencilAlt from '@fortawesome/free-solid-svg-icons/faPencilAlt';
import faColumns from '@fortawesome/free-solid-svg-icons/faColumns';
import faComments from '@fortawesome/free-solid-svg-icons/faComments';
import faGamepad from '@fortawesome/free-solid-svg-icons/faGamepad';
import faCloud from '@fortawesome/free-solid-svg-icons/faCloud';
import faPowerOff from '@fortawesome/free-solid-svg-icons/faPowerOff';
import faChevronCircleLeft from '@fortawesome/free-solid-svg-icons/faChevronCircleLeft';
import faChevronCircleRight from '@fortawesome/free-solid-svg-icons/faChevronCircleRight';
import faShareAlt from '@fortawesome/free-solid-svg-icons/faShareAlt';
import faTimes from '@fortawesome/free-solid-svg-icons/faTimes';
import faThumbtack from '@fortawesome/free-solid-svg-icons/faThumbtack';
import faSearch from '@fortawesome/free-solid-svg-icons/faSearch';
import {
faRetweet,
faPlus,
faUser,
faCog,
faCheck,
faStar,
faReply,
faEllipsisH,
faQuoteLeft,
faQuoteRight,
faAngleUp,
faAngleDown,
faAt,
faHashtag,
faHome,
faGlobe,
faCircle,
faList,
faHeart,
faUnlock,
faRssSquare,
faSort,
faChartPie,
faChartBar,
faPencilAlt,
faColumns,
faComments,
faGamepad,
faCloud,
faPowerOff,
faChevronCircleLeft,
faChevronCircleRight,
faShareAlt,
faTimes,
faThumbtack,
faSearch,
faAngleRight,
faWrench,
faTerminal,
faMoon,
faPalette,
faSlidersH,
faDesktop,
faVolumeUp,
faLanguage,
faInfoCircle,
faExclamationTriangle,
faKey,
faBan,
faCogs,
faUnlockAlt,
faPuzzlePiece,
faMobileAlt,
faSignInAlt,
faSyncAlt,
faPaperPlane,
faUpload,
faMapMarkerAlt,
faEnvelope,
faLock,
faFolderOpen,
faBirthdayCake,
faImage,
faEye,
faDownload,
faFileImport,
faLink,
faArrowRight,
faICursor,
faCaretRight,
faReplyAll,
faCamera,
faMinus,
faCaretDown,
faCalculator,
faUsers,
faBars,
faFileImage,
faPollH,
faFolder,
faMicrochip,
faMemory,
faServer,
faExclamationCircle,
faSpinner,
faBroadcastTower,
faChartLine,
faEllipsisV,
faStickyNote,
faUserPlus,
faExternalLinkSquareAlt,
faSync,
} from '@fortawesome/free-solid-svg-icons';
import farBell from '@fortawesome/free-regular-svg-icons/faBell';
import farEnvelope from '@fortawesome/free-regular-svg-icons/faEnvelope';
import farComments from '@fortawesome/free-regular-svg-icons/faComments';
import {
faBell as farBell,
faEnvelope as farEnvelope,
faComments as farComments,
faTrashAlt as farTrashAlt,
faWindowRestore as farWindowRestore,
faFolder as farFolder,
faLaugh as farLaugh,
faSmile as farSmile,
faEyeSlash as farEyeSlash,
faFolderOpen as farFolderOpen,
faSave as farSave,
faImages as farImages,
faChartBar as farChartBar,
faCommentAlt as farCommentAlt,
faClock as farClock,
faCalendarAlt as farCalendarAlt,
faHdd as farHdd,
} from '@fortawesome/free-regular-svg-icons';
import {
faTwitter as fabTwitter,
faGithub as fabGithub,
} from '@fortawesome/free-brands-svg-icons';
import i18n from './i18n';
library.add(
faRetweet,
@ -104,16 +183,84 @@ library.add(
faTimes,
faThumbtack,
faSearch,
faAngleRight,
faWrench,
faTerminal,
faMoon,
faPalette,
faSlidersH,
faDesktop,
faVolumeUp,
faLanguage,
faInfoCircle,
faExclamationTriangle,
faKey,
faBan,
faCogs,
faUnlockAlt,
faPuzzlePiece,
faMobileAlt,
faSignInAlt,
faSyncAlt,
faPaperPlane,
faUpload,
faMapMarkerAlt,
faEnvelope,
faLock,
faFolderOpen,
faBirthdayCake,
faImage,
faEye,
faDownload,
faFileImport,
faLink,
faArrowRight,
faICursor,
faCaretRight,
faReplyAll,
faCamera,
faMinus,
faCaretDown,
faCalculator,
faUsers,
faBars,
faFileImage,
faPollH,
faFolder,
faMicrochip,
faMemory,
faServer,
faExclamationCircle,
faSpinner,
faBroadcastTower,
faChartLine,
faEllipsisV,
faStickyNote,
faUserPlus,
faExternalLinkSquareAlt,
faSync,
farBell,
farEnvelope,
farComments,
farTrashAlt,
farWindowRestore,
farFolder,
farLaugh,
farSmile,
farEyeSlash,
farFolderOpen,
farSave,
farImages,
farChartBar,
farCommentAlt,
farClock,
farCalendarAlt,
farHdd,
fabTwitter,
fabGithub
);
*/
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
library.add(fas, far);
//#endregion
Vue.use(Vuex);
@ -121,7 +268,6 @@ Vue.use(VueRouter);
Vue.use(VAnimateCss);
Vue.use(VModal);
Vue.use(VueHotkey);
Vue.use(VueSweetalert2);
Vue.use(VueI18n);
Vue.component('fa', FontAwesomeIcon);
@ -269,13 +415,7 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS]) => void,
}, { passive: true });
const app = new Vue({
i18n: new VueI18n({
sync: false,
locale: lang,
messages: {
[lang]: {}
}
}),
i18n: i18n(),
store: os.store,
data() {
return {
@ -299,6 +439,13 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS]) => void,
document.body.appendChild(x.$el);
return x;
},
alert(opts) {
return new Promise((res) => {
const vm = this.new(Alert, opts);
vm.$once('ok', () => res(true));
vm.$once('cancel', () => res(false));
});
}
},
router,
render: createEl => createEl(App)

View File

@ -172,7 +172,7 @@ export default class MiOS extends EventEmitter {
callback();
// Init service worker
if (this.shouldRegisterSw) this.registerSw();
//if (this.shouldRegisterSw) this.registerSw();
};
// キャッシュがあったとき
@ -365,7 +365,7 @@ export default class MiOS extends EventEmitter {
});
// The path of service worker script
const sw = `/sw.${version}.${lang}.js`;
const sw = `/sw.${version}.js`;
// Register service worker
navigator.serviceWorker.register(sw).then(registration => {

View File

@ -27,7 +27,6 @@ import MkFollowing from './views/pages/following.vue';
import MkFavorites from './views/pages/favorites.vue';
import MkUserLists from './views/pages/user-lists.vue';
import MkUserList from './views/pages/user-list.vue';
import MkSettings from './views/pages/settings.vue';
import MkReversi from './views/pages/games/reversi.vue';
import MkTag from './views/pages/tag.vue';
import MkShare from './views/pages/share.vue';
@ -36,7 +35,6 @@ import MkFollow from '../common/views/pages/follow.vue';
import PostForm from './views/components/post-form-dialog.vue';
import FileChooser from './views/components/drive-file-chooser.vue';
import FolderChooser from './views/components/drive-folder-chooser.vue';
import Dialog from './views/components/dialog.vue';
/**
* init
@ -100,21 +98,6 @@ init((launch) => {
});
},
$dialog(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
});
},
$notify(message) {
alert(message);
}
@ -137,7 +120,7 @@ init((launch) => {
routes: [
{ path: '/', name: 'index', component: MkIndex },
{ path: '/signup', name: 'signup', component: MkSignup },
{ path: '/i/settings', name: 'settings', component: MkSettings },
{ path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) },
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
{ path: '/i/favorites', name: 'favorites', component: MkFavorites },
{ path: '/i/lists', name: 'user-lists', component: MkUserLists },
@ -154,7 +137,7 @@ init((launch) => {
{ path: '/tags/:tag', component: MkTag },
{ path: '/share', component: MkShare },
{ path: '/reversi/:game?', name: 'reversi', component: MkReversi },
{ path: '/@:user', component: MkUser },
{ path: '/@:user', component: () => import('./views/pages/user.vue').then(m => m.default) },
{ path: '/@:user/followers', component: MkFollowers },
{ path: '/@:user/following', component: MkFollowing },
{ path: '/notes/:note', component: MkNote },

View File

@ -1,167 +0,0 @@
<template>
<div class="mk-dialog">
<div class="bg" ref="bg" @click="onBgClick"></div>
<div class="main" ref="main">
<header v-html="title" :class="$style.header"></header>
<div class="body" v-html="text"></div>
<div class="buttons">
<button v-for="button in buttons" @click="click(button)">{{ button.text }}</button>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
export default Vue.extend({
props: {
title: {
type: String,
required: false
},
text: {
type: String,
required: true
},
buttons: {
type: Array,
default: () => {
return [{
text: 'OK'
}];
}
},
modal: {
type: Boolean,
default: false
}
},
mounted() {
this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto';
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 100,
easing: 'linear'
});
anime({
targets: this.$refs.main,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
});
},
methods: {
click(button) {
this.$emit('clicked', button.id);
this.close();
},
close() {
(this.$refs.bg as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
(this.$refs.main as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.main,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [ 0.5, -0.5, 1, 0.5 ],
complete: () => this.destroyDom()
});
},
onBgClick() {
if (!this.modal) {
this.close();
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-dialog
> .bg
display block
position fixed
z-index 8192
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
pointer-events none
> .main
display block
position fixed
z-index 8192
top 20%
left 0
right 0
margin 0 auto 0 auto
padding 16px
width calc(100% - 32px)
max-width 300px
background #fff
opacity 0
> .body
margin 1em 0
color #888
> .buttons
> button
display inline-block
float right
margin 0
padding 0 10px
font-size 1.1em
font-weight normal
text-decoration none
color #888
background transparent
outline none
border none
border-radius 0
cursor pointer
transition color 0.1s ease
i
margin 0 0.375em
&:hover
color var(--primary)
&:active
color var(--primaryDarken10)
transition color 0s ease
</style>
<style lang="stylus" module>
.header
margin 0 0 1em 0
color var(--primary)
// color #43A4EC
font-weight bold
&:empty
display none
> i
margin-right 0.5em
</style>

View File

@ -31,7 +31,7 @@
<span class="created-at" @click="showCreatedAt"><fa :icon="['far', 'clock']"/><mk-time :time="file.createdAt"/></span>
<template v-if="file.isSensitive">
<span class="separator"></span>
<span class="nsfw"><fa icon="eye-slash"/> {{ $t('nsfw') }}</span>
<span class="nsfw"><fa :icon="['far', 'eye-slash']"/> {{ $t('nsfw') }}</span>
</template>
</div>
</div>

View File

@ -15,7 +15,7 @@
<span class="created-at"><fa :icon="['far', 'clock']"/><mk-time :time="file.createdAt"/></span>
<template v-if="file.isSensitive">
<span class="separator"></span>
<span class="nsfw"><fa icon="eye-slash"/> {{ $t('nsfw') }}</span>
<span class="nsfw"><fa :icon="['far', 'eye-slash']"/> {{ $t('nsfw') }}</span>
</template>
</footer>
</div>

View File

@ -51,8 +51,6 @@ export default Vue.extend({
top 0
bottom 0
right 20px
> *
height 100%
</style>

View File

@ -1,134 +0,0 @@
<template>
<button class="mk-follow-button"
:class="{ wait: wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou }"
@click="onClick"
:disabled="wait"
>
<template v-if="!wait">
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/> {{ $t('request-pending') }}</template>
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/> {{ $t('follow-processing') }}</template>
<template v-else-if="u.isFollowing"><fa icon="minus"/> {{ $t('following') }}</template>
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
</button>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('mobile/views/components/follow-button.vue'),
props: {
user: {
type: Object,
required: true
}
},
data() {
return {
u: this.user,
wait: false,
connection: null
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onFollowChange(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
this.$forceUpdate();
}
},
async onClick() {
this.wait = true;
try {
if (this.u.isFollowing) {
this.u = await this.$root.api('following/delete', {
userId: this.u.id
});
} else {
if (this.u.hasPendingFollowRequestFromYou) {
this.u = await this.$root.api('following/requests/cancel', {
userId: this.u.id
});
} else if (this.u.isLocked) {
this.u = await this.$root.api('following/create', {
userId: this.u.id
});
} else {
this.u = await this.$root.api('following/create', {
userId: this.user.id
});
}
}
} catch (e) {
console.error(e);
} finally {
this.wait = false;
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-follow-button
display block
user-select none
cursor pointer
padding 0 16px
margin 0
min-width 100px
line-height 36px
font-size 14px
font-weight bold
color var(--primary)
background transparent
outline none
border solid 1px var(--primary)
border-radius 36px
&:hover
background var(--primaryAlpha01)
&:active
background var(--primaryAlpha02)
&.active
color var(--primaryForeground)
background var(--primary)
&:hover
background var(--primaryLighten10)
border-color var(--primaryLighten10)
&:active
background var(--primaryDarken10)
border-color var(--primaryDarken10)
&.wait
cursor wait !important
opacity 0.7
*
pointer-events none
</style>

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