Compare commits

..

35 Commits

Author SHA1 Message Date
syuilo
4c7dd7228f 10.49.3 2018-11-14 16:39:27 +09:00
syuilo
46a51addad ServiceWorkerはメンテナンスされていないのでとりあえず無効化 2018-11-14 16:39:04 +09:00
syuilo
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
Acid Chicken (硫酸鶏)
00bb403497 Fix #3228 (#3230)
* Update index.ts

* fix sw.js path

* Revert "Update index.ts"

This reverts commit e0b1d4239f.
2018-11-14 16:35:00 +09:00
syuilo
11afa8140c [Client] Improve alert component 2018-11-14 16:30:58 +09:00
syuilo
850396e9da [Client] Add missing icons 2018-11-14 14:57:59 +09:00
syuilo
5ee75be49e 🎨 2018-11-14 14:47:18 +09:00
dependabot[bot]
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
syuilo
e509b1f488 Update github-bot.ts 2018-11-14 05:33:48 +09:00
syuilo
468ff7037f 10.49.2 2018-11-14 03:07:55 +09:00
syuilo
df23504ccf [Client] Fix #3227 2018-11-14 03:05:13 +09:00
syuilo
66e3cb8eda Update src/client/app/init.ts 2018-11-14 02:04:24 +09:00
syuilo
6ddd2389dc [Client] Add missing icons 2018-11-14 01:45:15 +09:00
syuilo
402efb8c50 [Client] Fix imports 2018-11-14 01:40:29 +09:00
syuilo
7b6eae0ce4 Fix error 2018-11-14 01:32:38 +09:00
syuilo
26ce9725ce [Client] Fix #3009 2018-11-14 01:31:36 +09:00
ButterflyOfFire
ebfaa18f12 Create setup.fr.md (#3225) 2018-11-14 00:15:33 +09:00
syuilo
cc81d41a05 [Client] Some optimizations 2018-11-13 23:33:30 +09:00
syuilo
212176ee5c Use terser instead of uglifyjs 2018-11-13 23:10:51 +09:00
syuilo
a63ec05e41 [Client] Some optimizations 2018-11-13 23:06:31 +09:00
syuilo
0dcb527bf3 [Client] Fix bug 2018-11-13 22:45:28 +09:00
syuilo
54710f17fc [Client] Some performance optimizations 2018-11-13 22:43:09 +09:00
syuilo
e58a6593c0 [Client] Fix bug 2018-11-13 22:42:31 +09:00
syuilo
62132570e1 [Client] Split components to reduce bundle size 2018-11-13 20:21:52 +09:00
MeiMei
9f0b8ba2f8 Fix: notes/create hangs when rejected (#3221) 2018-11-13 19:34:09 +09:00
syuilo
adbe0fbcd1 10.49.1 2018-11-13 16:33:21 +09:00
syuilo
7896242f57 [Client] Fix #3213 2018-11-13 16:19:46 +09:00
syuilo
4a6722b9e9 Merge branch 'master' into develop 2018-11-13 15:01:52 +09:00
syuilo
7c9fb5228b Improve usability 2018-11-13 15:01:05 +09:00
MeiMei
81805b01cc sort user timeline by date (#3210) 2018-11-13 01:17:59 +09:00
syuilo
50824a7245 10.49.0 2018-11-13 01:12:27 +09:00
syuilo
6f2953f3a7 [Client] Clear cached locale data when shouldFlush is true 2018-11-13 01:11:36 +09:00
syuilo
dd3f007582 [Client] Improve post-form widget 2018-11-13 01:04:15 +09:00
syuilo
a4b2b093fc 🎨 2018-11-13 00:21:49 +09:00
syuilo
0fbf56219f [Client] Emoji picker
Closes #3130
2018-11-13 00:12:55 +09:00
125 changed files with 1877 additions and 1527 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

@@ -379,6 +379,17 @@ common/views/components/poll-editor.vue:
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: "パスワード"
@@ -657,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: "{} のフォロワー"
@@ -1325,7 +1329,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.48.1",
"clientVersion": "2.0.11733",
"version": "10.49.3",
"clientVersion": "2.0.11770",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@@ -198,6 +198,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",
@@ -220,7 +221,6 @@
"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

@@ -48,15 +48,15 @@ export default Vue.extend({
},
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

@@ -21,7 +21,7 @@
<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('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,7 +29,7 @@
<!-- <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>
@@ -58,6 +58,8 @@ import XEmoji from "./emoji.vue";
import XAnnouncements from "./announcements.vue";
import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue";
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { faGrin } from '@fortawesome/free-regular-svg-icons';
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
@@ -81,7 +83,9 @@ export default Vue.extend({
page: 'dashboard',
version,
isMobile,
navOpend: !isMobile
navOpend: !isMobile,
faGrin,
faArrowLeft
};
},
methods: {

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

@@ -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

@@ -146,6 +146,8 @@
function refresh() {
localStorage.setItem('shouldFlush', 'false');
localStorage.removeItem('locale');
// Random
localStorage.setItem('salt', Math.random().toString().substr(2, 8));

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,11 @@ export default (opts: Opts = {}) => ({
this.$root.api('notes/favorites/create', {
noteId: this.appearNote.id
}).then(() => {
this.$root.new(Ok);
// TODO
/*this.$root.alert({
pointer: false,
autoClose: true
});*/
});
},

View File

@@ -0,0 +1,179 @@
<template>
<div class="felqjxyj" :class="{ pointer }">
<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">
<ui-button @click="ok" primary>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: true
},
showCancelButton: {
type: Boolean,
default: false
},
pointer: {
type: Boolean,
default: true
}
},
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]
});
});
},
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.5, -0.5, 1, 0.5 ],
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%
&:not(.pointer)
pointer-events none
> .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 42px
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
font-weight bold
> .body
margin 16px 0
</style>

View File

@@ -0,0 +1,202 @@
<template>
<div class="prlncendiewqqkrevzeruhndoakghvtx">
<header>
<button v-for="category in categories"
:title="category.text"
@click="go(category.ref)"
:class="{ active: category.isActive }"
>
<fa :icon="category.icon" fixed-width/>
</button>
</header>
<div class="emojis" ref="emojis" @scroll.passive="onScroll">
<section v-for="category in categories" :ref="category.ref">
<header><fa :icon="category.icon" fixed-width/> {{ category.text }}</header>
<div v-if="category.name">
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === category.name)"
:title="emoji[0]"
@click="chosen(emoji[1].char)"
>
<mk-emoji :emoji="emoji[1].char"/>
</button>
</div>
<div v-else>
<button v-for="emoji in customEmojis"
:title="emoji.name"
@click="chosen(`:${emoji.name}:`)"
>
<img :src="emoji.url" :alt="emoji.name"/>
</button>
</div>
</section>
</div>
</div>
</template>
<script lang="ts">
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'),
data() {
return {
lib,
customEmojis: [],
categories: [{
ref: 'customEmojiSection',
text: this.$t('custom-emoji'),
icon: faAsterisk,
isActive: true
}, {
name: 'people',
ref: 'peopleSection',
text: this.$t('people'),
icon: ['far', 'laugh'],
isActive: false
}, {
name: 'animals_and_nature',
ref: 'animalsAndNatureSection',
text: this.$t('animals-and-nature'),
icon: faLeaf,
isActive: false
}, {
name: 'food_and_drink',
ref: 'foodAndDrinkSection',
text: this.$t('food-and-drink'),
icon: faUtensils,
isActive: false
}, {
name: 'activity',
ref: 'activitySection',
text: this.$t('activity'),
icon: faFutbol,
isActive: false
}, {
name: 'travel_and_places',
ref: 'travelAndPlacesSection',
text: this.$t('travel-and-places'),
icon: faCity,
isActive: false
}, {
name: 'objects',
ref: 'objectsSection',
text: this.$t('objects'),
icon: faDice,
isActive: false
}, {
name: 'symbols',
ref: 'symbolsSection',
text: this.$t('symbols'),
icon: faHeart,
isActive: false
}, {
name: 'flags',
ref: 'flagsSection',
text: this.$t('flags'),
icon: faFlag,
isActive: false
}]
}
},
created() {
this.customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
},
methods: {
go(ref) {
this.$refs.emojis.scrollTop = this.$refs[ref][0].offsetTop;
},
onScroll(e) {
const section = this.categories.forEach(x => {
const top = e.target.scrollTop;
const el = this.$refs[x.ref][0];
x.isActive = el.offsetTop <= top && el.offsetTop + el.offsetHeight > top;
});
},
chosen(emoji) {
this.$emit('chosen', emoji);
}
}
});
</script>
<style lang="stylus" scoped>
.prlncendiewqqkrevzeruhndoakghvtx
width 350px
background var(--face)
> header
display flex
> button
flex 1
padding 10px 0
font-size 16px
color var(--text)
transition color 0.2s ease
&:hover
color var(--textHighlighted)
transition color 0s
&.active
color var(--primary)
transition color 0s
> .emojis
height 300px
overflow-y auto
overflow-x hidden
> section
> header
position sticky
top 0
left 0
z-index 1
padding 8px
background var(--faceHeader)
color var(--text)
font-size 12px
> div
display grid
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
gap 4px
padding 8px
> button
padding 0
width 100%
&:before
content ''
display block
width 1px
height 0
padding-bottom 100%
&:hover
> *
transform scale(1.2)
transition transform 0s
> *
position absolute
top 0
left 0
width 100%
height 100%
font-size 28px
transition transform 0.2s ease
pointer-events none
</style>

View File

@@ -22,7 +22,7 @@ export default Vue.extend({
},
customEmojis: {
required: false,
default: []
default: () => []
}
},

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"/>

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({
@@ -79,7 +78,8 @@ export default Vue.extend({
this.$root.api('i/pin', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
// TODO
//this.$root.new(Ok);
this.destroyDom();
});
},
@@ -105,7 +105,8 @@ export default Vue.extend({
this.$root.api('notes/favorites/create', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
// TODO
//this.$root.new(Ok);
this.destroyDom();
});
},
@@ -114,7 +115,8 @@ export default Vue.extend({
this.$root.api('notes/favorites/delete', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
// TODO
//this.$root.new(Ok);
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')
});

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

@@ -221,7 +221,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 +234,7 @@ export default Vue.extend({
}
if (theme.id == null) {
this.$swal({
this.$root.alert({
type: 'error',
text: this.$t('invalid-theme')
});
@@ -242,7 +242,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 +254,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 +267,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 +304,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 +318,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

@@ -57,6 +57,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 +86,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
> *
flex 1
&:not(.noGrow)
display flex
&:not(:last-child)
margin-right 16px
> *
flex 1
> *: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>

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

@@ -0,0 +1,84 @@
<template>
<div class="gcafiosrssbtbnbzqupfmglvzgiaipyv">
<x-picker @chosen="chosen"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import contains from '../../../common/scripts/contains';
export default Vue.extend({
components: {
XPicker: () => import('../../../common/views/components/emoji-picker.vue').then(m => m.default)
},
props: {
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
}
},
mounted() {
this.$nextTick(() => {
const width = this.$el.offsetWidth;
const height = this.$el.offsetHeight;
let x = this.x;
let y = this.y;
if (x + width - window.pageXOffset > window.innerWidth) {
x = window.innerWidth - width + window.pageXOffset;
}
if (y + height - window.pageYOffset > window.innerHeight) {
y = window.innerHeight - height + window.pageYOffset;
}
this.$el.style.left = x + 'px';
this.$el.style.top = y + 'px';
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.addEventListener('mousedown', this.onMousedown);
});
});
},
methods: {
onMousedown(e) {
e.preventDefault();
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
return false;
},
chosen(emoji) {
this.$emit('chosen', emoji);
this.close();
},
close() {
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.removeEventListener('mousedown', this.onMousedown);
});
this.$emit('closed');
this.destroyDom();
}
}
});
</script>
<style lang="stylus" scoped>
.gcafiosrssbtbnbzqupfmglvzgiaipyv
position fixed
top 0
left 0
z-index 3000
box-shadow 0 2px 12px 0 rgba(0, 0, 0, 0.3)
</style>

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

@@ -15,28 +15,33 @@
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
</div>
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
<textarea :class="{ with: (files.length != 0 || poll) }"
ref="text" v-model="text" :disabled="posting"
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
v-autocomplete="'text'"
></textarea>
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
<p class="remain">{{ 4 - files.length }}/4</p>
<div class="textarea">
<textarea :class="{ with: (files.length != 0 || poll) }"
ref="text" v-model="text" :disabled="posting"
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
v-autocomplete="'text'"
></textarea>
<button class="emoji" @click="emoji" ref="emoji">
<fa :icon="['far', 'laugh']"/>
</button>
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
<p class="remain">{{ 4 - files.length }}/4</p>
</div>
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
</div>
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
</div>
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
<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>
@@ -377,6 +382,19 @@ export default Vue.extend({
this.visibleUsers = erase(user, this.visibleUsers);
},
async emoji() {
const Picker = await import('./emoji-picker-dialog.vue').then(m => m.default);
const button = this.$refs.emoji;
const rect = button.getBoundingClientRect();
const vm = this.$root.new(Picker, {
x: button.offsetWidth + rect.left + window.pageXOffset,
y: rect.top + window.pageYOffset
});
vm.$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji);
});
},
post() {
this.posting = true;
@@ -469,7 +487,7 @@ export default Vue.extend({
> .content
> input
> textarea
> .textarea > textarea
display block
width 100%
padding 12px
@@ -498,27 +516,108 @@ export default Vue.extend({
> input
margin-bottom 8px
> textarea
margin 0
max-width 100%
min-width 100%
min-height 84px
> .textarea
> .emoji
position absolute
top 0
right 0
padding 10px
font-size 18px
color var(--text)
opacity 0.5
&:hover
& + *
& + * + *
border-color var(--primaryAlpha02)
transition border-color .1s ease
&:hover
color var(--textHighlighted)
opacity 1
&:focus
& + *
& + * + *
border-color var(--primaryAlpha05)
transition border-color 0s ease
&:active
color var(--primary)
opacity 1
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 4px 4px 0 0
> textarea
margin 0
max-width 100%
min-width 100%
min-height 84px
&:hover
& + * + *
& + * + * + *
border-color var(--primaryAlpha02)
transition border-color .1s ease
&:focus
& + * + *
& + * + * + *
border-color var(--primaryAlpha05)
transition border-color 0s ease
& + .emoji
opacity 0.7
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 4px 4px 0 0
> .files
margin 0
padding 0
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 0
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color var(--primaryAlpha04)
> div
padding 4px
&:after
content ""
display block
clear both
> div
float left
border solid 4px transparent
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .mk-poll-editor
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
> .visibleUsers
margin-bottom 8px
@@ -541,66 +640,6 @@ export default Vue.extend({
margin-right 8px
white-space nowrap
> .files
margin 0
padding 0
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 0
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color var(--primaryAlpha04)
> div
padding 4px
&:after
content ""
display block
clear both
> div
float left
border solid 4px transparent
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .mk-poll-editor
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
> .mk-uploader
margin 8px 0 0 0
padding 8px

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

@@ -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,8 @@ export default Vue.extend({
listId: list.id,
userId: this.user.id
});
this.$root.new(Ok);
// TODO
//this.$root.new(Ok);
});
}
}];

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

@@ -1,16 +1,51 @@
<template>
<div class="mkw-post-form">
<template v-if="props.design == 0">
<p class="title"><fa icon="pencil-alt"/>{{ $t('title') }}</p>
</template>
<textarea :disabled="posting" v-model="text" @keydown="onKeydown" :placeholder="placeholder"></textarea>
<button @click="post" :disabled="posting">{{ $t('note') }}</button>
<div>
<mk-widget-container :show-header="props.design == 0">
<template slot="header"><fa icon="pencil-alt"/>{{ $t('title') }}</template>
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body"
@dragover.stop="onDragover"
@drop.stop="onDrop"
>
<div class="textarea">
<textarea
:disabled="posting"
v-model="text"
@keydown="onKeydown"
@paste="onPaste"
:placeholder="placeholder"
ref="text"
v-autocomplete="'text'"
></textarea>
<button class="emoji" @click="emoji" ref="emoji">
<fa :icon="['far', 'laugh']"/>
</button>
</div>
<div class="files" v-show="files.length != 0">
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
</div>
<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
<mk-uploader ref="uploader" @uploaded="attachMedia"/>
<footer>
<button @click="chooseFile"><fa icon="upload"/></button>
<button @click="chooseFileFromDrive"><fa icon="cloud"/></button>
<button @click="post" :disabled="posting" class="post">{{ $t('note') }}</button>
</footer>
</div>
</mk-widget-container>
</div>
</template>
<script lang="ts">
import define from '../../../common/define-widget';
import i18n from '../../../i18n';
import insertTextAtCursor from 'insert-text-at-cursor';
import * as XDraggable from 'vuedraggable';
export default define({
name: 'post-form',
@@ -19,12 +54,19 @@ export default define({
})
}).extend({
i18n: i18n('desktop/views/widgets/post-form.vue'),
components: {
XDraggable
},
data() {
return {
posting: false,
text: ''
text: '',
files: [],
};
},
computed: {
placeholder(): string {
const xs = [
@@ -38,6 +80,7 @@ export default define({
return xs[Math.floor(Math.random() * xs.length)];
}
},
methods: {
func() {
if (this.props.design == 1) {
@@ -47,14 +90,95 @@ export default define({
}
this.save();
},
chooseFile() {
(this.$refs.file as any).click();
},
chooseFileFromDrive() {
this.$chooseDriveFile({
multiple: true
}).then(files => {
files.forEach(this.attachMedia);
});
},
attachMedia(driveFile) {
this.files.push(driveFile);
this.$emit('change-attached-files', this.files);
},
detachMedia(id) {
this.files = this.files.filter(x => x.id != id);
this.$emit('change-attached-files', this.files);
},
onKeydown(e) {
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && !this.posting && this.text) this.post();
},
onPaste(e) {
Array.from(e.clipboardData.items).forEach((item: any) => {
if (item.kind == 'file') {
this.upload(item.getAsFile());
}
});
},
onChangeFile() {
Array.from((this.$refs.file as any).files).forEach(this.upload);
},
upload(file) {
(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;
const rect = button.getBoundingClientRect();
const vm = this.$root.new(Picker, {
x: button.offsetWidth + rect.left + window.pageXOffset,
y: rect.top + window.pageYOffset
});
vm.$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji);
});
},
post() {
this.posting = true;
this.$root.api('notes/create', {
text: this.text
text: this.text == '' ? undefined : this.text,
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
}).then(data => {
this.clear();
}).catch(err => {
@@ -63,66 +187,114 @@ export default define({
this.posting = false;
});
},
clear() {
this.text = '';
this.files = [];
}
}
});
</script>
<style lang="stylus" scoped>
.lhcuptdmcdkfwmipgazeawoiuxpzaclc-body
> .textarea
> .emoji
position absolute
top 0
right 0
padding 10px
font-size 18px
color var(--text)
opacity 0.5
&:hover
color var(--textHighlighted)
opacity 1
.mkw-post-form
background #fff
overflow hidden
border solid 1px rgba(#000, 0.075)
border-radius 6px
&:active
color var(--primary)
opacity 1
> .title
z-index 1
margin 0
padding 0 16px
line-height 42px
font-size 0.9em
font-weight bold
color #888
box-shadow 0 1px rgba(#000, 0.07)
> textarea
display block
width 100%
max-width 100%
min-width 100%
padding 16px
color var(--desktopPostFormTextareaFg)
outline none
background var(--desktopPostFormTextareaBg)
border none
border-bottom solid 1px var(--faceDivider)
> [data-icon]
margin-right 4px
&:focus
& + .emoji
opacity 0.7
> textarea
display block
width 100%
max-width 100%
min-width 100%
padding 16px
margin-bottom 28px + 16px
border none
border-bottom solid 1px #eee
> .files
> div
padding 4px
> button
display block
position absolute
bottom 8px
right 8px
margin 0
padding 0 10px
height 28px
color var(--primaryForeground)
background var(--primary) !important
outline none
border none
border-radius 4px
transition background 0.1s ease
cursor pointer
&:after
content ""
display block
clear both
&:hover
background var(--primaryLighten10) !important
> div
float left
border solid 4px transparent
cursor move
&:active
background var(--primaryDarken10) !important
transition background 0s ease
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> input[type=file]
display none
> footer
display flex
padding 8px
> button:not(.post)
color var(--text)
&:hover
color var(--textHighlighted)
> .post
display block
margin 0 0 0 auto
padding 0 10px
height 28px
color var(--primaryForeground)
background var(--primary) !important
outline none
border none
border-radius 4px
transition background 0.1s ease
cursor pointer
&:hover
background var(--primaryLighten10) !important
&:active
background var(--primaryDarken10) !important
transition background 0s ease
</style>

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,121 @@ 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,
} 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 +178,79 @@ 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,
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 +258,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 +405,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 +429,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%
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>

View File

@@ -5,7 +5,7 @@
<mk-user-card v-for="user in users" :key="user.id" :user="user"/>
</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="close" :title="$t('title')"><fa icon="times"/></button>
</div>

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