Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
cb0673b1ec | |||
cd018db945 | |||
50fe67b99b | |||
1dba82aae5 | |||
17c6d64750 | |||
b4c04efa23 | |||
152dd74abf | |||
0985f7f609 | |||
56d571c0f0 | |||
dc9a19b9c7 | |||
88a2c7715a | |||
2fa8cb1b73 | |||
5f8a66fdb9 | |||
57320a94f9 | |||
89f045d624 | |||
1a77dea7ed | |||
d063d59a91 | |||
90429b787c | |||
7a2ef04ec3 | |||
76a9ea8d3d | |||
0a05a2d060 | |||
a7e2ee3b0c | |||
40efa90dd5 | |||
4ca0a22bfc | |||
20a943b193 | |||
552df8737d | |||
860f622d79 | |||
e76bf5707a | |||
bf37a72f59 | |||
840ad75830 | |||
4c7dd7228f | |||
46a51addad | |||
0a5fe37025 | |||
00bb403497 | |||
11afa8140c | |||
850396e9da | |||
5ee75be49e | |||
879116a20c | |||
e509b1f488 | |||
468ff7037f | |||
df23504ccf | |||
66e3cb8eda | |||
6ddd2389dc | |||
402efb8c50 | |||
7b6eae0ce4 | |||
26ce9725ce | |||
ebfaa18f12 | |||
cc81d41a05 | |||
212176ee5c | |||
a63ec05e41 | |||
0dcb527bf3 | |||
54710f17fc | |||
e58a6593c0 | |||
62132570e1 | |||
9f0b8ba2f8 | |||
adbe0fbcd1 | |||
7896242f57 | |||
4a6722b9e9 | |||
7c9fb5228b | |||
81805b01cc |
126
docs/setup.fr.md
Normal file
126
docs/setup.fr.md
Normal 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 !
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -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"
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -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 s’agit d’une 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: "S’abonner 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 d’abonnement"
|
||||
follow-request: "Demande d'abonnement"
|
||||
follow-processing: "Demande en attente"
|
||||
follow-request: "Demande d’abonnement"
|
||||
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 l’abonnement"
|
||||
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 n’est mis·e en sourdine"
|
||||
no-blocked-users: "Aucun utilisateur·rice n’est 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 d’abonnement"
|
||||
follow-request: "Demande d'abonnement"
|
||||
follow: "S’abonner"
|
||||
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 d’abonnement"
|
||||
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 d’abonnement"
|
||||
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:
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -668,13 +668,6 @@ desktop/views/components/media-video.vue:
|
||||
sensitive: "閲覧注意"
|
||||
click-to-show: "クリックして表示"
|
||||
|
||||
desktop/views/components/follow-button.vue:
|
||||
following: "フォロー中"
|
||||
follow: "フォロー"
|
||||
request-pending: "フォロー許可待ち"
|
||||
follow-processing: "フォロー処理中"
|
||||
follow-request: "フォロー申請"
|
||||
|
||||
desktop/views/components/followers-window.vue:
|
||||
followers: "{} のフォロワー"
|
||||
|
||||
@ -1041,6 +1034,7 @@ admin/views/index.vue:
|
||||
dashboard: "ダッシュボード"
|
||||
instance: "インスタンス"
|
||||
emoji: "カスタム絵文字"
|
||||
moderators: "モデレーター"
|
||||
users: "ユーザー"
|
||||
update: "更新"
|
||||
announcements: "お知らせ"
|
||||
@ -1140,6 +1134,12 @@ admin/views/users.vue:
|
||||
unverify: "公式アカウントを解除する"
|
||||
unverified: "公式アカウントを解除しました"
|
||||
|
||||
admin/views/moderators.vue:
|
||||
add-moderator:
|
||||
title: "モデレーターの登録"
|
||||
add: "登録"
|
||||
added: "モデレーターを登録しました"
|
||||
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "絵文字の登録"
|
||||
@ -1336,7 +1336,7 @@ mobile/views/components/media-video.vue:
|
||||
sensitive: "閲覧注意"
|
||||
click-to-show: "クリックして表示"
|
||||
|
||||
mobile/views/components/follow-button.vue:
|
||||
common/views/components/follow-button.vue:
|
||||
following: "フォロー中"
|
||||
follow: "フォロー"
|
||||
request-pending: "フォロー許可待ち"
|
||||
|
@ -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: "おすすめのユーザーはおらん。"
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -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: "フォロー申請"
|
||||
|
@ -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: "フォロー申請"
|
||||
|
@ -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."
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -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: "フォロー許可待ち"
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.49.0",
|
||||
"clientVersion": "2.0.11740",
|
||||
"version": "10.51.0",
|
||||
"clientVersion": "2.0.11800",
|
||||
"codename": "nighthike",
|
||||
"main": "./built/index.js",
|
||||
"private": true,
|
||||
@ -176,6 +176,7 @@
|
||||
"pug": "2.0.3",
|
||||
"punycode": "2.1.1",
|
||||
"qrcode": "1.3.2",
|
||||
"randomcolor": "0.5.3",
|
||||
"ratelimiter": "3.2.0",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "4.1.10",
|
||||
@ -198,6 +199,7 @@
|
||||
"summaly": "2.2.0",
|
||||
"systeminformation": "3.47.0",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.1.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.0.33",
|
||||
@ -217,10 +219,10 @@
|
||||
"vue-i18n": "8.3.1",
|
||||
"vue-js-modal": "1.3.26",
|
||||
"vue-loader": "15.4.2",
|
||||
"vue-marquee-text-component": "1.1.0",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.2.1",
|
||||
"vue-sweetalert2": "1.5.7",
|
||||
"vue-template-compiler": "2.5.17",
|
||||
"vuedraggable": "2.16.0",
|
||||
"vuewordcloud": "18.7.11",
|
||||
|
@ -41,22 +41,22 @@ export default Vue.extend({
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
this.announcements.push({
|
||||
this.announcements.unshift({
|
||||
title: '',
|
||||
text: ''
|
||||
});
|
||||
},
|
||||
|
||||
remove(i) {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'warning',
|
||||
text: this.$t('_remove.are-you-sure').replace('$1', this.announcements.find((_, j) => j == i).title),
|
||||
showCancelButton: true
|
||||
}).then(res => {
|
||||
if (!res.value) return;
|
||||
if (!res) return;
|
||||
this.announcements = this.announcements.filter((_, j) => j !== i);
|
||||
this.save(true);
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('_remove.removed')
|
||||
});
|
||||
@ -68,13 +68,13 @@ export default Vue.extend({
|
||||
broadcasts: this.announcements
|
||||
}).then(() => {
|
||||
if (!silent) {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('saved')
|
||||
});
|
||||
}
|
||||
}).catch(e => {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
|
@ -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
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -20,8 +20,9 @@
|
||||
<ul>
|
||||
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
|
||||
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
|
||||
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
|
||||
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
|
||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="['far', 'grin']" fixed-width/>{{ $t('emoji') }}</li>
|
||||
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
|
||||
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
|
||||
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
|
||||
|
||||
@ -29,21 +30,27 @@
|
||||
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">{{ $t('update') }}</li> -->
|
||||
</ul>
|
||||
<div class="back-to-misskey">
|
||||
<a href="/"><fa icon="arrow-left"/> {{ $t('back-to-misskey') }}</a>
|
||||
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
|
||||
</div>
|
||||
<div class="version">
|
||||
<small>Misskey {{ version }}</small>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
<marquee-text v-if="instances.length > 0" class="instances" :repeat="10" :duration="10">
|
||||
<span v-for="instance in instances" class="instance"><b :style="{ background: instance.bg }">{{ instance.host }}</b>{{ instance.notesCount | number }}</span>
|
||||
</marquee-text>
|
||||
<div class="page">
|
||||
<div v-if="page == 'dashboard'"><x-dashboard/></div>
|
||||
<div v-if="page == 'instance'"><x-instance/></div>
|
||||
<div v-if="page == 'moderators'"><x-moderators/></div>
|
||||
<div v-if="page == 'users'"><x-users/></div>
|
||||
<div v-if="page == 'emoji'"><x-emoji/></div>
|
||||
<div v-if="page == 'announcements'"><x-announcements/></div>
|
||||
<div v-if="page == 'hashtags'"><x-hashtags/></div>
|
||||
<div v-if="page == 'drive'"></div>
|
||||
<div v-if="page == 'update'"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
@ -54,10 +61,15 @@ import i18n from '../../i18n';
|
||||
import { version } from '../../config';
|
||||
import XDashboard from "./dashboard.vue";
|
||||
import XInstance from "./instance.vue";
|
||||
import XModerators from "./moderators.vue";
|
||||
import XEmoji from "./emoji.vue";
|
||||
import XAnnouncements from "./announcements.vue";
|
||||
import XHashtags from "./hashtags.vue";
|
||||
import XUsers from "./users.vue";
|
||||
import { faHeadset, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faGrin } from '@fortawesome/free-regular-svg-icons';
|
||||
import MarqueeText from 'vue-marquee-text-component';
|
||||
import randomColor from 'randomcolor';
|
||||
|
||||
// Detect the user agent
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
@ -68,10 +80,12 @@ export default Vue.extend({
|
||||
components: {
|
||||
XDashboard,
|
||||
XInstance,
|
||||
XModerators,
|
||||
XEmoji,
|
||||
XAnnouncements,
|
||||
XHashtags,
|
||||
XUsers
|
||||
XUsers,
|
||||
MarqueeText
|
||||
},
|
||||
provide: {
|
||||
isMobile
|
||||
@ -81,9 +95,24 @@ export default Vue.extend({
|
||||
page: 'dashboard',
|
||||
version,
|
||||
isMobile,
|
||||
navOpend: !isMobile
|
||||
navOpend: !isMobile,
|
||||
instances: [],
|
||||
faGrin,
|
||||
faArrowLeft,
|
||||
faHeadset
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$root.api('instances').then(instances => {
|
||||
instances.forEach(i => {
|
||||
i.bg = randomColor({
|
||||
seed: i.host,
|
||||
luminosity: 'dark'
|
||||
});
|
||||
});
|
||||
this.instances = instances;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
nav(page: string) {
|
||||
this.page = page;
|
||||
@ -92,7 +121,7 @@ export default Vue.extend({
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
<style lang="stylus" scoped>
|
||||
.mk-admin
|
||||
$headerHeight = 48px
|
||||
|
||||
@ -253,6 +282,22 @@ export default Vue.extend({
|
||||
> main
|
||||
width 100%
|
||||
padding 0 0 0 250px
|
||||
|
||||
> .instances
|
||||
padding 8px
|
||||
background rgba(0, 0, 0, 0.7)
|
||||
color #fff
|
||||
font-size 14px
|
||||
|
||||
>>> .instance
|
||||
margin 0 10px
|
||||
|
||||
> b
|
||||
padding 0px 6px
|
||||
margin-right 4px
|
||||
border-radius 4px
|
||||
|
||||
> .page
|
||||
max-width 1300px
|
||||
|
||||
&.isMobile
|
||||
|
@ -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
|
||||
});
|
||||
|
61
src/client/app/admin/views/moderators.vue
Normal file
61
src/client/app/admin/views/moderators.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="jnhmugbb">
|
||||
<ui-card>
|
||||
<div slot="title"><fa icon="plus"/> {{ $t('add-moderator.title') }}</div>
|
||||
<section class="fit-top">
|
||||
<ui-input v-model="username" type="text">
|
||||
<span slot="prefix">@</span>
|
||||
</ui-input>
|
||||
<ui-button @click="add" :disabled="adding">{{ $t('add-moderator.add') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import parseAcct from "../../../../misc/acct/parse";
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/moderators.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
adding: false
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
async add() {
|
||||
this.adding = true;
|
||||
|
||||
const process = async () => {
|
||||
const user = await this.$root.api('users/show', parseAcct(this.username));
|
||||
await this.$root.api('admin/moderators/add', { userId: user.id });
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('add-moderator.added')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.adding = false;
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.jnhmugbb
|
||||
@media (min-width 500px)
|
||||
padding 16px
|
||||
|
||||
</style>
|
@ -49,6 +49,7 @@ import parseAcct from "../../../../misc/acct/parse";
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/users.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
verifyUsername: null,
|
||||
@ -67,13 +68,19 @@ export default Vue.extend({
|
||||
this.verifying = true;
|
||||
|
||||
const process = async () => {
|
||||
const user = await this.$root.os.api('users/show', parseAcct(this.verifyUsername));
|
||||
await this.$root.os.api('admin/verify-user', { userId: user.id });
|
||||
//this.$root.os.apis.dialog({ text: this.$t('verified') });
|
||||
const user = await this.$root.api('users/show', parseAcct(this.verifyUsername));
|
||||
await this.$root.api('admin/verify-user', { userId: user.id });
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('verified')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.verifying = false;
|
||||
@ -83,13 +90,19 @@ export default Vue.extend({
|
||||
this.unverifying = true;
|
||||
|
||||
const process = async () => {
|
||||
const user = await this.$root.os.api('users/show', parseAcct(this.unverifyUsername));
|
||||
await this.$root.os.api('admin/unverify-user', { userId: user.id });
|
||||
//this.$root.os.apis.dialog({ text: this.$t('unverified') });
|
||||
const user = await this.$root.api('users/show', parseAcct(this.unverifyUsername));
|
||||
await this.$root.api('admin/unverify-user', { userId: user.id });
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('unverified')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.unverifying = false;
|
||||
@ -99,13 +112,19 @@ export default Vue.extend({
|
||||
this.suspending = true;
|
||||
|
||||
const process = async () => {
|
||||
const user = await this.$root.os.api('users/show', parseAcct(this.suspendUsername));
|
||||
await this.$root.os.api('admin/suspend-user', { userId: user.id });
|
||||
//this.$root.os.apis.dialog({ text: this.$t('suspended') });
|
||||
const user = await this.$root.api('users/show', parseAcct(this.suspendUsername));
|
||||
await this.$root.api('admin/suspend-user', { userId: user.id });
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('suspended')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.suspending = false;
|
||||
@ -115,13 +134,19 @@ export default Vue.extend({
|
||||
this.unsuspending = true;
|
||||
|
||||
const process = async () => {
|
||||
const user = await this.$root.os.api('users/show', parseAcct(this.unsuspendUsername));
|
||||
await this.$root.os.api('admin/unsuspend-user', { userId: user.id });
|
||||
//this.$root.os.apis.dialog({ text: this.$t('unsuspended') });
|
||||
const user = await this.$root.api('users/show', parseAcct(this.unsuspendUsername));
|
||||
await this.$root.api('admin/unsuspend-user', { userId: user.id });
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('unsuspended')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.unsuspending = false;
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -43,6 +43,9 @@
|
||||
if (`${url.pathname}/`.startsWith('/admin/')) app = 'admin';
|
||||
//#endregion
|
||||
|
||||
// Script version
|
||||
const ver = localStorage.getItem('v') || VERSION;
|
||||
|
||||
//#region Detect the user language
|
||||
let lang = null;
|
||||
|
||||
@ -67,7 +70,7 @@
|
||||
|
||||
let locale = localStorage.getItem('locale');
|
||||
if (locale == null) {
|
||||
const locale = await fetch(`/assets/locales/${lang}.json`)
|
||||
const locale = await fetch(`/assets/locales/${lang}.json?ver=${ver}`)
|
||||
.then(response => response.json());
|
||||
|
||||
localStorage.setItem('locale', JSON.stringify(locale));
|
||||
@ -98,9 +101,6 @@
|
||||
app = isMobile ? 'mobile' : 'desktop';
|
||||
}
|
||||
|
||||
// Script version
|
||||
const ver = localStorage.getItem('v') || VERSION;
|
||||
|
||||
// Get salt query
|
||||
const salt = localStorage.getItem('salt')
|
||||
? `?salt=${localStorage.getItem('salt')}`
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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 })
|
||||
});
|
||||
|
@ -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')
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { sum } from '../../../../prelude/array';
|
||||
import shouldMuteNote from './should-mute-note';
|
||||
import MkNoteMenu from '../views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../views/components/reaction-picker.vue';
|
||||
import Ok from '../views/components/ok.vue';
|
||||
|
||||
function focus(el, fn) {
|
||||
const target = fn(el);
|
||||
@ -142,7 +141,10 @@ export default (opts: Opts = {}) => ({
|
||||
this.$root.api('notes/favorites/create', {
|
||||
noteId: this.appearNote.id
|
||||
}).then(() => {
|
||||
this.$root.new(Ok);
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
197
src/client/app/common/views/components/alert.vue
Normal file
197
src/client/app/common/views/components/alert.vue
Normal file
@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="felqjxyj" :class="{ splash }">
|
||||
<div class="bg" ref="bg" @click="onBgClick"></div>
|
||||
<div class="main" ref="main">
|
||||
<div class="icon" :class="type"><fa :icon="icon"/></div>
|
||||
<header v-if="title" v-html="title"></header>
|
||||
<div class="body" v-if="text" v-html="text"></div>
|
||||
<ui-horizon-group no-grow class="buttons" v-if="!splash">
|
||||
<ui-button @click="ok" primary autofocus>OK</ui-button>
|
||||
<ui-button @click="cancel" v-if="showCancelButton">Cancel</ui-button>
|
||||
</ui-horizon-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as anime from 'animejs';
|
||||
import { faTimesCircle, faQuestionCircle } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'info'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
showCancelButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
splash: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
icon(): any {
|
||||
switch (this.type) {
|
||||
case 'success': return 'check';
|
||||
case 'error': return faTimesCircle;
|
||||
case 'warning': return 'exclamation-triangle';
|
||||
case 'info': return 'info-circle';
|
||||
case 'question': return faQuestionCircle;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
(this.$refs.bg as any).style.pointerEvents = 'auto';
|
||||
anime({
|
||||
targets: this.$refs.bg,
|
||||
opacity: 1,
|
||||
duration: 100,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
anime({
|
||||
targets: this.$refs.main,
|
||||
opacity: 1,
|
||||
scale: [1.2, 1],
|
||||
duration: 300,
|
||||
easing: [0, 0.5, 0.5, 1]
|
||||
});
|
||||
|
||||
if (this.splash) {
|
||||
setTimeout(() => {
|
||||
this.close();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
ok() {
|
||||
this.$emit('ok');
|
||||
this.close();
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.$emit('cancel');
|
||||
this.close();
|
||||
},
|
||||
|
||||
close() {
|
||||
(this.$refs.bg as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.bg,
|
||||
opacity: 0,
|
||||
duration: 300,
|
||||
easing: 'linear'
|
||||
});
|
||||
|
||||
(this.$refs.main as any).style.pointerEvents = 'none';
|
||||
anime({
|
||||
targets: this.$refs.main,
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
duration: 300,
|
||||
easing: [0, 0.5, 0.5, 1],
|
||||
complete: () => this.destroyDom()
|
||||
});
|
||||
},
|
||||
|
||||
onBgClick() {
|
||||
this.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.felqjxyj
|
||||
display flex
|
||||
align-items center
|
||||
justify-content center
|
||||
position fixed
|
||||
z-index 30000
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
height 100%
|
||||
|
||||
&.splash
|
||||
&, *
|
||||
pointer-events none !important
|
||||
|
||||
> .main
|
||||
min-width 0
|
||||
width initial
|
||||
|
||||
> .bg
|
||||
display block
|
||||
position fixed
|
||||
top 0
|
||||
left 0
|
||||
width 100%
|
||||
height 100%
|
||||
background rgba(#000, 0.7)
|
||||
opacity 0
|
||||
pointer-events none
|
||||
|
||||
> .main
|
||||
display block
|
||||
position fixed
|
||||
margin auto
|
||||
padding 32px
|
||||
min-width 320px
|
||||
max-width 480px
|
||||
width calc(100% - 32px)
|
||||
text-align center
|
||||
background var(--face)
|
||||
border-radius 8px
|
||||
color var(--faceText)
|
||||
opacity 0
|
||||
|
||||
> .icon
|
||||
font-size 32px
|
||||
|
||||
&.success
|
||||
color #37ec92
|
||||
|
||||
&.error
|
||||
color #ec4137
|
||||
|
||||
&.warning
|
||||
color #ecb637
|
||||
|
||||
> *
|
||||
display block
|
||||
margin 0 auto
|
||||
|
||||
> header
|
||||
margin 16px 0 8px 0
|
||||
font-weight bold
|
||||
font-size 20px
|
||||
|
||||
& + .body
|
||||
margin-top 8px
|
||||
|
||||
> .body
|
||||
margin 16px 0
|
||||
|
||||
> .buttons
|
||||
margin-top 16px
|
||||
|
||||
</style>
|
@ -37,6 +37,8 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { lib } from 'emojilib';
|
||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart, faFlag } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/emoji-picker.vue'),
|
||||
@ -48,7 +50,7 @@ export default Vue.extend({
|
||||
categories: [{
|
||||
ref: 'customEmojiSection',
|
||||
text: this.$t('custom-emoji'),
|
||||
icon: ['fas', 'asterisk'],
|
||||
icon: faAsterisk,
|
||||
isActive: true
|
||||
}, {
|
||||
name: 'people',
|
||||
@ -60,43 +62,43 @@ export default Vue.extend({
|
||||
name: 'animals_and_nature',
|
||||
ref: 'animalsAndNatureSection',
|
||||
text: this.$t('animals-and-nature'),
|
||||
icon: ['fas', 'leaf'],
|
||||
icon: faLeaf,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'food_and_drink',
|
||||
ref: 'foodAndDrinkSection',
|
||||
text: this.$t('food-and-drink'),
|
||||
icon: ['fas', 'utensils'],
|
||||
icon: faUtensils,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'activity',
|
||||
ref: 'activitySection',
|
||||
text: this.$t('activity'),
|
||||
icon: ['fas', 'futbol'],
|
||||
icon: faFutbol,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'travel_and_places',
|
||||
ref: 'travelAndPlacesSection',
|
||||
text: this.$t('travel-and-places'),
|
||||
icon: ['fas', 'city'],
|
||||
icon: faCity,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'objects',
|
||||
ref: 'objectsSection',
|
||||
text: this.$t('objects'),
|
||||
icon: ['fas', 'poo-storm'],
|
||||
icon: faDice,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'symbols',
|
||||
ref: 'symbolsSection',
|
||||
text: this.$t('symbols'),
|
||||
icon: ['far', 'heart'],
|
||||
icon: faHeart,
|
||||
isActive: false
|
||||
}, {
|
||||
name: 'flags',
|
||||
ref: 'flagsSection',
|
||||
text: this.$t('flags'),
|
||||
icon: ['far', 'flag'],
|
||||
icon: faFlag,
|
||||
isActive: false
|
||||
}]
|
||||
}
|
||||
|
184
src/client/app/common/views/components/follow-button.vue
Normal file
184
src/client/app/common/views/components/follow-button.vue
Normal 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>
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -8,7 +8,7 @@
|
||||
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
|
||||
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>{{ $t('no-history') }}</p>
|
||||
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
|
||||
<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
|
||||
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
|
||||
</button>
|
||||
<template v-for="(message, i) in _messages">
|
||||
<x-message :message="message" :key="message.id"/>
|
||||
@ -20,7 +20,7 @@
|
||||
<footer>
|
||||
<transition name="fade">
|
||||
<div class="new-message" v-show="showIndicator">
|
||||
<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>{{ $t('new-message') }}</button>
|
||||
<button @click="onIndicatorClick"><i><fa :icon="faArrowCircleDown"/></i>{{ $t('new-message') }}</button>
|
||||
</div>
|
||||
</transition>
|
||||
<x-form :user="user" ref="form"/>
|
||||
@ -34,6 +34,7 @@ import i18n from '../../../i18n';
|
||||
import XMessage from './messaging-room.message.vue';
|
||||
import XForm from './messaging-room.form.vue';
|
||||
import { url } from '../../../config';
|
||||
import { faArrowCircleDown } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/messaging-room.vue'),
|
||||
@ -52,7 +53,8 @@ export default Vue.extend({
|
||||
existMoreMessages: false,
|
||||
connection: null,
|
||||
showIndicator: false,
|
||||
timer: null
|
||||
timer: null,
|
||||
faArrowCircleDown
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -9,7 +9,6 @@ import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { url } from '../../../config';
|
||||
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
|
||||
import Ok from './ok.vue';
|
||||
import { concat, intersperse } from '../../../../../prelude/array';
|
||||
|
||||
export default Vue.extend({
|
||||
@ -56,7 +55,7 @@ export default Vue.extend({
|
||||
}
|
||||
] : []
|
||||
], [
|
||||
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin ? [{
|
||||
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [{
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: this.$t('delete'),
|
||||
action: this.del
|
||||
@ -79,7 +78,10 @@ export default Vue.extend({
|
||||
this.$root.api('i/pin', {
|
||||
noteId: this.note.id
|
||||
}).then(() => {
|
||||
this.$root.new(Ok);
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
this.destroyDom();
|
||||
});
|
||||
},
|
||||
@ -93,19 +95,29 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
del() {
|
||||
if (!window.confirm(this.$t('delete-confirm'))) return;
|
||||
this.$root.alert({
|
||||
type: 'warning',
|
||||
text: this.$t('delete-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(res => {
|
||||
if (!res) return;
|
||||
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.note.id
|
||||
}).then(() => {
|
||||
this.destroyDom();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
favorite() {
|
||||
this.$root.api('notes/favorites/create', {
|
||||
noteId: this.note.id
|
||||
}).then(() => {
|
||||
this.$root.new(Ok);
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
this.destroyDom();
|
||||
});
|
||||
},
|
||||
@ -114,7 +126,10 @@ export default Vue.extend({
|
||||
this.$root.api('notes/favorites/delete', {
|
||||
noteId: this.note.id
|
||||
}).then(() => {
|
||||
this.$root.new(Ok);
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
this.destroyDom();
|
||||
});
|
||||
},
|
||||
|
@ -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>
|
@ -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;
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ export default Vue.extend({
|
||||
this.$store.state.i.bannerUrl = i.bannerUrl;
|
||||
|
||||
if (notify) {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('saved')
|
||||
});
|
||||
@ -223,6 +223,5 @@ export default Vue.extend({
|
||||
width 72px
|
||||
height 72px
|
||||
margin auto
|
||||
box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
|
||||
|
||||
</style>
|
||||
|
@ -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>
|
||||
|
@ -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'">
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="nicnklzforebnpfgasiypmpdaaglujqm">
|
||||
<label>
|
||||
<span>{{ $t('light-theme') }}</span>
|
||||
<span><fa :icon="faSun"/> {{ $t('light-theme') }}</span>
|
||||
<ui-select v-model="light" :placeholder="$t('light-theme')">
|
||||
<optgroup :label="$t('light-themes')">
|
||||
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
@ -13,7 +13,7 @@
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span>{{ $t('dark-theme') }}</span>
|
||||
<span><fa :icon="faMoon"/> {{ $t('dark-theme') }}</span>
|
||||
<ui-select v-model="dark" :placeholder="$t('dark-theme')">
|
||||
<optgroup :label="$t('dark-themes')">
|
||||
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
@ -104,6 +104,7 @@ import { Chrome } from 'vue-color';
|
||||
import * as uuid from 'uuid';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import * as JSON5 from 'json5';
|
||||
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
// 後方互換性のため
|
||||
function convertOldThemedefinition(t) {
|
||||
@ -135,7 +136,8 @@ export default Vue.extend({
|
||||
myThemeDesc: '',
|
||||
myThemePrimary: lightTheme.vars.primary,
|
||||
myThemeSecondary: lightTheme.vars.secondary,
|
||||
myThemeText: lightTheme.vars.text
|
||||
myThemeText: lightTheme.vars.text,
|
||||
faMoon, faSun
|
||||
};
|
||||
},
|
||||
|
||||
@ -221,7 +223,7 @@ export default Vue.extend({
|
||||
try {
|
||||
theme = JSON5.parse(code);
|
||||
} catch (e) {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: this.$t('invalid-theme')
|
||||
});
|
||||
@ -234,7 +236,7 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
if (theme.id == null) {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'error',
|
||||
text: this.$t('invalid-theme')
|
||||
});
|
||||
@ -242,7 +244,7 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
if (this.$store.state.device.themes.some(t => t.id == theme.id)) {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'info',
|
||||
text: this.$t('already-installed')
|
||||
});
|
||||
@ -254,7 +256,7 @@ export default Vue.extend({
|
||||
key: 'themes', value: themes
|
||||
});
|
||||
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('installed').replace('{}', theme.name)
|
||||
});
|
||||
@ -267,7 +269,7 @@ export default Vue.extend({
|
||||
key: 'themes', value: themes
|
||||
});
|
||||
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'info',
|
||||
text: this.$t('uninstalled').replace('{}', theme.name)
|
||||
});
|
||||
@ -304,7 +306,7 @@ export default Vue.extend({
|
||||
const theme = this.myTheme;
|
||||
|
||||
if (theme.name == null || theme.name.trim() == '') {
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'warning',
|
||||
text: this.$t('theme-name-required')
|
||||
});
|
||||
@ -318,7 +320,7 @@ export default Vue.extend({
|
||||
key: 'themes', value: themes
|
||||
});
|
||||
|
||||
this.$swal({
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
text: this.$t('saved')
|
||||
});
|
||||
|
@ -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>
|
@ -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">
|
||||
|
@ -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() {
|
||||
|
@ -38,12 +38,24 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
styl: 'fill'
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (this.autofocus) {
|
||||
this.$nextTick(() => {
|
||||
this.$el.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@ -57,6 +69,7 @@ export default Vue.extend({
|
||||
text-align center
|
||||
font-weight normal
|
||||
font-size 16px
|
||||
line-height 24px
|
||||
border none
|
||||
border-radius 6px
|
||||
outline none
|
||||
@ -85,6 +98,7 @@ export default Vue.extend({
|
||||
&.inline
|
||||
display inline-block
|
||||
width auto
|
||||
min-width 100px
|
||||
|
||||
&.primary
|
||||
font-weight bold
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze" :class="{ inputs }">
|
||||
<div class="vnxwkwuf" :class="{ inputs, noGrow }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@ -15,21 +15,27 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
noGrow: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
|
||||
display flex
|
||||
|
||||
.vnxwkwuf
|
||||
&.inputs
|
||||
margin 32px 0
|
||||
|
||||
&:not(.noGrow)
|
||||
display flex
|
||||
|
||||
> *
|
||||
flex 1
|
||||
|
||||
&:not(:last-child)
|
||||
> *:not(:last-child)
|
||||
margin-right 16px
|
||||
</style>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ol v-if="uploads.length > 0">
|
||||
<li v-for="ctx in uploads" :key="ctx.id">
|
||||
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
|
||||
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
|
||||
<p class="name"><fa icon="spinner" pulse/>{{ ctx.name }}</p>
|
||||
<p class="status">
|
||||
<span class="initing" v-if="ctx.progress == undefined">{{ $t('waiting') }}<mk-ellipsis/></span>
|
||||
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
|
||||
@ -57,17 +57,11 @@ export default Vue.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload if the file didn't exist yet
|
||||
const buf = new Uint8Array(e.target.result);
|
||||
let bin = '';
|
||||
// We use for-of loop instead of apply() to avoid RangeError
|
||||
// SEE: https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string
|
||||
for (const byte of buf) bin += String.fromCharCode(byte);
|
||||
const ctx = {
|
||||
id: id,
|
||||
name: file.name || 'untitled',
|
||||
progress: undefined,
|
||||
img: 'data:*/*;base64,' + btoa(bin)
|
||||
img: window.URL.createObjectURL(file)
|
||||
};
|
||||
|
||||
this.uploads.push(ctx);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 || {};
|
||||
|
@ -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)"/>
|
||||
|
@ -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>
|
@ -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')
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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')
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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')
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||
<button class="kao" :title="$t('insert-a-kao')" @click="kao"><fa :icon="['far', 'smile']"/></button>
|
||||
<button class="poll" :title="$t('create-poll')" @click="poll = !poll"><fa icon="chart-pie"/></button>
|
||||
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa icon="eye-slash"/></button>
|
||||
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa :icon="['far', 'eye-slash']"/></button>
|
||||
<button class="geo" :title="$t('attach-location-information')" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
|
||||
<button class="visibility" :title="$t('visibility')" @click="setVisibility" ref="visibilityButton">
|
||||
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
|
||||
|
@ -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,
|
||||
|
@ -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')
|
||||
});
|
||||
|
@ -58,7 +58,7 @@
|
||||
<i><fa icon="angle-right"/></i>
|
||||
</p>
|
||||
</li>
|
||||
<li v-if="$store.state.i.isAdmin">
|
||||
<li v-if="$store.state.i.isAdmin || $store.state.i.isModerator">
|
||||
<a href="/admin">
|
||||
<i><fa icon="terminal"/></i>
|
||||
<span>{{ $t('admin') }}</span>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<header :style="bannerStyle">
|
||||
<div>
|
||||
<button class="menu" @click="menu" ref="menu"><fa icon="ellipsis-h"/></button>
|
||||
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow"/>
|
||||
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow" mini/>
|
||||
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
||||
<span class="name">{{ user | userName }}</span>
|
||||
<span class="acct">@{{ user | acct }}</span>
|
||||
@ -87,7 +87,6 @@ import XNotes from './deck.notes.vue';
|
||||
import XNote from '../../components/note.vue';
|
||||
import Menu from '../../../../common/views/components/menu.vue';
|
||||
import MkUserListsWindow from '../../components/user-lists-window.vue';
|
||||
import Ok from '../../../../common/views/components/ok.vue';
|
||||
import { concat } from '../../../../../../prelude/array';
|
||||
import * as ApexCharts from 'apexcharts';
|
||||
|
||||
@ -155,7 +154,8 @@ export default Vue.extend({
|
||||
this.$root.api('users/notes', {
|
||||
userId: this.user.id,
|
||||
fileType: image,
|
||||
limit: 9
|
||||
limit: 9,
|
||||
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||
}).then(notes => {
|
||||
notes.forEach(note => {
|
||||
note.files.forEach(file => {
|
||||
@ -254,6 +254,7 @@ export default Vue.extend({
|
||||
this.$root.api('users/notes', {
|
||||
userId: this.user.id,
|
||||
limit: fetchLimit + 1,
|
||||
untilDate: new Date().getTime() + 1000 * 86400 * 365,
|
||||
withFiles: this.withFiles,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
@ -274,7 +275,7 @@ export default Vue.extend({
|
||||
const promise = this.$root.api('users/notes', {
|
||||
userId: this.user.id,
|
||||
limit: fetchLimit + 1,
|
||||
untilId: (this.$refs.timeline as any).tail().id,
|
||||
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
|
||||
withFiles: this.withFiles,
|
||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||
@ -306,7 +307,10 @@ export default Vue.extend({
|
||||
listId: list.id,
|
||||
userId: this.user.id
|
||||
});
|
||||
this.$root.new(Ok);
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
@ -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 => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="profile" v-if="$store.getters.isSignedIn">
|
||||
<div class="friend-form" v-if="$store.state.i.id != user.id">
|
||||
<mk-follow-button :user="user" size="big"/>
|
||||
<mk-follow-button :user="user" block/>
|
||||
<p class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</p>
|
||||
<p class="stalk" v-if="user.isFollowing">
|
||||
<span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span>
|
||||
@ -11,11 +11,11 @@
|
||||
<div class="action-form">
|
||||
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
|
||||
<span v-if="user.isMuted"><fa icon="eye"/> {{ $t('unmute') }}</span>
|
||||
<span v-else><fa icon="eye-slash"/> {{ $t('mute') }}</span>
|
||||
<span v-else><fa :icon="['far', 'eye-slash']"/> {{ $t('mute') }}</span>
|
||||
</ui-button>
|
||||
<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
|
||||
<span v-if="user.isBlocking"><fa icon="user"/> {{ $t('unblock') }}</span>
|
||||
<span v-else><fa icon="user-slash"/> {{ $t('block') }}</span>
|
||||
<span v-if="user.isBlocking"><fa icon="ban"/> {{ $t('unblock') }}</span>
|
||||
<span v-else><fa icon="ban"/> {{ $t('block') }}</span>
|
||||
</ui-button>
|
||||
<ui-button @click="list"><fa icon="list"/> {{ $t('push-to-a-list') }}</ui-button>
|
||||
</div>
|
||||
@ -73,7 +73,13 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
block() {
|
||||
if (!window.confirm(this.$t('block-confirm'))) return;
|
||||
this.$root.alert({
|
||||
type: 'warning',
|
||||
text: this.$t('block-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(res => {
|
||||
if (!res) return;
|
||||
|
||||
this.$root.api('blocking/create', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
@ -81,6 +87,7 @@ export default Vue.extend({
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
unblock() {
|
||||
@ -101,7 +108,7 @@ export default Vue.extend({
|
||||
listId: list.id,
|
||||
userId: this.user.id
|
||||
});
|
||||
this.$dialog({
|
||||
this.$root.alert({
|
||||
title: 'Done!',
|
||||
text: this.$t('list-pushed').replace('{user}', this.user.name).replace('{list}', list.title)
|
||||
});
|
||||
|
@ -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 => {
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -3,7 +3,10 @@
|
||||
<mk-widget-container :show-header="props.design == 0">
|
||||
<template slot="header"><fa icon="pencil-alt"/>{{ $t('title') }}</template>
|
||||
|
||||
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body">
|
||||
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body"
|
||||
@dragover.stop="onDragover"
|
||||
@drop.stop="onDrop"
|
||||
>
|
||||
<div class="textarea">
|
||||
<textarea
|
||||
:disabled="posting"
|
||||
@ -130,6 +133,33 @@ export default define({
|
||||
(this.$refs.uploader as any).upload(file);
|
||||
},
|
||||
|
||||
onDragover(e) {
|
||||
const isFile = e.dataTransfer.items[0].kind == 'file';
|
||||
const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
|
||||
if (isFile || isDriveFile) {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
|
||||
}
|
||||
},
|
||||
|
||||
onDrop(e): void {
|
||||
// ファイルだったら
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
e.preventDefault();
|
||||
Array.from(e.dataTransfer.files).forEach(this.upload);
|
||||
return;
|
||||
}
|
||||
|
||||
//#region ドライブのファイル
|
||||
const driveFile = e.dataTransfer.getData('mk_drive_file');
|
||||
if (driveFile != null && driveFile != '') {
|
||||
const file = JSON.parse(driveFile);
|
||||
this.files.push(file);
|
||||
e.preventDefault();
|
||||
}
|
||||
//#endregion
|
||||
},
|
||||
|
||||
async emoji() {
|
||||
const Picker = await import('../components/emoji-picker-dialog.vue').then(m => m.default);
|
||||
const button = this.$refs.emoji;
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -7,7 +7,6 @@ import Vuex from 'vuex';
|
||||
import VueRouter from 'vue-router';
|
||||
import VAnimateCss from 'v-animate-css';
|
||||
import VModal from 'vue-js-modal';
|
||||
import VueSweetalert2 from 'vue-sweetalert2';
|
||||
import VueI18n from 'vue-i18n';
|
||||
|
||||
import VueHotkey from './common/hotkey';
|
||||
@ -16,6 +15,7 @@ import checkForUpdate from './common/scripts/check-for-update';
|
||||
import MiOS from './mios';
|
||||
import { clientVersion as version, codename, lang } from './config';
|
||||
import { builtinThemes, lightTheme, applyTheme } from './theme';
|
||||
import Alert from './common/views/components/alert.vue';
|
||||
|
||||
if (localStorage.getItem('theme') == null) {
|
||||
applyTheme(lightTheme);
|
||||
@ -25,47 +25,126 @@ if (localStorage.getItem('theme') == null) {
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
|
||||
/* なぜか動かない
|
||||
import faRetweet from '@fortawesome/free-solid-svg-icons/faRetweet';
|
||||
import faPlus from '@fortawesome/free-solid-svg-icons/faPlus';
|
||||
import faUser from '@fortawesome/free-solid-svg-icons/faUser';
|
||||
import faCog from '@fortawesome/free-solid-svg-icons/faCog';
|
||||
import faCheck from '@fortawesome/free-solid-svg-icons/faCheck';
|
||||
import faStar from '@fortawesome/free-solid-svg-icons/faStar';
|
||||
import faReply from '@fortawesome/free-solid-svg-icons/faReply';
|
||||
import faEllipsisH from '@fortawesome/free-solid-svg-icons/faEllipsisH';
|
||||
import faQuoteLeft from '@fortawesome/free-solid-svg-icons/faQuoteLeft';
|
||||
import faQuoteRight from '@fortawesome/free-solid-svg-icons/faQuoteRight';
|
||||
import faAngleUp from '@fortawesome/free-solid-svg-icons/faAngleUp';
|
||||
import faAngleDown from '@fortawesome/free-solid-svg-icons/faAngleDown';
|
||||
import faAt from '@fortawesome/free-solid-svg-icons/faAt';
|
||||
import faHashtag from '@fortawesome/free-solid-svg-icons/faHashtag';
|
||||
import faHome from '@fortawesome/free-solid-svg-icons/faHome';
|
||||
import faGlobe from '@fortawesome/free-solid-svg-icons/faGlobe';
|
||||
import faCircle from '@fortawesome/free-solid-svg-icons/faCircle';
|
||||
import faList from '@fortawesome/free-solid-svg-icons/faList';
|
||||
import faHeart from '@fortawesome/free-solid-svg-icons/faHeart';
|
||||
import faUnlock from '@fortawesome/free-solid-svg-icons/faUnlock';
|
||||
import faRssSquare from '@fortawesome/free-solid-svg-icons/faRssSquare';
|
||||
import faSort from '@fortawesome/free-solid-svg-icons/faSort';
|
||||
import faChartPie from '@fortawesome/free-solid-svg-icons/faChartPie';
|
||||
import faChartBar from '@fortawesome/free-solid-svg-icons/faChartBar';
|
||||
import faPencilAlt from '@fortawesome/free-solid-svg-icons/faPencilAlt';
|
||||
import faColumns from '@fortawesome/free-solid-svg-icons/faColumns';
|
||||
import faComments from '@fortawesome/free-solid-svg-icons/faComments';
|
||||
import faGamepad from '@fortawesome/free-solid-svg-icons/faGamepad';
|
||||
import faCloud from '@fortawesome/free-solid-svg-icons/faCloud';
|
||||
import faPowerOff from '@fortawesome/free-solid-svg-icons/faPowerOff';
|
||||
import faChevronCircleLeft from '@fortawesome/free-solid-svg-icons/faChevronCircleLeft';
|
||||
import faChevronCircleRight from '@fortawesome/free-solid-svg-icons/faChevronCircleRight';
|
||||
import faShareAlt from '@fortawesome/free-solid-svg-icons/faShareAlt';
|
||||
import faTimes from '@fortawesome/free-solid-svg-icons/faTimes';
|
||||
import faThumbtack from '@fortawesome/free-solid-svg-icons/faThumbtack';
|
||||
import faSearch from '@fortawesome/free-solid-svg-icons/faSearch';
|
||||
import {
|
||||
faRetweet,
|
||||
faPlus,
|
||||
faUser,
|
||||
faCog,
|
||||
faCheck,
|
||||
faStar,
|
||||
faReply,
|
||||
faEllipsisH,
|
||||
faQuoteLeft,
|
||||
faQuoteRight,
|
||||
faAngleUp,
|
||||
faAngleDown,
|
||||
faAt,
|
||||
faHashtag,
|
||||
faHome,
|
||||
faGlobe,
|
||||
faCircle,
|
||||
faList,
|
||||
faHeart,
|
||||
faUnlock,
|
||||
faRssSquare,
|
||||
faSort,
|
||||
faChartPie,
|
||||
faChartBar,
|
||||
faPencilAlt,
|
||||
faColumns,
|
||||
faComments,
|
||||
faGamepad,
|
||||
faCloud,
|
||||
faPowerOff,
|
||||
faChevronCircleLeft,
|
||||
faChevronCircleRight,
|
||||
faShareAlt,
|
||||
faTimes,
|
||||
faThumbtack,
|
||||
faSearch,
|
||||
faAngleRight,
|
||||
faWrench,
|
||||
faTerminal,
|
||||
faMoon,
|
||||
faPalette,
|
||||
faSlidersH,
|
||||
faDesktop,
|
||||
faVolumeUp,
|
||||
faLanguage,
|
||||
faInfoCircle,
|
||||
faExclamationTriangle,
|
||||
faKey,
|
||||
faBan,
|
||||
faCogs,
|
||||
faUnlockAlt,
|
||||
faPuzzlePiece,
|
||||
faMobileAlt,
|
||||
faSignInAlt,
|
||||
faSyncAlt,
|
||||
faPaperPlane,
|
||||
faUpload,
|
||||
faMapMarkerAlt,
|
||||
faEnvelope,
|
||||
faLock,
|
||||
faFolderOpen,
|
||||
faBirthdayCake,
|
||||
faImage,
|
||||
faEye,
|
||||
faDownload,
|
||||
faFileImport,
|
||||
faLink,
|
||||
faArrowRight,
|
||||
faICursor,
|
||||
faCaretRight,
|
||||
faReplyAll,
|
||||
faCamera,
|
||||
faMinus,
|
||||
faCaretDown,
|
||||
faCalculator,
|
||||
faUsers,
|
||||
faBars,
|
||||
faFileImage,
|
||||
faPollH,
|
||||
faFolder,
|
||||
faMicrochip,
|
||||
faMemory,
|
||||
faServer,
|
||||
faExclamationCircle,
|
||||
faSpinner,
|
||||
faBroadcastTower,
|
||||
faChartLine,
|
||||
faEllipsisV,
|
||||
faStickyNote,
|
||||
faUserPlus,
|
||||
faExternalLinkSquareAlt,
|
||||
faSync,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import farBell from '@fortawesome/free-regular-svg-icons/faBell';
|
||||
import farEnvelope from '@fortawesome/free-regular-svg-icons/faEnvelope';
|
||||
import farComments from '@fortawesome/free-regular-svg-icons/faComments';
|
||||
import {
|
||||
faBell as farBell,
|
||||
faEnvelope as farEnvelope,
|
||||
faComments as farComments,
|
||||
faTrashAlt as farTrashAlt,
|
||||
faWindowRestore as farWindowRestore,
|
||||
faFolder as farFolder,
|
||||
faLaugh as farLaugh,
|
||||
faSmile as farSmile,
|
||||
faEyeSlash as farEyeSlash,
|
||||
faFolderOpen as farFolderOpen,
|
||||
faSave as farSave,
|
||||
faImages as farImages,
|
||||
faChartBar as farChartBar,
|
||||
faCommentAlt as farCommentAlt,
|
||||
faClock as farClock,
|
||||
faCalendarAlt as farCalendarAlt,
|
||||
faHdd as farHdd,
|
||||
} from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
import {
|
||||
faTwitter as fabTwitter,
|
||||
faGithub as fabGithub,
|
||||
} from '@fortawesome/free-brands-svg-icons';
|
||||
import i18n from './i18n';
|
||||
|
||||
library.add(
|
||||
faRetweet,
|
||||
@ -104,16 +183,84 @@ library.add(
|
||||
faTimes,
|
||||
faThumbtack,
|
||||
faSearch,
|
||||
faAngleRight,
|
||||
faWrench,
|
||||
faTerminal,
|
||||
faMoon,
|
||||
faPalette,
|
||||
faSlidersH,
|
||||
faDesktop,
|
||||
faVolumeUp,
|
||||
faLanguage,
|
||||
faInfoCircle,
|
||||
faExclamationTriangle,
|
||||
faKey,
|
||||
faBan,
|
||||
faCogs,
|
||||
faUnlockAlt,
|
||||
faPuzzlePiece,
|
||||
faMobileAlt,
|
||||
faSignInAlt,
|
||||
faSyncAlt,
|
||||
faPaperPlane,
|
||||
faUpload,
|
||||
faMapMarkerAlt,
|
||||
faEnvelope,
|
||||
faLock,
|
||||
faFolderOpen,
|
||||
faBirthdayCake,
|
||||
faImage,
|
||||
faEye,
|
||||
faDownload,
|
||||
faFileImport,
|
||||
faLink,
|
||||
faArrowRight,
|
||||
faICursor,
|
||||
faCaretRight,
|
||||
faReplyAll,
|
||||
faCamera,
|
||||
faMinus,
|
||||
faCaretDown,
|
||||
faCalculator,
|
||||
faUsers,
|
||||
faBars,
|
||||
faFileImage,
|
||||
faPollH,
|
||||
faFolder,
|
||||
faMicrochip,
|
||||
faMemory,
|
||||
faServer,
|
||||
faExclamationCircle,
|
||||
faSpinner,
|
||||
faBroadcastTower,
|
||||
faChartLine,
|
||||
faEllipsisV,
|
||||
faStickyNote,
|
||||
faUserPlus,
|
||||
faExternalLinkSquareAlt,
|
||||
faSync,
|
||||
|
||||
farBell,
|
||||
farEnvelope,
|
||||
farComments,
|
||||
farTrashAlt,
|
||||
farWindowRestore,
|
||||
farFolder,
|
||||
farLaugh,
|
||||
farSmile,
|
||||
farEyeSlash,
|
||||
farFolderOpen,
|
||||
farSave,
|
||||
farImages,
|
||||
farChartBar,
|
||||
farCommentAlt,
|
||||
farClock,
|
||||
farCalendarAlt,
|
||||
farHdd,
|
||||
|
||||
fabTwitter,
|
||||
fabGithub
|
||||
);
|
||||
*/
|
||||
|
||||
import { fas } from '@fortawesome/free-solid-svg-icons';
|
||||
import { far } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
library.add(fas, far);
|
||||
//#endregion
|
||||
|
||||
Vue.use(Vuex);
|
||||
@ -121,7 +268,6 @@ Vue.use(VueRouter);
|
||||
Vue.use(VAnimateCss);
|
||||
Vue.use(VModal);
|
||||
Vue.use(VueHotkey);
|
||||
Vue.use(VueSweetalert2);
|
||||
Vue.use(VueI18n);
|
||||
|
||||
Vue.component('fa', FontAwesomeIcon);
|
||||
@ -269,13 +415,7 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS]) => void,
|
||||
}, { passive: true });
|
||||
|
||||
const app = new Vue({
|
||||
i18n: new VueI18n({
|
||||
sync: false,
|
||||
locale: lang,
|
||||
messages: {
|
||||
[lang]: {}
|
||||
}
|
||||
}),
|
||||
i18n: i18n(),
|
||||
store: os.store,
|
||||
data() {
|
||||
return {
|
||||
@ -299,6 +439,13 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS]) => void,
|
||||
document.body.appendChild(x.$el);
|
||||
return x;
|
||||
},
|
||||
alert(opts) {
|
||||
return new Promise((res) => {
|
||||
const vm = this.new(Alert, opts);
|
||||
vm.$once('ok', () => res(true));
|
||||
vm.$once('cancel', () => res(false));
|
||||
});
|
||||
}
|
||||
},
|
||||
router,
|
||||
render: createEl => createEl(App)
|
||||
|
@ -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 => {
|
||||
|
@ -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 },
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -51,8 +51,6 @@ export default Vue.extend({
|
||||
top 0
|
||||
bottom 0
|
||||
right 20px
|
||||
|
||||
> *
|
||||
height 100%
|
||||
|
||||
</style>
|
||||
|
@ -1,134 +0,0 @@
|
||||
<template>
|
||||
<button class="mk-follow-button"
|
||||
:class="{ wait: wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou }"
|
||||
@click="onClick"
|
||||
:disabled="wait"
|
||||
>
|
||||
<template v-if="!wait">
|
||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/> {{ $t('request-pending') }}</template>
|
||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/> {{ $t('follow-processing') }}</template>
|
||||
<template v-else-if="u.isFollowing"><fa icon="minus"/> {{ $t('following') }}</template>
|
||||
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
|
||||
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
|
||||
</template>
|
||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('mobile/views/components/follow-button.vue'),
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
u: this.user,
|
||||
wait: false,
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('follow', this.onFollowChange);
|
||||
this.connection.on('unfollow', this.onFollowChange);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onFollowChange(user) {
|
||||
if (user.id == this.u.id) {
|
||||
this.u.isFollowing = user.isFollowing;
|
||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
async onClick() {
|
||||
this.wait = true;
|
||||
|
||||
try {
|
||||
if (this.u.isFollowing) {
|
||||
this.u = await this.$root.api('following/delete', {
|
||||
userId: this.u.id
|
||||
});
|
||||
} else {
|
||||
if (this.u.hasPendingFollowRequestFromYou) {
|
||||
this.u = await this.$root.api('following/requests/cancel', {
|
||||
userId: this.u.id
|
||||
});
|
||||
} else if (this.u.isLocked) {
|
||||
this.u = await this.$root.api('following/create', {
|
||||
userId: this.u.id
|
||||
});
|
||||
} else {
|
||||
this.u = await this.$root.api('following/create', {
|
||||
userId: this.user.id
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
this.wait = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.mk-follow-button
|
||||
display block
|
||||
user-select none
|
||||
cursor pointer
|
||||
padding 0 16px
|
||||
margin 0
|
||||
min-width 100px
|
||||
line-height 36px
|
||||
font-size 14px
|
||||
font-weight bold
|
||||
color var(--primary)
|
||||
background transparent
|
||||
outline none
|
||||
border solid 1px var(--primary)
|
||||
border-radius 36px
|
||||
|
||||
&:hover
|
||||
background var(--primaryAlpha01)
|
||||
|
||||
&:active
|
||||
background var(--primaryAlpha02)
|
||||
|
||||
&.active
|
||||
color var(--primaryForeground)
|
||||
background var(--primary)
|
||||
|
||||
&:hover
|
||||
background var(--primaryLighten10)
|
||||
border-color var(--primaryLighten10)
|
||||
|
||||
&:active
|
||||
background var(--primaryDarken10)
|
||||
border-color var(--primaryDarken10)
|
||||
|
||||
&.wait
|
||||
cursor wait !important
|
||||
opacity 0.7
|
||||
|
||||
*
|
||||
pointer-events none
|
||||
|
||||
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user