Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f5f233bb5 | |||
d33492cd49 | |||
83ad9f369f | |||
3a78b62520 | |||
2479f75d8a | |||
f0d187f71e | |||
056942391a | |||
2feef81516 | |||
037d4b581b | |||
da4cf6fdb4 | |||
848bcd5a63 | |||
7b60b6c6dc | |||
fbc801d1da | |||
2f18f82e3d | |||
c2d5a96bb6 | |||
46575d4f04 | |||
5e7be93980 | |||
3f749c6540 | |||
2a7d4ee866 | |||
083095ded6 | |||
7207663f37 | |||
66b49c909a | |||
ab469aa243 | |||
afa4563e1e | |||
320b3d8617 | |||
7493429b4d | |||
d70f7a717b | |||
4ab38b7894 | |||
2068407be0 | |||
10a7369fec |
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,6 +1,19 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
12.8.0 (2020/02/13)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* タイムラインなどを遡っているときは新しいアイテムが来てもスクロールしないように
|
||||
* 表示言語を切り替えられるように
|
||||
* グループに招待されたときの通知を追加
|
||||
* フランス語と関西弁を有効に
|
||||
* OSネイティブの絵文字を使用オプションを追加
|
||||
|
||||
### 🐛Fixes
|
||||
* リストを追加するとエラーが出る問題を修正
|
||||
* タイムラインを放置すると先頭の投稿が見えなくなるのを修正
|
||||
|
||||
12.7.1 (2020/02/11)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Čeština"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Dansk"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Deutsch"
|
||||
|
@ -1,20 +1,5 @@
|
||||
---
|
||||
_ago:
|
||||
unknown: "Unknown"
|
||||
future: "Future"
|
||||
justNow: "Just now"
|
||||
secondsAgo: "{n}s ago"
|
||||
minutesAgo: "{n}m ago"
|
||||
hoursAgo: "{n}h ago"
|
||||
daysAgo: "{n}d ago"
|
||||
weeksAgo: "{n}week(s) ago"
|
||||
monthsAgo: "{n}month(s) ago"
|
||||
yearsAgo: "{n}year(s) ago"
|
||||
_time:
|
||||
second: "s"
|
||||
minute: "m"
|
||||
hour: "h"
|
||||
day: "d"
|
||||
_lang_: "English"
|
||||
introMisskey: "Welcome! Misskey is an open source distributed microblogging service.\nCreate \"notes\" to share what's happening or to tell everyone about you📡\nThen send \"reactions\" to respond quickly to everyone's notes👍\nLet's explore a new world🚀"
|
||||
monthAndDay: "{month}/{day}"
|
||||
search: "Search"
|
||||
@ -84,7 +69,7 @@ enterListName: "List name"
|
||||
privacy: "Privacy"
|
||||
makeFollowManuallyApprove: "Follow requests require approval"
|
||||
defaultNoteVisibility: "Default visibility"
|
||||
follow: "Following"
|
||||
follow: "Follow"
|
||||
followRequest: "Request follow"
|
||||
followRequests: "Follow requests"
|
||||
unfollow: "Unfollow"
|
||||
@ -127,7 +112,7 @@ flagAsBot: "This account is a bot"
|
||||
flagAsCat: "This account is a cat"
|
||||
autoAcceptFollowed: "Automatically approve follow requests from users you're following"
|
||||
addAcount: "Add Account"
|
||||
loginFailed: "Sign in failure"
|
||||
loginFailed: "Failed to sign in"
|
||||
showOnRemote: "View on remote instance"
|
||||
general: "General"
|
||||
wallpaper: "Wallpaper"
|
||||
@ -264,8 +249,8 @@ instanceDescription: "Instance description"
|
||||
maintainerName: "Maintainer"
|
||||
maintainerEmail: "Maintainer email"
|
||||
tosUrl: "Terms of Service URL"
|
||||
thisYear: "This year"
|
||||
thisMonth: "This month"
|
||||
thisYear: "Year"
|
||||
thisMonth: "Month"
|
||||
today: "Today"
|
||||
dayX: "{day} days"
|
||||
monthX: "{month} months"
|
||||
@ -370,12 +355,52 @@ members: "Members"
|
||||
transfer: "Transfer"
|
||||
messagingWithUser: "Messaging with other user"
|
||||
messagingWithGroup: "Messaging within group"
|
||||
title: "Title"
|
||||
text: "Text"
|
||||
enable: "Enable"
|
||||
next: "Next"
|
||||
retype: "Enter again"
|
||||
noteOf: "{user}'s notes"
|
||||
inviteToGroup: "Invite to group"
|
||||
maxNoteTextLength: "Character limit of the note"
|
||||
quoteAttached: "Quoted"
|
||||
quoteQuestion: "Do you want to append a quote?"
|
||||
noMessagesYet: "No messages yet"
|
||||
newMessageExists: "You've got a new message"
|
||||
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
||||
signinRequired: "Please sign in"
|
||||
invitationCode: "Invitation code"
|
||||
checking: "Checking"
|
||||
available: "Available"
|
||||
unavailable: "Not available"
|
||||
usernameInvalidFormat: "letters, numbers and _ are acceptable."
|
||||
tooShort: "Too short"
|
||||
tooLong: "Too long"
|
||||
weakPassword: "Weak password"
|
||||
normalPassword: "Good password"
|
||||
strongPassword: "Strong password"
|
||||
passwordMatched: "Matched"
|
||||
passwordNotMatched: "Doesn't match"
|
||||
signinWith: "Sign in with {x}"
|
||||
tapSecurityKey: "Tap your security key"
|
||||
or: "Or"
|
||||
uiLanguage: "UI display language"
|
||||
_ago:
|
||||
unknown: "Unknown"
|
||||
future: "Future"
|
||||
justNow: "Just now"
|
||||
secondsAgo: "{n}s ago"
|
||||
minutesAgo: "{n}m ago"
|
||||
hoursAgo: "{n}h ago"
|
||||
daysAgo: "{n}d ago"
|
||||
weeksAgo: "{n}week(s) ago"
|
||||
monthsAgo: "{n}month(s) ago"
|
||||
yearsAgo: "{n}year(s) ago"
|
||||
_time:
|
||||
second: "s"
|
||||
minute: "m"
|
||||
hour: "h"
|
||||
day: "d"
|
||||
_tutorial:
|
||||
title: "How to use Misskey"
|
||||
step1_1: "Welcome!"
|
||||
|
@ -1,20 +1,5 @@
|
||||
---
|
||||
_ago:
|
||||
unknown: "Desconocido"
|
||||
future: "Futuro"
|
||||
justNow: "Recién ahora"
|
||||
secondsAgo: "Hace {n} segundos"
|
||||
minutesAgo: "Hace {n} minutos"
|
||||
hoursAgo: "Hace {n} horas"
|
||||
daysAgo: "Hace {n} días"
|
||||
weeksAgo: "Hace {n} semanas"
|
||||
monthsAgo: "Hace {n} meses"
|
||||
yearsAgo: "Hace {n} años"
|
||||
_time:
|
||||
second: "Segundos"
|
||||
minute: "Minutos"
|
||||
hour: "Horas"
|
||||
day: "Días"
|
||||
_lang_: "Español"
|
||||
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto. Escribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos. 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos.👍\nExplora un nuevo mundo.🚀"
|
||||
monthAndDay: "{day}/{month}"
|
||||
search: "Buscar"
|
||||
@ -370,12 +355,52 @@ members: "Miembros"
|
||||
transfer: "Transferir"
|
||||
messagingWithUser: "Chatear con usuario"
|
||||
messagingWithGroup: "Chatear en grupo"
|
||||
title: "Título"
|
||||
text: "Texto"
|
||||
enable: "Activar"
|
||||
next: "Siguiente"
|
||||
retype: "Intentar de nuevo"
|
||||
noteOf: "Notas de {user}"
|
||||
inviteToGroup: "Invitar al grupo"
|
||||
maxNoteTextLength: "Límite de caracteres en una nota"
|
||||
quoteAttached: "Cita añadida"
|
||||
quoteQuestion: "¿Quiere añadir una cita?"
|
||||
noMessagesYet: "Aún no hay chat"
|
||||
newMessageExists: "Tienes un mensaje nuevo"
|
||||
onlyOneFileCanBeAttached: "Solo se puede añadir un archivo al mensaje"
|
||||
signinRequired: "Iniciar sesión"
|
||||
invitationCode: "Código de invitación"
|
||||
checking: "Comprobando"
|
||||
available: "Disponible"
|
||||
unavailable: "No disponible"
|
||||
usernameInvalidFormat: "utiliza letras, números y/o -."
|
||||
tooShort: "Demasiado corto"
|
||||
tooLong: "Demasiado largo"
|
||||
weakPassword: "Contraseña débil"
|
||||
normalPassword: "Buena contraseña"
|
||||
strongPassword: "Muy buena contraseña"
|
||||
passwordMatched: "Correcto"
|
||||
passwordNotMatched: "Las contraseñas no son las mismas"
|
||||
signinWith: "Inicie sesión con {x}"
|
||||
tapSecurityKey: "Toque la clave de seguridad"
|
||||
or: "O"
|
||||
uiLanguage: "Idioma de visualización de la interfaz"
|
||||
_ago:
|
||||
unknown: "Desconocido"
|
||||
future: "Futuro"
|
||||
justNow: "Recién ahora"
|
||||
secondsAgo: "Hace {n} segundos"
|
||||
minutesAgo: "Hace {n} minutos"
|
||||
hoursAgo: "Hace {n} horas"
|
||||
daysAgo: "Hace {n} días"
|
||||
weeksAgo: "Hace {n} semanas"
|
||||
monthsAgo: "Hace {n} meses"
|
||||
yearsAgo: "Hace {n} años"
|
||||
_time:
|
||||
second: "Segundos"
|
||||
minute: "Minutos"
|
||||
hour: "Horas"
|
||||
day: "Días"
|
||||
_tutorial:
|
||||
title: "Cómo usar Misskey"
|
||||
step1_1: "Bienvenido"
|
||||
|
@ -1 +1,820 @@
|
||||
---
|
||||
_lang_: "Français"
|
||||
monthAndDay: "{day}/{month}"
|
||||
search: "Rechercher"
|
||||
notifications: "Notifications"
|
||||
username: "Nom d'utilisateur·rice"
|
||||
password: "Mot de passe"
|
||||
fetchingAsApObject: "Récupération depuis le fédiverse"
|
||||
ok: "D'accord"
|
||||
gotIt: "J'ai compris !"
|
||||
cancel: "Annuler"
|
||||
enterUsername: "Entrer un nom d'utilisateur·rice"
|
||||
renotedBy: "Renoté par {user}"
|
||||
noNotes: "Pas de notes"
|
||||
noNotifications: "Pas de notifications"
|
||||
instance: "Instance"
|
||||
settings: "Paramètres"
|
||||
profile: "Profil"
|
||||
timeline: "Fil d'actualité"
|
||||
noAccountDescription: "L'utilisateur·rice n'a pas renseigné de présentation sur son profil"
|
||||
login: "Se connecter"
|
||||
loggingIn: "Connexion en cours"
|
||||
logout: "Se déconnecter"
|
||||
signup: "S'enregistrer"
|
||||
uploading: "Envoi en cours"
|
||||
save: "Enregistrer"
|
||||
users: "Utilisateur·rice·s"
|
||||
addUser: "Ajouter un·e utilisateur·rice"
|
||||
favorite: "Ajouter aux favoris"
|
||||
favorites: "Favoris"
|
||||
unfavorite: "Retirer des favoris"
|
||||
pin: "Épingler sur le profil"
|
||||
unpin: "Désépingler"
|
||||
copyContent: "Copier le contenu"
|
||||
copyLink: "Copier le lien"
|
||||
delete: "Supprimer"
|
||||
addToList: "Ajouter à une liste"
|
||||
sendMessage: "Envoyer un message"
|
||||
copyUsername: "Copier le nom d'utilisateur"
|
||||
reply: "Répondre"
|
||||
loadMore: "Voir plus"
|
||||
youGotNewFollower: "Vous a suivi"
|
||||
receiveFollowRequest: "Demande de suivi reçue"
|
||||
followRequestAccepted: "Suivre la demande acceptée"
|
||||
mentions: "Mentions"
|
||||
directNotes: "Messages directs"
|
||||
importAndExport: "Import et export"
|
||||
import: "Importer"
|
||||
export: "Exporter"
|
||||
files: "Fichier·s"
|
||||
download: "Télécharger"
|
||||
driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées."
|
||||
unfollowConfirm: "Êtes-vous sûr·e ne plus vouloir suivre {name} ?"
|
||||
exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive."
|
||||
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
|
||||
lists: "Listes"
|
||||
noLists: "Aucune liste"
|
||||
note: "Note"
|
||||
notes: "Notes"
|
||||
following: "Abonnements"
|
||||
followers: "Abonné·e·s"
|
||||
followsYou: "Votre abonné"
|
||||
createList: "Créer une liste"
|
||||
manageLists: "Gérer les listes"
|
||||
error: "Une erreur est survenue"
|
||||
retry: "Réessayer"
|
||||
enterListName: "Nom de la liste"
|
||||
privacy: "Vie privée"
|
||||
makeFollowManuallyApprove: "Demandes d’abonnements requiert l’approbation"
|
||||
defaultNoteVisibility: "Visibilité par défaut"
|
||||
follow: "Abonnement"
|
||||
followRequest: "Demande d’abonnement"
|
||||
followRequests: "Demandes d’abonnement"
|
||||
unfollow: "Se désabonner"
|
||||
followRequestPending: "En attente d’approbation"
|
||||
enterEmoji: "ou entrez un émoji"
|
||||
renote: "Renote"
|
||||
unrenote: "Annuler Renote"
|
||||
quote: "Citation"
|
||||
pinnedNote: "Note épinglée"
|
||||
you: "Vous"
|
||||
clickToShow: "Cliquer pour afficher"
|
||||
sensitive: "Contenu sensible"
|
||||
add: "Ajouter"
|
||||
reaction: "Réactions"
|
||||
reactionSettingDescription: "Personnaliser les émojis à afficher dans le sélecteur de réactions, délimités par les sauts de ligne."
|
||||
rememberNoteVisibility: "Se souvenir de la visibilité des notes"
|
||||
renameFile: "Renommer le ficher"
|
||||
attachCancel: "Enlever le fichier attaché"
|
||||
markAsSensitive: "Marquer comme sensible"
|
||||
unmarkAsSensitive: "Enlever le marquage comme sensible"
|
||||
enterFileName: "Entrer le nom du fichier"
|
||||
mute: "Mettre en sourdine"
|
||||
unmute: "Enlever la sourdine"
|
||||
block: "Bloquer"
|
||||
unblock: "Débloquer"
|
||||
suspend: "Suspendre"
|
||||
unsuspend: "Annuler la suspension"
|
||||
blockConfirm: "Désirez-vous bloquer ce compte ?"
|
||||
unblockConfirm: "Désirez-vous débloquer ce compte ?"
|
||||
suspendConfirm: "Désirez-vous suspendre ce compte ?"
|
||||
unsuspendConfirm: "Désirez-vous annuler la suspension de ce compte ?"
|
||||
selectList: "Sélectionner une liste"
|
||||
customEmojis: "Émojis personnalisés"
|
||||
emojiName: "Nom de l’émoji"
|
||||
emojiUrl: "URL de l’émoji"
|
||||
addEmoji: "Ajouter un émoji"
|
||||
cacheRemoteFiles: "Mettre en cache des fichiers distants"
|
||||
cacheRemoteFilesDescription: "Quand ce paramètre est désactivé, les fichiers distants sont chargés directement de l'instance distante. Désactiver cela diminuera l'utilisation du stockage mais augmentera le trafic parce les miniatures ne seront pas générées."
|
||||
flagAsBot: "Ce compte est un robot"
|
||||
flagAsCat: "Ce compte est un chat"
|
||||
autoAcceptFollowed: "Approuver automatiquement les abonnements des utilisateurs abonné·e·s"
|
||||
addAcount: "Ajouter un compte"
|
||||
loginFailed: "Échec de la connexion"
|
||||
showOnRemote: "Voir sur l'instance distante"
|
||||
general: "Général"
|
||||
wallpaper: "Arrière plan"
|
||||
removeWallpaper: "Supprimer l'arrière plan"
|
||||
searchWith: "Recherche : {q}"
|
||||
youHaveNoLists: "Vous n'avez aucune liste"
|
||||
followConfirm: "Désirez-vous suivre {name} ?"
|
||||
proxyAccount: "Compte proxy"
|
||||
proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la suit. Le compte proxy va donc le·la suivre pour que ses notes soient acheminées."
|
||||
host: "Hôte"
|
||||
selectUser: "Sélectionner un·e utilisateur·rice"
|
||||
recipient: "Correspondant·e"
|
||||
annotation: "Commentaires"
|
||||
federation: "Fédération"
|
||||
instances: "Instance"
|
||||
registeredAt: "Premier contact le"
|
||||
latestRequestSentAt: "Dernière requête envoyée"
|
||||
latestRequestReceivedAt: "Dernière requête reçue"
|
||||
latestStatus: "Dernière statut"
|
||||
storageUsage: "Stockage utilisé"
|
||||
charts: "Graphiques"
|
||||
perHour: "par heure"
|
||||
perDay: "par jour"
|
||||
stopActivityDelivery: "Arrêter l'envoi d'activités"
|
||||
blockThisInstance: "Bloquer cette instnce"
|
||||
operations: "Opérations"
|
||||
software: "Logiciel"
|
||||
version: "Version"
|
||||
metadata: "Métadonnées"
|
||||
withNFiles: "{n} fichier(s)"
|
||||
monitor: "Écran de contrôle"
|
||||
jobQueue: "File d’attente"
|
||||
cpuAndMemory: "Processeur et mémoire"
|
||||
network: "Réseau"
|
||||
disk: "Disque"
|
||||
instanceInfo: "Informations sur l'instance"
|
||||
statistics: "Statistiques"
|
||||
clearQueue: "Vider la file d'attente"
|
||||
clearQueueConfirmTitle: "Êtes-vous sûr·e de vouloir vider la file d'attente ?"
|
||||
clearQueueConfirmText: "Les notes non distribuées ne seront pas livrées. Normalement, vous n'avez PAS besoin d'effectuer cette opération."
|
||||
clearCachedFiles: "Vider le cache"
|
||||
clearCachedFilesConfirm: "Êtes-vous sûr·e de vouloir vider le cache de fichiers distants ?"
|
||||
blockedInstances: "Instances bloquées"
|
||||
blockedInstancesDescription: "Listez les instance que vous désirez bloquer, une par ligne. Ces instances bloquées ne seront pas capable d'interagir avec cette instance."
|
||||
muteAndBlock: "Masqués / Bloqués"
|
||||
mutedUsers: "Utilisateur·rice·s en sourdine"
|
||||
blockedUsers: "Utilisateur·rice·s bloqué·e·s"
|
||||
noUsers: "Il n'y a aucun utilisateur·rice"
|
||||
editProfile: "Modifier votre profil"
|
||||
noteDeleteConfirm: "Confirmez-vous la suppression de cette note ?"
|
||||
pinLimitExceeded: "Je ne peux plus épingler"
|
||||
done: "Terminé"
|
||||
processing: "Traitement en cours"
|
||||
preview: "Prévisualisation"
|
||||
noCustomEmojis: "Il a pas d’émoji"
|
||||
customEmojisOfRemote: "Émojis l'instance distante"
|
||||
noJobs: "Il n'y a aucune tâche planifiée"
|
||||
federating: "En cours de fédération"
|
||||
blocked: "Bloqué"
|
||||
suspended: "Suspendu"
|
||||
all: "Tous"
|
||||
subscribing: "Abonné"
|
||||
publishing: "Publié"
|
||||
notResponding: "Ne répond pas"
|
||||
instanceFollowing: "Abonnements une instance"
|
||||
instanceFollowers: "Abonné·e·s de l'instance"
|
||||
instanceUsers: "Utilisateur·e·s de l'instance"
|
||||
changePassword: "Modifier votre mot de passe"
|
||||
security: "Sécurité"
|
||||
retypedNotMatch: "Les saisies ne correspondent pas."
|
||||
currentPassword: "Mot de passe actuel"
|
||||
newPassword: "Nouveau mot de passe"
|
||||
newPasswordRetype: "Nouveau mot de passe (répéter)"
|
||||
attachFile: "Joindre un fichier"
|
||||
more: "Plus !"
|
||||
featured: "Surlignage"
|
||||
usernameOrUserId: "Nom d'utilisateur ou ID utilisateur"
|
||||
noSuchUser: "Utilisateur non trouvé"
|
||||
lookup: "Recherche"
|
||||
announcements: "Annonces"
|
||||
imageUrl: "URL de l’image"
|
||||
remove: "Supprimer"
|
||||
removed: "Supprimé"
|
||||
removeAreYouSure: "Supprimer «{x}» ?"
|
||||
saved: "Enregistré"
|
||||
messaging: "Discuter"
|
||||
upload: "Téléchargez"
|
||||
fromDrive: "Depuis le Drive"
|
||||
fromUrl: "De l'URL"
|
||||
explore: "Découvrir"
|
||||
games: "Jeux de Misskey"
|
||||
messageRead: "Lus"
|
||||
recentUsedEmojis: "Emoji récemment utilisé"
|
||||
noMoreHistory: "Plus d'histoire passée"
|
||||
startMessaging: "Commencer à écrire un discutez"
|
||||
nUsersRead: "{n} personnes ont lu"
|
||||
agreeTo: "D'accord {0}"
|
||||
tos: "Conditions d'utilisation"
|
||||
start: "Commencer"
|
||||
home: "Principal"
|
||||
remoteUserCaution: "Les informations sont incomplètes en raison de l'utilisateur distant."
|
||||
activity: "Activités"
|
||||
images: "Images"
|
||||
birthday: "Date de naissance"
|
||||
yearsOld: "{age} ans"
|
||||
registeredDate: "Date de création"
|
||||
location: "Localisation"
|
||||
theme: "Thème"
|
||||
lightThemes: "Thème lumineux"
|
||||
darkThemes: "Thème sombre"
|
||||
drive: "Drive"
|
||||
selectFile: "Choisir le fichier"
|
||||
selectFiles: "Choisir le fichiers"
|
||||
renameFolder: "Renommer le dossier"
|
||||
createFolder: "Créer un dossier"
|
||||
deleteFolder: "Supprimer le dossier"
|
||||
addFile: "Ajoutez un fichier"
|
||||
emptyDrive: "Le Drive est vide"
|
||||
emptyFolder: "Le dossier est vide"
|
||||
copyUrl: "Copier l’URL"
|
||||
rename: "Renommer"
|
||||
avatar: "Avatar"
|
||||
banner: "Bannière"
|
||||
nsfw: "Contenu sensible"
|
||||
disconnectedFromServer: "Déconnecté du serveur"
|
||||
reloadConfirm: "Voulez-vous recharger?"
|
||||
watch: "Surveiller"
|
||||
unwatch: "Ne plus surveiller"
|
||||
accept: "Autoriser"
|
||||
reject: "Refuser"
|
||||
instanceName: "Nom de l’instance"
|
||||
instanceDescription: "Description de l’instance"
|
||||
maintainerName: "Nom d'administrateur"
|
||||
maintainerEmail: "Email de l'administrateur"
|
||||
tosUrl: "URL des conditions d'utilisation"
|
||||
thisYear: "Cette année"
|
||||
thisMonth: "Ce mois-ci"
|
||||
today: "Aujourd'hui"
|
||||
dayX: "{day} jour"
|
||||
monthX: "{month} mois"
|
||||
yearX: "{year} année"
|
||||
pages: "Pages"
|
||||
integration: "Intégrations"
|
||||
connectSerice: "Connecter"
|
||||
disconnectSerice: "Déconnecter"
|
||||
enableLocalTimeline: "Activer le fil local"
|
||||
enableGlobalTimeline: "Activer le fil global"
|
||||
disablingTimelinesInfo: "Si vous désactivez ces le fils, les administrateurs et les modérateurs pourront toujours y accéder pour plus de commodité."
|
||||
registration: "S'inscrire"
|
||||
enableRegistration: "Autoriser n’importe qui à s’enregistrés"
|
||||
invite: "Inviter"
|
||||
proxyRemoteFiles: "Proxy fichiers distants"
|
||||
driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local"
|
||||
driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant"
|
||||
inMb: "en mégaoctets"
|
||||
iconUrl: "URL de l'image de l'icône"
|
||||
bannerUrl: "URL de l'image de la bannière"
|
||||
basicInfo: "Informations basiques"
|
||||
pinnedUsers: "Utilisateur·rice épinglé·e"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Activation de reCAPTCHA"
|
||||
recaptchaSiteKey: "Clé du site"
|
||||
recaptchaSecretKey: "Clé secrète"
|
||||
antennas: "Antenne"
|
||||
manageAntennas: "Gestion d'antenne"
|
||||
name: "Nom"
|
||||
antennaSource: "Recevoir la source"
|
||||
antennaKeywords: "Mots clés entrants"
|
||||
antennaKeywordsDescription: "Lorsqu'il est séparé par un espace, il devient une spécification ET, et lorsqu'il est séparé par un saut de ligne, il devient une spécification OU."
|
||||
notifyAntenna: "Notifier les nouvelles notes"
|
||||
withFileAntenna: "Notes uniquement avec fichiers joints"
|
||||
serviceworker: "ServiceWorker"
|
||||
enableServiceworker: "Activer ServiceWorker"
|
||||
antennaUsersDescription: "Spécifiez les noms d'utilisateurs séparés par des sauts de ligne"
|
||||
caseSensitive: "Sensible à la casse"
|
||||
withReplies: "Y compris répondres"
|
||||
connectedTo: "Vous êtes connectés aux services suivants"
|
||||
notesAndReplies: "Notes et Répondres"
|
||||
withFiles: "Avec fichiers joints"
|
||||
silence: "Mettre en masquer"
|
||||
silenceConfirm: "Mettre l'utilisateur sous masquer ?"
|
||||
unsilenceConfirm: "Voulez-vous annuler le masquer ?"
|
||||
popularUsers: "Utilisateur·rice·s populaires"
|
||||
recentlyUpdatedUsers: "Utilisateur·rice·s actif·ve·s récemment"
|
||||
recentlyRegisteredUsers: "Utilisateur·rice·s récemment enregistrés"
|
||||
recentlyDiscoveredUsers: "Utilisateur·rice·s récemment découverts"
|
||||
exploreUsersCount: "Il y a {count} utilisateur·rice·s"
|
||||
exploreFediverse: "Explorer le Fédiverse"
|
||||
popularTags: "Mots-clés populaires"
|
||||
userList: "Listes"
|
||||
about: "Informations"
|
||||
aboutMisskey: "À propos de Misskey"
|
||||
patrons: "Supporteurs"
|
||||
administrator: "Administrateur"
|
||||
token: "Jeton"
|
||||
twoStepAuthentication: "Authentification à deux facteurs"
|
||||
moderator: "Modérateurs"
|
||||
nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s"
|
||||
securityKey: "Clé de sécurité"
|
||||
securityKeyName: "Nom de la clé"
|
||||
registerSecurityKey: "S’inscrire la clé de sécurité"
|
||||
lastUsed: "Dernier utilisé"
|
||||
unregister: "Se désinscrire"
|
||||
passwordLessLogin: "Connectez-vous sans mot de passe"
|
||||
resetPassword: "Réinitialiser mot de passe"
|
||||
newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
|
||||
post: "Notes"
|
||||
posted: "Publié !"
|
||||
autoReloadWhenDisconnected: "Rechargement automatique lorsque le serveur se déconnecte"
|
||||
autoNoteWatch: "Surveiller automatique pour les notes"
|
||||
reduceUiAnimation: "Réduire l'animation de l'interface"
|
||||
share: "Partager"
|
||||
notFound: "Non trouvé"
|
||||
notFoundDescription: "Aucune page ne correspond à l'URL spécifiée."
|
||||
uploadFolder: "Emplacement de téléversement par défaut"
|
||||
cacheClear: "Vider le cache"
|
||||
markAsReadAllNotifications: "Marquer toutes les notifications comme lues"
|
||||
markAsReadAllUnreadNotes: "Marquer toutes les notes comme lues"
|
||||
markAsReadAllTalkMessages: "Marquer toutes les discutez comme lues"
|
||||
help: "Aide"
|
||||
inputMessageHere: "Tapez ici votre message"
|
||||
close: "Fermer"
|
||||
group: "Groupe"
|
||||
groups: "Groupes"
|
||||
createGroup: "Créer un groupe"
|
||||
ownedGroups: "Groupe propriétaire"
|
||||
joinedGroups: "Membre dans les groupes"
|
||||
invites: "Inviter"
|
||||
groupName: "Nom du groupe"
|
||||
members: "Membres"
|
||||
transfer: "Transférer"
|
||||
messagingWithUser: "Discutez avec les utilisateurs"
|
||||
messagingWithGroup: "Discuter en groupe"
|
||||
title: "Titre"
|
||||
text: "Texte"
|
||||
enable: "Activer"
|
||||
next: "Suivant"
|
||||
retype: "Retapez"
|
||||
noteOf: "{user} notes"
|
||||
inviteToGroup: "Inviter au groupe"
|
||||
maxNoteTextLength: "Limite de note caractères"
|
||||
quoteAttached: "Avec citation"
|
||||
quoteQuestion: "Souhaitez-vous ajoutez une citation ?"
|
||||
noMessagesYet: "Pas encore discuté"
|
||||
newMessageExists: "Vous avez un nouveau message"
|
||||
onlyOneFileCanBeAttached: "Vous ne pouvez joindre qu'un seul fichier au message"
|
||||
signinRequired: "Veuillez vous connecter"
|
||||
invitationCode: "Code d’invitation"
|
||||
checking: "Vérification"
|
||||
available: "Disponible"
|
||||
unavailable: "Non disponible"
|
||||
usernameInvalidFormat: "Vous pouvez utiliser des lettres, des nombres et _"
|
||||
tooShort: "Est trop court"
|
||||
tooLong: "Est trop long"
|
||||
weakPassword: "Faible mot de passe"
|
||||
normalPassword: "Bon mot de passe"
|
||||
strongPassword: "Fort mot de passe"
|
||||
passwordMatched: "Correcte"
|
||||
passwordNotMatched: "Ne correspond pas"
|
||||
signinWith: "Connectez-vous avec {x}"
|
||||
tapSecurityKey: "Touchez la clé de sécurité"
|
||||
or: "OU"
|
||||
uiLanguage: "Langue d'affichage de l'interface"
|
||||
_ago:
|
||||
unknown: "Inconnu"
|
||||
future: "Futur"
|
||||
justNow: "à l’instant"
|
||||
secondsAgo: "Il y a {n}s"
|
||||
minutesAgo: "Il y a {n}min"
|
||||
hoursAgo: "Il y a {n} heures"
|
||||
daysAgo: "Il y a {n} jours"
|
||||
weeksAgo: "Il y a {n} semaines"
|
||||
monthsAgo: "Il y a {n} mois"
|
||||
yearsAgo: "Il y a {n} ans"
|
||||
_time:
|
||||
second: "s"
|
||||
minute: "min"
|
||||
hour: "h"
|
||||
day: "j"
|
||||
_tutorial:
|
||||
title: "Comment utiliser Misskey"
|
||||
_2fa:
|
||||
alreadyRegistered: "Cette étape à déjà été complétée"
|
||||
registerDevice: "S’inscrire l'appareil"
|
||||
registerKey: "S’inscrire la clé"
|
||||
step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil."
|
||||
step2: "Ensuite, scannez le code QR affiché avec l'application."
|
||||
step3: "Entrez le jeton affiché sur l'application et vous avez terminé."
|
||||
step4: "Lorsque vous vous connectez, entrez le jeton de la même manière."
|
||||
securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion avec non seulement la clé de sécurité matérielle qui prend en charge FIDO2, mais également l'authentification par empreinte digitale ou PIN sur votre appareil."
|
||||
_permissions:
|
||||
"read:account": "Afficher les informations du compte"
|
||||
"write:account": "Mettre à jour les informations de votre compte"
|
||||
"read:blocks": "Voir les blocs"
|
||||
"write:blocks": "Écrire des blocs"
|
||||
"read:drive": "Parcourir le Drive"
|
||||
"write:drive": "Écrire sur le Drive"
|
||||
"read:favorites": "Afficher les favoris"
|
||||
"write:favorites": "Écrire des favoris"
|
||||
"read:following": "Voir les informations de l'abonné"
|
||||
"write:following": "Abonnements/Se désabonner"
|
||||
"read:messaging": "Cherche à discuter"
|
||||
"write:messaging": "Contrôler le discuter"
|
||||
"read:mutes": "Voir les comptes muets"
|
||||
"write:mutes": "Gérer les comptes muets"
|
||||
"write:notes": "Créer / supprimer des notes"
|
||||
"read:notifications": "Afficher les notifications"
|
||||
"write:notifications": "Gérer vos notifications"
|
||||
"read:reactions": "Lire les réactions"
|
||||
"write:reactions": "Gérer vos réactions"
|
||||
"write:votes": "Voter"
|
||||
"read:pages": "Afficher la page"
|
||||
"write:pages": "Mettre à jour les Pages"
|
||||
"read:page-likes": "Lire les favoris sur les Pages"
|
||||
"write:page-likes": "Mettre à jour les favoris sur les Pages"
|
||||
"read:user-groups": "Voir les groupes d'utilisateur·rice·s"
|
||||
"write:user-groups": "Éditer les groupes des utilisateur·rice·s"
|
||||
_auth:
|
||||
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
|
||||
permissionAsk: "Cette application nécessite les autorisations suivantes "
|
||||
_antennaSources:
|
||||
all: "Toutes les notes"
|
||||
homeTimeline: "Notes de l'utilisateur auquel je m'abonne"
|
||||
users: "Notes des un ou plusieurs utilisateurs spécifiés"
|
||||
userList: "Notes pour les utilisateurs de la liste spécifiée"
|
||||
_weekday:
|
||||
sunday: "Dimanche"
|
||||
monday: "Lundi"
|
||||
tuesday: "Mardi"
|
||||
wednesday: "Mercredi"
|
||||
thursday: "Jeudi"
|
||||
friday: "Vendredi"
|
||||
saturday: "Samedi"
|
||||
_widgets:
|
||||
memo: "Note collante"
|
||||
notifications: "Notifications"
|
||||
timeline: "Fil d'actualité"
|
||||
calendar: "Calendrier"
|
||||
trends: "Tendances"
|
||||
clock: "Horloge"
|
||||
rss: "Lecteur de flux RSS"
|
||||
_cw:
|
||||
hide: "Masquer"
|
||||
show: "Voir plus"
|
||||
chars: "{count} caractères"
|
||||
files: "{count} fichiers"
|
||||
poll: "Sondage"
|
||||
_poll:
|
||||
noOnlyOneChoice: "Au moins 2 réponses nécéssaires"
|
||||
choiceN: "Choix {n}"
|
||||
noMore: "Vous ne pouvez pas en ajouter davantage"
|
||||
canMultipleVote: "Autoriser le multi-choix"
|
||||
expiration: "Fin du sondage"
|
||||
infinite: "Illimité"
|
||||
at: "Choisir une date"
|
||||
after: "Chosir une durée"
|
||||
deadlineDate: "Date de fin"
|
||||
deadlineTime: "Heure de fin"
|
||||
duration: "Durée"
|
||||
votesCount: "{n} votes"
|
||||
totalVotes: "{n} votes au total"
|
||||
vote: "Voter"
|
||||
showResult: "Voir les résultats"
|
||||
voted: "Déjà voté"
|
||||
closed: "Terminé"
|
||||
remainingDays: "{d} jours, {h} heures restantes"
|
||||
remainingHours: "{h} heures et {m} minutes restantes"
|
||||
remainingMinutes: "{m} minutes et {s} secondes restantes"
|
||||
remainingSeconds: "{s} secondes restantes"
|
||||
_visibility:
|
||||
public: "Public"
|
||||
publicDescription: "Publier à tou·te·s les utilisateur·rice·s"
|
||||
home: "Principal"
|
||||
homeDescription: "Publier sur le fil principal uniquement"
|
||||
followers: "Abonné·e·s"
|
||||
followersDescription: "Publier à vos abonné·e·s uniquement"
|
||||
specified: "Direct"
|
||||
specifiedDescription: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
|
||||
localOnly: "Local seulement"
|
||||
_postForm:
|
||||
replyPlaceholder: "Répondre à cette note ..."
|
||||
quotePlaceholder: "Citez cette note ..."
|
||||
_placeholders:
|
||||
a: "Qu'est-ce qu'il se passe ?"
|
||||
b: "Quoi de neuf ?"
|
||||
c: "Qu’avez-vous en tête ?"
|
||||
d: "Désirez-vous publier quelques mots ?"
|
||||
e: "Écrivez ici"
|
||||
f: "En attente de vos écrits ..."
|
||||
_profile:
|
||||
name: "Nom"
|
||||
username: "Nom d'utilisateur·rice"
|
||||
description: "À propos de moi"
|
||||
youCanIncludeHashtags: "Vous pouvez également inclure des hashtags."
|
||||
metadata: "Informations complémentaires"
|
||||
metadataLabel: "Étiquette"
|
||||
metadataContent: "Contenu"
|
||||
_exportOrImport:
|
||||
allNotes: "Toutes les notes"
|
||||
followingList: "Abonnements"
|
||||
muteList: "Mettre en sourdine"
|
||||
blockingList: "Bloquer"
|
||||
userLists: "Listes"
|
||||
_charts:
|
||||
federationInstancesIncDec: "Variation du nombre d'instances"
|
||||
federationInstancesTotal: "Nombre d'instances au total"
|
||||
usersIncDec: "Variation du nombre d'utilisateur·rice·s"
|
||||
usersTotal: "Nombre d'utilsateur·rice·s au total"
|
||||
activeUsers: "Utilisateur·rice·s actif·ve·s"
|
||||
notesIncDec: "Variation du nombre d'notes"
|
||||
localNotesIncDec: "Variation du nombre de notes local"
|
||||
remoteNotesIncDec: "Variation du nombre d’notes distant"
|
||||
notesTotal: "Nombre d'notes au total"
|
||||
filesIncDec: "Variation du nombre de fichiers"
|
||||
filesTotal: "Nombre de fichiers au total"
|
||||
storageUsageIncDec: "Variation de l'utilisation du stockage"
|
||||
storageUsageTotal: "Utilisation totale du stockage"
|
||||
_instanceCharts:
|
||||
requests: "Requêtes"
|
||||
users: "Variation du nombre d'utilisateur·rice·s"
|
||||
usersTotal: "Somme du nombre d'utilisateur·rice·s accumulés"
|
||||
notes: "Variation du nombre d'notes"
|
||||
notesTotal: "Somme du nombre d’notes accumulés"
|
||||
ff: "Variation des abonné·e·s"
|
||||
ffTotal: "Somme du nombre d'abonnements accumulés"
|
||||
cacheSize: "Variation de la taille du cache"
|
||||
cacheSizeTotal: "Somme de la taille du cache accumulé"
|
||||
files: "Variation du nombre de fichiers"
|
||||
filesTotal: "Somme du nombre de fichiers accumulés"
|
||||
_timelines:
|
||||
home: "Principal"
|
||||
local: "Local"
|
||||
social: "Social"
|
||||
global: "Global"
|
||||
_pages:
|
||||
newPage: "Créer une page"
|
||||
editPage: "Modifier une page"
|
||||
readPage: "Voir la source"
|
||||
page-created: "Page a été créée !"
|
||||
page-updated: "A mis à jour la page"
|
||||
name-already-exists: "Une page portant le même nom existe déjà"
|
||||
title-invalid-name: "L’URL de la page spécifiée n’est pas valide"
|
||||
text-invalid-name: "Assurez-vous qu'il n'est pas vide"
|
||||
editThisPage: "Éditer cette page"
|
||||
viewSource: "Afficher la source"
|
||||
viewPage: "Afficher la page"
|
||||
like: "Favori"
|
||||
unlike: "Je n’favoris pas"
|
||||
liked-pages: "Pages favoris"
|
||||
my-pages: "Mes pages"
|
||||
inspector: "Inspecteur"
|
||||
content: "Bloc de page"
|
||||
variables: "Variables"
|
||||
more-details: "Description"
|
||||
title: "Titre"
|
||||
url: "URL de page"
|
||||
summary: "Résumé de page"
|
||||
alignCenter: "Centrée"
|
||||
hide-title-when-pinned: "Masquer le titre de la page lorsque celle-ci est épinglée au profil"
|
||||
font: "Police de caractères"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
set-eye-catching-image: "Définir une image attirante"
|
||||
remove-eye-catching-image: "Supprimer une image attirante"
|
||||
chooseBlock: "Ajouter un bloc"
|
||||
selectType: "Choisir un type"
|
||||
enterVariableName: "Veuillez entrer un nom de variable"
|
||||
the-variable-name-is-already-used: "Cette variable est déjà utilisée"
|
||||
content-blocks: "Contenu"
|
||||
input-blocks: "Entrée"
|
||||
special-blocks: "Spécial"
|
||||
post-from-post-form: "Publier ce contenu"
|
||||
posted-from-post-form: "Publié !"
|
||||
blocks:
|
||||
text: "Texte"
|
||||
textarea: "Zone de texte"
|
||||
section: "Section"
|
||||
image: "Images"
|
||||
button: "Bouton"
|
||||
if: "Si"
|
||||
_if:
|
||||
variable: "Variables"
|
||||
post: "Formulaire à publier"
|
||||
_post:
|
||||
text: "Contenu"
|
||||
textInput: "Entrée de textuelle"
|
||||
_textInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
textareaInput: "Entrée de textuelle multiligne"
|
||||
_textareaInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
numberInput: "Entrée numérique"
|
||||
_numberInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
switch: "Basculer"
|
||||
_switch:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
counter: "Compteur"
|
||||
_counter:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
inc: "Augmenter le chiffre"
|
||||
_button:
|
||||
text: "Titre"
|
||||
colored: "Coloré"
|
||||
action: "L'opération lorsque le bouton sera pressé"
|
||||
_action:
|
||||
dialog: "Afficher une fenêtre de dialogue"
|
||||
_dialog:
|
||||
content: "Contenu"
|
||||
resetRandom: "Réinitialiser le nombre aléatoire"
|
||||
pushEvent: "Envoyer un évènement"
|
||||
_pushEvent:
|
||||
event: "Nom de l'évènement"
|
||||
message: "Message à afficher lorsque appuyé"
|
||||
variable: "Variable à envoyer"
|
||||
no-variable: "Rien"
|
||||
radioButton: "Choix"
|
||||
_radioButton:
|
||||
name: "Nom de la variable"
|
||||
title: "Titre"
|
||||
values: "Choix séparés par des sauts de ligne"
|
||||
default: "Valeur par défaut"
|
||||
script:
|
||||
categories:
|
||||
flow: "Contrôle"
|
||||
logical: "Opération logique"
|
||||
operation: "Calculer"
|
||||
comparison: "Comparer"
|
||||
random: "Aléatoire"
|
||||
value: "Valeur"
|
||||
fn: "Fonction"
|
||||
text: "Manipulation de texte"
|
||||
convert: "Convertir"
|
||||
list: "Listes"
|
||||
blocks:
|
||||
text: "Texte"
|
||||
multiLineText: "Texte (Multi-lignes)"
|
||||
textList: "Liste de texte"
|
||||
_textList:
|
||||
info: "Veuillez séparer chacun avec une nouvelle ligne"
|
||||
strLen: "Longueur d'un texte"
|
||||
_strLen:
|
||||
arg1: "Texte"
|
||||
strPick: "Extraire un caractère"
|
||||
_strPick:
|
||||
arg1: "Texte"
|
||||
arg2: "Position du joueur"
|
||||
strReplace: "Remplacement de texte"
|
||||
_strReplace:
|
||||
arg1: "Texte"
|
||||
arg2: "Avant le remplacement"
|
||||
arg3: "Après le remplacement"
|
||||
strReverse: "Inverser le texte"
|
||||
_strReverse:
|
||||
arg1: "Texte"
|
||||
join: "Concaténer du texte"
|
||||
_join:
|
||||
arg1: "Listes"
|
||||
arg2: "Séparateur"
|
||||
add: "Ajouter"
|
||||
_add:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
subtract: "Soustraire"
|
||||
_subtract:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
multiply: "Multiplier par"
|
||||
_multiply:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
divide: "Diviser par"
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
mod: "Reste"
|
||||
_mod:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
round: "Décimal rond"
|
||||
_round:
|
||||
arg1: "Numérique"
|
||||
eq: "A et B sont équivalents"
|
||||
_eq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
notEq: "A et B sont différents"
|
||||
_notEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
and: "A et B"
|
||||
_and:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
or: "A ou B"
|
||||
_or:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
lt: "A est plus petit que B"
|
||||
_lt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gt: "A est supérieur à B"
|
||||
_gt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
ltEq: "A est plus petit ou égal à B"
|
||||
_ltEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gtEq: "A est supérieur ou égal à B"
|
||||
_gtEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
if: "Branche"
|
||||
_if:
|
||||
arg1: "Si"
|
||||
arg2: "Si"
|
||||
arg3: "Sinon"
|
||||
not: "Nier"
|
||||
_not:
|
||||
arg1: "Nier"
|
||||
random: "Aléatoire"
|
||||
_random:
|
||||
arg1: "Probabilité"
|
||||
rannum: "Nombre aléatoire"
|
||||
_rannum:
|
||||
arg1: "Minimum"
|
||||
arg2: "Maximum"
|
||||
randomPick: "Sélectionner au hasard dans la liste"
|
||||
_randomPick:
|
||||
arg1: "Listes"
|
||||
dailyRandom: "Aléatoire (Quotidien pour chaque utilisateur)"
|
||||
_dailyRandom:
|
||||
arg1: "Probabilité"
|
||||
dailyRannum: "Numéros aléatoires (Quotidien pour chaque utilisateur)"
|
||||
_dailyRannum:
|
||||
arg1: "Minimum"
|
||||
arg2: "Maximum"
|
||||
dailyRandomPick: "Sélectionné au hasard dans la liste (Quotidien pour chaque utilisateur)"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listes"
|
||||
seedRandom: "Aléatoire (graine)"
|
||||
_seedRandom:
|
||||
arg1: "Graine"
|
||||
arg2: "Probabilité"
|
||||
seedRannum: "Nombre aléatoire (Graine)"
|
||||
_seedRannum:
|
||||
arg1: "Graine"
|
||||
arg2: "Minimum"
|
||||
arg3: "Maximum"
|
||||
seedRandomPick: "Sélectionné au hasard dans la liste (graine)"
|
||||
_seedRandomPick:
|
||||
arg1: "Graine"
|
||||
arg2: "Listes"
|
||||
DRPWPM: "Sélectionné au hasard dans une liste de probabilités (Quotidien pour chaque utilisateur)"
|
||||
_DRPWPM:
|
||||
arg1: "Liste de texte"
|
||||
pick: "Sélectionner dans la liste"
|
||||
_pick:
|
||||
arg1: "Listes"
|
||||
arg2: "Position"
|
||||
listLen: "Longueur de la liste"
|
||||
_listLen:
|
||||
arg1: "Listes"
|
||||
number: "Numérique"
|
||||
stringToNumber: "Convertir du texte en numérique"
|
||||
_stringToNumber:
|
||||
arg1: "Texte"
|
||||
numberToString: "Convertir du numérique en texte"
|
||||
_numberToString:
|
||||
arg1: "Numérique"
|
||||
splitStrByLine: "Séparer le texte par lignes"
|
||||
_splitStrByLine:
|
||||
arg1: "Texte"
|
||||
ref: "Variables"
|
||||
fn: "Fonction"
|
||||
_fn:
|
||||
slots: "Slots"
|
||||
slots-info: "Veuillez délimiter chaque slot par un saut de ligne"
|
||||
arg1: "Sortie"
|
||||
for: "Répéter"
|
||||
_for:
|
||||
arg1: "Compter"
|
||||
arg2: "Action"
|
||||
typeError: "Le slot {slot} accepte \"{expect}\" mais a \"{actual}\" !"
|
||||
thereIsEmptySlot: "Slot {slot} est vide !"
|
||||
types:
|
||||
string: "Texte"
|
||||
number: "Numérique"
|
||||
boolean: "Marqueur"
|
||||
array: "Listes"
|
||||
stringArray: "Liste de texte"
|
||||
emptySlot: "Slot vide"
|
||||
enviromentVariables: "Variables d'environnement"
|
||||
pageVariables: "Élément de page"
|
||||
argVariables: "Entrée slot"
|
||||
|
@ -19,9 +19,9 @@ const languages = [
|
||||
//'de-DE',
|
||||
'en-US',
|
||||
'es-ES',
|
||||
//'fr-FR',
|
||||
'fr-FR',
|
||||
'ja-JP',
|
||||
//'ja-KS',
|
||||
'ja-KS',
|
||||
'ko-KR',
|
||||
//'nl-NL',
|
||||
//'pl-PL',
|
||||
|
@ -1,20 +1,4 @@
|
||||
_ago:
|
||||
unknown: "謎"
|
||||
future: "未来"
|
||||
justNow: "たった今"
|
||||
secondsAgo: "{n}秒前"
|
||||
minutesAgo: "{n}分前"
|
||||
hoursAgo: "{n}時間前"
|
||||
daysAgo: "{n}日前"
|
||||
weeksAgo: "{n}週間前"
|
||||
monthsAgo: "{n}ヶ月前"
|
||||
yearsAgo: "{n}年前"
|
||||
|
||||
_time:
|
||||
second: "秒"
|
||||
minute: "分"
|
||||
hour: "時間"
|
||||
day: "日"
|
||||
_lang_: "日本語"
|
||||
|
||||
introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスです。\n「ノート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
@ -327,6 +311,7 @@ aboutMisskey: "Misskeyについて"
|
||||
aboutMisskeyText: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。"
|
||||
misskeyMembers: "現在以下のメンバーによって開発・メンテナンスされています:"
|
||||
misskeySource: "ソースコードはここで公開されています:"
|
||||
misskeyTranslation: "Misskeyの翻訳にご協力をお願いします:"
|
||||
misskeyDonate: "Misskeyに寄付をして開発をサポートできます:"
|
||||
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰"
|
||||
patrons: "支援者"
|
||||
@ -371,12 +356,57 @@ members: "メンバー"
|
||||
transfer: "譲渡"
|
||||
messagingWithUser: "ユーザーとチャット"
|
||||
messagingWithGroup: "グループでチャット"
|
||||
title: "タイトル"
|
||||
text: "テキスト"
|
||||
enable: "有効にする"
|
||||
next: "次"
|
||||
retype: "再入力"
|
||||
noteOf: "{user}のノート"
|
||||
inviteToGroup: "グループに招待"
|
||||
maxNoteTextLength: "ノートの文字数制限"
|
||||
quoteAttached: "引用付き"
|
||||
quoteQuestion: "引用として添付しますか?"
|
||||
noMessagesYet: "まだチャットはありません"
|
||||
newMessageExists: "新しいメッセージがあります"
|
||||
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
|
||||
signinRequired: "ログインしてください"
|
||||
invitationCode: "招待コード"
|
||||
checking: "確認しています"
|
||||
available: "利用できます"
|
||||
unavailable: "利用できません"
|
||||
usernameInvalidFormat: "a~z、A~Z、0~9、_が使えます"
|
||||
tooShort: "短すぎます"
|
||||
tooLong: "長すぎます"
|
||||
weakPassword: "弱いパスワード"
|
||||
normalPassword: "普通のパスワード"
|
||||
strongPassword: "強いパスワード"
|
||||
passwordMatched: "一致しました"
|
||||
passwordNotMatched: "一致していません"
|
||||
signinWith: "{x}でログイン"
|
||||
tapSecurityKey: "セキュリティーキーにタッチ"
|
||||
or: "もしくは"
|
||||
uiLanguage: "UIの表示言語"
|
||||
groupInvited: "グループに招待されました"
|
||||
aboutX: "{x}について"
|
||||
useOsNativeEmojis: "OSネイティブの絵文字を使用"
|
||||
|
||||
_ago:
|
||||
unknown: "謎"
|
||||
future: "未来"
|
||||
justNow: "たった今"
|
||||
secondsAgo: "{n}秒前"
|
||||
minutesAgo: "{n}分前"
|
||||
hoursAgo: "{n}時間前"
|
||||
daysAgo: "{n}日前"
|
||||
weeksAgo: "{n}週間前"
|
||||
monthsAgo: "{n}ヶ月前"
|
||||
yearsAgo: "{n}年前"
|
||||
|
||||
_time:
|
||||
second: "秒"
|
||||
minute: "分"
|
||||
hour: "時間"
|
||||
day: "日"
|
||||
|
||||
_tutorial:
|
||||
title: "Misskeyの使い方"
|
||||
|
@ -1,20 +1,5 @@
|
||||
---
|
||||
_ago:
|
||||
unknown: "謎"
|
||||
future: "未来"
|
||||
justNow: "たった今"
|
||||
secondsAgo: "{n}秒前"
|
||||
minutesAgo: "{n}分前"
|
||||
hoursAgo: "{n}時間前"
|
||||
daysAgo: "{n}日前"
|
||||
weeksAgo: "{n}週間前"
|
||||
monthsAgo: "{n}ヶ月前"
|
||||
yearsAgo: "{n}年前"
|
||||
_time:
|
||||
second: "秒"
|
||||
minute: "分"
|
||||
hour: "時間"
|
||||
day: "日"
|
||||
_lang_: "日本語 (関西弁)"
|
||||
introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作成しぃ、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素はよ反応を追加することもできます✌\n新しい世界を探検しよう🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "探す"
|
||||
@ -124,6 +109,22 @@ aboutMisskey: "Misskeyってなんや?"
|
||||
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
|
||||
close: "さいなら"
|
||||
joinedGroups: "参加しとるグループ"
|
||||
_ago:
|
||||
unknown: "謎"
|
||||
future: "未来"
|
||||
justNow: "たった今"
|
||||
secondsAgo: "{n}秒前"
|
||||
minutesAgo: "{n}分前"
|
||||
hoursAgo: "{n}時間前"
|
||||
daysAgo: "{n}日前"
|
||||
weeksAgo: "{n}週間前"
|
||||
monthsAgo: "{n}ヶ月前"
|
||||
yearsAgo: "{n}年前"
|
||||
_time:
|
||||
second: "秒"
|
||||
minute: "分"
|
||||
hour: "時間"
|
||||
day: "日"
|
||||
_2fa:
|
||||
alreadyRegistered: "もう設定終わっとるわ"
|
||||
_auth:
|
||||
|
2
locales/kn-IN.yml
Normal file
2
locales/kn-IN.yml
Normal file
@ -0,0 +1,2 @@
|
||||
---
|
||||
_lang_: "ಕನ್ನಡ"
|
@ -1,20 +1,5 @@
|
||||
---
|
||||
_ago:
|
||||
unknown: "알 수 없음"
|
||||
future: "미래"
|
||||
justNow: "방금 전"
|
||||
secondsAgo: "{n}초 전"
|
||||
minutesAgo: "{n}분 전"
|
||||
hoursAgo: "{n}시간 전"
|
||||
daysAgo: "{n}일 전"
|
||||
weeksAgo: "{n}주 전"
|
||||
monthsAgo: "{n}개월 전"
|
||||
yearsAgo: "{n}년 전"
|
||||
_time:
|
||||
second: "초"
|
||||
minute: "분"
|
||||
hour: "시간"
|
||||
day: "일"
|
||||
_lang_: "한국어"
|
||||
introMisskey: "환영합니다! Misskey 는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n\"노트\" 를 작성해서, 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n\"리액션\" 기능으로, 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀"
|
||||
monthAndDay: "{month}월 {day}일"
|
||||
search: "검색"
|
||||
@ -370,12 +355,52 @@ members: "멤버"
|
||||
transfer: "양도"
|
||||
messagingWithUser: "유저와 대화하기"
|
||||
messagingWithGroup: "그룹끼리 대화하기"
|
||||
title: "제목"
|
||||
text: "텍스트"
|
||||
enable: "사용"
|
||||
next: "다음"
|
||||
retype: "다시 입력"
|
||||
noteOf: "{user}의 노트"
|
||||
inviteToGroup: "그룹에 초대하기"
|
||||
maxNoteTextLength: "노트의 문자 수 제한"
|
||||
quoteAttached: "인용함"
|
||||
quoteQuestion: "인용해서 작성하시겠습니까?"
|
||||
noMessagesYet: "아직 대화가 없습니다"
|
||||
newMessageExists: "새 메시지가 있습니다"
|
||||
onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
|
||||
signinRequired: "로그인 해주세요"
|
||||
invitationCode: "초대 코드"
|
||||
checking: "확인하는 중입니다"
|
||||
available: "사용 가능합니다"
|
||||
unavailable: "사용할 수 없습니다"
|
||||
usernameInvalidFormat: "a~z, A~Z, 0-9, _를 사용할 수 있습니다"
|
||||
tooShort: "너무 짧습니다"
|
||||
tooLong: "너무 깁니다"
|
||||
weakPassword: "약한 비밀번호"
|
||||
normalPassword: "좋은 비밀번호"
|
||||
strongPassword: "강한 비밀번호"
|
||||
passwordMatched: "일치합니다"
|
||||
passwordNotMatched: "일치하지 않습니다"
|
||||
signinWith: "{x}로 로그인"
|
||||
tapSecurityKey: "보안 키를 터치"
|
||||
or: "혹은"
|
||||
uiLanguage: "UI 표시 언어"
|
||||
_ago:
|
||||
unknown: "알 수 없음"
|
||||
future: "미래"
|
||||
justNow: "방금 전"
|
||||
secondsAgo: "{n}초 전"
|
||||
minutesAgo: "{n}분 전"
|
||||
hoursAgo: "{n}시간 전"
|
||||
daysAgo: "{n}일 전"
|
||||
weeksAgo: "{n}주 전"
|
||||
monthsAgo: "{n}개월 전"
|
||||
yearsAgo: "{n}년 전"
|
||||
_time:
|
||||
second: "초"
|
||||
minute: "분"
|
||||
hour: "시간"
|
||||
day: "일"
|
||||
_tutorial:
|
||||
title: "Misskey의 사용 방법"
|
||||
step1_1: "환영합니다!"
|
||||
@ -407,7 +432,7 @@ _2fa:
|
||||
step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
|
||||
step3: "앱에 표시된 토큰을 입력하시면 완료됩니다."
|
||||
step4: "다음 로그인부터는 토큰을 입력해야 합니다."
|
||||
securityKeyInfo: "FIDO2를 지원하는 하드웨어 시큐리티 키 혹은 휴대전화의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
|
||||
securityKeyInfo: "FIDO2를 지원하는 하드웨어 보안 키 혹은 디바이스의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
|
||||
_permissions:
|
||||
"read:account": "계정의 정보를 봅니다"
|
||||
"write:account": "계정의 정보를 변경합니다"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Nederlands"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Norsk Bokmål"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "język polski"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Português"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Русский язык"
|
||||
|
@ -1,20 +1,5 @@
|
||||
---
|
||||
_ago:
|
||||
unknown: "未知"
|
||||
future: "未来"
|
||||
justNow: "最近"
|
||||
secondsAgo: "{n}秒前"
|
||||
minutesAgo: "{n}分前"
|
||||
hoursAgo: "{n}小时前"
|
||||
daysAgo: "{n}日前"
|
||||
weeksAgo: "{n}周前"
|
||||
monthsAgo: "{n}月前"
|
||||
yearsAgo: "{n}年前"
|
||||
_time:
|
||||
second: "秒"
|
||||
minute: "分"
|
||||
hour: "小时"
|
||||
day: "日"
|
||||
_lang_: "中文(简体)"
|
||||
introMisskey: "欢迎!Misskey是一个开源的分散型SNS服务。\n通过「帖子」来分享现在发生的事情吧!📡\n「反应」功能,可以让你快速的对大家的「帖子」来表达感情👍\n一起来探索新的世界吧!🚀"
|
||||
monthAndDay: "{month}月 {day}日"
|
||||
search: "搜索"
|
||||
@ -350,12 +335,30 @@ invites: "邀请"
|
||||
groupName: "群组名"
|
||||
members: "成员"
|
||||
transfer: "转让"
|
||||
title: "标题"
|
||||
text: "文本"
|
||||
enable: "启用"
|
||||
next: "下一个"
|
||||
retype: "重新输入"
|
||||
noteOf: "{user}的帖子"
|
||||
inviteToGroup: "群组邀请"
|
||||
maxNoteTextLength: "帖子的字数限制"
|
||||
_ago:
|
||||
unknown: "未知"
|
||||
future: "未来"
|
||||
justNow: "最近"
|
||||
secondsAgo: "{n}秒前"
|
||||
minutesAgo: "{n}分前"
|
||||
hoursAgo: "{n}小时前"
|
||||
daysAgo: "{n}日前"
|
||||
weeksAgo: "{n}周前"
|
||||
monthsAgo: "{n}月前"
|
||||
yearsAgo: "{n}年前"
|
||||
_time:
|
||||
second: "秒"
|
||||
minute: "分"
|
||||
hour: "小时"
|
||||
day: "日"
|
||||
_tutorial:
|
||||
title: "Misskey的使用方法"
|
||||
step1_1: "欢迎!"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "中文(繁体)"
|
||||
|
38
migration/1581526429287-user-group-invitation.ts
Normal file
38
migration/1581526429287-user-group-invitation.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class userGroupInvitation1581526429287 implements MigrationInterface {
|
||||
name = 'userGroupInvitation1581526429287'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "user_group_invitation" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_160c63ec02bf23f6a5c5e8140d6" PRIMARY KEY ("id"))`, undefined);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_bfbc6305547539369fe73eb144" ON "user_group_invitation" ("userId") `, undefined);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_5cc8c468090e129857e9fecce5" ON "user_group_invitation" ("userGroupId") `, undefined);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e9793f65f504e5a31fbaedbf2f" ON "user_group_invitation" ("userId", "userGroupId") `, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`, undefined);
|
||||
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, undefined);
|
||||
await queryRunner.query(`CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, undefined);
|
||||
await queryRunner.query(`DROP TYPE "notification_type_enum_old"`, undefined);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_bfbc6305547539369fe73eb144a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_5cc8c468090e129857e9fecce5a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_5cc8c468090e129857e9fecce5a"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_bfbc6305547539369fe73eb144a"`, undefined);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS ''`, undefined);
|
||||
await queryRunner.query(`CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted')`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, undefined);
|
||||
await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined);
|
||||
await queryRunner.query(`ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`, undefined);
|
||||
await queryRunner.query(`DROP INDEX "IDX_e9793f65f504e5a31fbaedbf2f"`, undefined);
|
||||
await queryRunner.query(`DROP INDEX "IDX_5cc8c468090e129857e9fecce5"`, undefined);
|
||||
await queryRunner.query(`DROP INDEX "IDX_bfbc6305547539369fe73eb144"`, undefined);
|
||||
await queryRunner.query(`DROP TABLE "user_group_invitation"`, undefined);
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||
"version": "12.7.1",
|
||||
"version": "12.8.0",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -157,7 +157,7 @@ import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regul
|
||||
import { ResizeObserver } from '@juggle/resize-observer';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import i18n from './i18n';
|
||||
import { host } from './config';
|
||||
import { host, instanceName } from './config';
|
||||
import { search } from './scripts/search';
|
||||
import contains from './scripts/contains';
|
||||
import MkToast from './components/toast.vue';
|
||||
@ -247,11 +247,15 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
this.$root.stream.on('_disconnected_', () => {
|
||||
if (!this.disconnectedDialog) {
|
||||
if (this.disconnectedDialog) return;
|
||||
if (this.$store.state.device.autoReload) {
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.$root.stream.state !== 'reconnecting') return;
|
||||
|
||||
this.disconnectedDialog = this.$root.dialog({
|
||||
type: 'warning',
|
||||
showCancelButton: true,
|
||||
@ -263,7 +267,7 @@ export default Vue.extend({
|
||||
}
|
||||
this.disconnectedDialog = null;
|
||||
});
|
||||
}
|
||||
}, 150)
|
||||
});
|
||||
},
|
||||
|
||||
@ -274,7 +278,7 @@ export default Vue.extend({
|
||||
const lastChild = this.$refs.widgets.children[this.$refs.widgets.children.length - 1];
|
||||
if (lastChild == null) return;
|
||||
|
||||
const width = lastChild.offsetLeft + 300;
|
||||
const width = lastChild.offsetLeft + 300 + 16;
|
||||
this.$refs.widgets.style.width = width + 'px';
|
||||
};
|
||||
setInterval(adjustWidgetsWidth, 1000);
|
||||
@ -477,9 +481,14 @@ export default Vue.extend({
|
||||
icon: faQuestionCircle,
|
||||
}, {
|
||||
type: 'link',
|
||||
text: this.$t('about'),
|
||||
text: this.$t('aboutX', { x: instanceName || host }),
|
||||
to: '/about',
|
||||
icon: faInfoCircle,
|
||||
}, {
|
||||
type: 'link',
|
||||
text: this.$t('aboutMisskey'),
|
||||
to: '/about-misskey',
|
||||
icon: faInfoCircle,
|
||||
}],
|
||||
align: 'left',
|
||||
fixed: true,
|
||||
@ -744,10 +753,9 @@ export default Vue.extend({
|
||||
position: relative;
|
||||
|
||||
> input {
|
||||
$margin: 8px;
|
||||
width: 200px;
|
||||
width: 210px;
|
||||
box-sizing: border-box;
|
||||
margin-right: $margin;
|
||||
margin-right: 8px;
|
||||
padding: 0 12px 0 42px;
|
||||
font-size: 1rem;
|
||||
line-height: 38px;
|
||||
|
@ -18,7 +18,7 @@
|
||||
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
|
||||
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
|
||||
<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else-if="!useOsDefaultEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else-if="!useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else>{{ emoji.emoji }}</span>
|
||||
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
|
||||
<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
|
||||
@ -130,8 +130,8 @@ export default Vue.extend({
|
||||
return (this.$refs.suggests as Element).children;
|
||||
},
|
||||
|
||||
useOsDefaultEmojis(): boolean {
|
||||
return this.$store.state.device.useOsDefaultEmojis;
|
||||
useOsNativeEmojis(): boolean {
|
||||
return this.$store.state.device.useOsNativeEmojis;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -23,13 +23,13 @@
|
||||
<x-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/>
|
||||
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
|
||||
<div class="padding" v-for="n in 16"></div>
|
||||
<mk-button v-if="moreFolders">{{ $t('@.load-more') }}</mk-button>
|
||||
<mk-button v-if="moreFolders">{{ $t('loadMore') }}</mk-button>
|
||||
</div>
|
||||
<div class="files" ref="filesContainer" v-if="files.length > 0">
|
||||
<x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="selectMode"/>
|
||||
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
|
||||
<div class="padding" v-for="n in 16"></div>
|
||||
<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('@.load-more') }}</mk-button>
|
||||
<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('loadMore') }}</mk-button>
|
||||
</div>
|
||||
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
|
||||
<p v-if="draghover">{{ $t('empty-draghover') }}</p>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt"/>
|
||||
<img v-else-if="char && !useOsDefaultEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt"/>
|
||||
<span v-else-if="char && useOsDefaultEmojis">{{ char }}</span>
|
||||
<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt"/>
|
||||
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
|
||||
<span v-else>:{{ name }}:</span>
|
||||
</template>
|
||||
|
||||
@ -53,8 +53,8 @@ export default Vue.extend({
|
||||
return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
|
||||
},
|
||||
|
||||
useOsDefaultEmojis(): boolean {
|
||||
return this.$store.state.device.useOsDefaultEmojis && !this.isReaction;
|
||||
useOsNativeEmojis(): boolean {
|
||||
return this.$store.state.device.useOsNativeEmojis && !this.isReaction;
|
||||
},
|
||||
|
||||
ce() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mjndxjcg _panel">
|
||||
<img src="https://xn--931a.moe/assets/error.jpg" alt=""/>
|
||||
<img src="https://xn--931a.moe/assets/error.png" alt=""/>
|
||||
<p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p>
|
||||
<mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mk-google">
|
||||
<input type="search" v-model="query" :placeholder="q">
|
||||
<button @click="search"><fa icon="search"/> {{ $t('@.search') }}</button>
|
||||
<button @click="search"><fa icon="search"/> {{ $t('search') }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="yxspomdl">
|
||||
<div class="yxspomdl" :class="{ inline }">
|
||||
<div class="ring"></div>
|
||||
</div>
|
||||
</template>
|
||||
@ -8,6 +8,13 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
inline: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -25,22 +32,31 @@ export default Vue.extend({
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
|
||||
&.inline {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
|
||||
> .ring:after {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
> .ring {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
opacity: 0.7;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
> .ring:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 8px;
|
||||
box-sizing: border-box;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: solid 6px;
|
||||
border-color: var(--fg) transparent transparent transparent;
|
||||
border: solid 4px;
|
||||
border-color: currentColor transparent transparent transparent;
|
||||
animation: ring 0.5s linear infinite;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mk-notes" v-size="[{ max: 500 }]">
|
||||
<div class="empty" v-if="empty">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" alt=""/>
|
||||
<img src="https://xn--931a.moe/assets/info.png" alt=""/>
|
||||
<div>{{ $t('noNotes') }}</div>
|
||||
</div>
|
||||
|
||||
@ -11,28 +11,28 @@
|
||||
<x-note :note="note" :detail="detail" :key="note.id"/>
|
||||
</x-list>
|
||||
|
||||
<footer v-if="more">
|
||||
<button @click="fetchMore()" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" class="_buttonPrimary">
|
||||
<footer class="more" v-if="more">
|
||||
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
|
||||
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
|
||||
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
|
||||
</button>
|
||||
<template v-if="moreFetching"><mk-loading inline/></template>
|
||||
</mk-button>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../i18n';
|
||||
import paging from '../scripts/paging';
|
||||
import XNote from './note.vue';
|
||||
import XList from './date-separated-list.vue';
|
||||
import MkButton from './ui/button.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
|
||||
components: {
|
||||
XNote, XList
|
||||
XNote, XList, MkButton
|
||||
},
|
||||
|
||||
mixins: [
|
||||
@ -63,12 +63,6 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faSpinner
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
notes(): any[] {
|
||||
return this.extract ? this.extract(this.items) : this.items;
|
||||
@ -113,23 +107,11 @@ export default Vue.extend({
|
||||
}
|
||||
}
|
||||
|
||||
> footer {
|
||||
text-align: center;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> button {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
> .more > .button {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 48px;
|
||||
width: 100%;
|
||||
border-radius: var(--radius);
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -6,6 +6,7 @@
|
||||
<fa :icon="faPlus" v-if="notification.type === 'follow'"/>
|
||||
<fa :icon="faClock" v-if="notification.type === 'receiveFollowRequest'"/>
|
||||
<fa :icon="faCheck" v-if="notification.type === 'followRequestAccepted'"/>
|
||||
<fa :icon="faIdCardAlt" v-if="notification.type === 'groupInvited'"/>
|
||||
<fa :icon="faRetweet" v-if="notification.type === 'renote'"/>
|
||||
<fa :icon="faReply" v-if="notification.type === 'reply'"/>
|
||||
<fa :icon="faAt" v-if="notification.type === 'mention'"/>
|
||||
@ -40,13 +41,14 @@
|
||||
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $t('youGotNewFollower') }}<div v-if="full"><mk-follow-button :user="notification.user" :full="true"/></div></span>
|
||||
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $t('followRequestAccepted') }}</span>
|
||||
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $t('receiveFollowRequest') }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $t('reject') }}</button></div></span>
|
||||
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ $t('groupInvited') }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ $t('reject') }}</button></div></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faClock } from '@fortawesome/free-regular-svg-icons';
|
||||
import getNoteSummary from '../../misc/get-note-summary';
|
||||
import XReactionIcon from './reaction-icon.vue';
|
||||
@ -78,7 +80,8 @@ export default Vue.extend({
|
||||
return {
|
||||
getNoteSummary,
|
||||
followRequestDone: false,
|
||||
faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck
|
||||
groupInviteDone: false,
|
||||
faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -90,6 +93,18 @@ export default Vue.extend({
|
||||
this.followRequestDone = true;
|
||||
this.$root.api('following/requests/reject', { userId: this.notification.user.id });
|
||||
},
|
||||
acceptGroupInvitation() {
|
||||
this.groupInviteDone = true;
|
||||
this.$root.api('users/groups/invitations/accept', { invitationId: this.notification.invitation.id });
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
iconOnly: true, autoClose: true
|
||||
});
|
||||
},
|
||||
rejectGroupInvitation() {
|
||||
this.groupInviteDone = true;
|
||||
this.$root.api('users/groups/invitations/reject', { invitationId: this.notification.invitation.id });
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@ -149,7 +164,7 @@ export default Vue.extend({
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.follow, &.followRequestAccepted, &.receiveFollowRequest {
|
||||
&.follow, &.followRequestAccepted, &.receiveFollowRequest, &.groupInvited {
|
||||
padding: 3px;
|
||||
background: #36aed2;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
<div class="form">
|
||||
<x-note-preview class="preview" v-if="reply" :note="reply"/>
|
||||
<x-note-preview class="preview" v-if="renote" :note="renote"/>
|
||||
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('@.post-form.quote-attached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
|
||||
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('quoteAttached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
|
||||
<div v-if="visibility === 'specified'" class="to-specified">
|
||||
<span style="margin-right: 8px;">{{ $t('recipient') }}</span>
|
||||
<div class="visibleUsers">
|
||||
@ -445,7 +445,7 @@ export default Vue.extend({
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: this.$t('@.post-form.quote-question'),
|
||||
text: this.$t('quoteQuestion'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) {
|
||||
@ -610,6 +610,7 @@ export default Vue.extend({
|
||||
right: 0;
|
||||
|
||||
> .text-count {
|
||||
opacity: 0.7;
|
||||
line-height: 66px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
|
@ -13,7 +13,7 @@
|
||||
mode="out-in"
|
||||
appear
|
||||
>
|
||||
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="/^[a-z]+$/.test(reaction) ? $t('@.reactions.' + reaction) : reaction"><x-reaction-icon :reaction="reaction"/></button>
|
||||
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction"><x-reaction-icon :reaction="reaction"/></button>
|
||||
</transition-group>
|
||||
<input class="text" v-model="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText" v-autocomplete="{ model: 'text' }">
|
||||
</div>
|
||||
|
@ -40,23 +40,31 @@ export default Vue.extend({
|
||||
i: 0,
|
||||
methods: {
|
||||
beforeEnter(el) {
|
||||
if (document.hidden) return;
|
||||
|
||||
el.style.opacity = 0;
|
||||
el.style.transform = this.direction === 'down' ? 'translateY(-64px)' : 'translateY(64px)';
|
||||
let index = this.$options.i;
|
||||
const delay = this.delay * index;
|
||||
const delay = this.delay * this.$options.i;
|
||||
el.style.transition = [getComputedStyle(el).transition, `transform 0.7s cubic-bezier(0.23, 1, 0.32, 1) ${delay}ms`, `opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1) ${delay}ms`].filter(x => x != '').join(',');
|
||||
this.$options.i++;
|
||||
},
|
||||
enter(el, done) {
|
||||
|
||||
setTimeout(() => {
|
||||
el.style.transition = null;
|
||||
el.style.transform = null;
|
||||
el.style.opacity = null;
|
||||
this.$options.i--;
|
||||
}, delay + 710);
|
||||
},
|
||||
enter(el) {
|
||||
if (document.hidden) {
|
||||
el.style.opacity = 1;
|
||||
el.style.transform = 'translateY(0px)';
|
||||
} else {
|
||||
setTimeout(() => { // 必要
|
||||
el.style.opacity = 1;
|
||||
el.style.transform = 'translateY(0px)';
|
||||
el.addEventListener('transitionend', () => {
|
||||
el.style.transition = '';
|
||||
this.$options.i--;
|
||||
done();
|
||||
}, { once: true });
|
||||
});
|
||||
}
|
||||
},
|
||||
leave(el) {
|
||||
el.style.opacity = 0;
|
||||
|
@ -12,15 +12,15 @@
|
||||
<template #prefix><fa :icon="faLock"/></template>
|
||||
</mk-input>
|
||||
<mk-button type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</mk-button>
|
||||
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p>
|
||||
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p>
|
||||
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
|
||||
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="faTwitter"/> {{ $t('signinWith', { x: 'Twitter' }) }}</a></p>
|
||||
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="faGithub"/> {{ $t('signinWith', { x: 'GitHub' }) }}</a></p>
|
||||
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="faDiscord"/> {{ $t('signinWith', { x: 'Discord' }) }}</a></p>
|
||||
</div>
|
||||
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
|
||||
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
|
||||
<p>{{ $t('tap-key') }}</p>
|
||||
<p>{{ $t('tapSecurityKey') }}</p>
|
||||
<mk-button @click="queryKey" v-if="!queryingKey">
|
||||
{{ $t('@.error.retry') }}
|
||||
{{ $t('retry') }}
|
||||
</mk-button>
|
||||
</div>
|
||||
<div class="or-hr" v-if="user && user.securityKeys">
|
||||
@ -46,6 +46,7 @@
|
||||
import Vue from 'vue';
|
||||
import { toUnicode } from 'punycode';
|
||||
import { faLock, faGavel } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
|
||||
import MkButton from './ui/button.vue';
|
||||
import MkInput from './ui/input.vue';
|
||||
import i18n from '../i18n';
|
||||
@ -86,7 +87,7 @@ export default Vue.extend({
|
||||
credential: null,
|
||||
challengeData: null,
|
||||
queryingKey: false,
|
||||
faLock, faGavel
|
||||
faLock, faGavel, faTwitter, faDiscord, faGithub
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -2,9 +2,8 @@
|
||||
<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
|
||||
<template v-if="meta">
|
||||
<mk-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
|
||||
<span>{{ $t('invitation-code') }}</span>
|
||||
<template #prefix><fa icon="id-card-alt"/></template>
|
||||
<template #desc v-html="this.$t('invitation-info').replace('{}', 'mailto:' + meta.maintainerEmail)"></template>
|
||||
<span>{{ $t('invitationCode') }}</span>
|
||||
<template #prefix><fa :icon="faKey"/></template>
|
||||
</mk-input>
|
||||
<mk-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername">
|
||||
<span>{{ $t('username') }}</span>
|
||||
@ -15,26 +14,26 @@
|
||||
<span v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('available') }}</span>
|
||||
<span v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('unavailable') }}</span>
|
||||
<span v-if="usernameState == 'error'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('error') }}</span>
|
||||
<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('invalid-format') }}</span>
|
||||
<span v-if="usernameState == 'min-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('too-short') }}</span>
|
||||
<span v-if="usernameState == 'max-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('too-long') }}</span>
|
||||
<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('usernameInvalidFormat') }}</span>
|
||||
<span v-if="usernameState == 'min-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('tooShort') }}</span>
|
||||
<span v-if="usernameState == 'max-range'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('tooLong') }}</span>
|
||||
</template>
|
||||
</mk-input>
|
||||
<mk-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword">
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><fa :icon="faLock"/></template>
|
||||
<template #desc>
|
||||
<p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('weak-password') }}</p>
|
||||
<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('normal-password') }}</p>
|
||||
<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('strong-password') }}</p>
|
||||
<p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('weakPassword') }}</p>
|
||||
<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('normalPassword') }}</p>
|
||||
<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('strongPassword') }}</p>
|
||||
</template>
|
||||
</mk-input>
|
||||
<mk-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
|
||||
<span>{{ $t('password') }} ({{ $t('retype') }})</span>
|
||||
<template #prefix><fa :icon="faLock"/></template>
|
||||
<template #desc>
|
||||
<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('password-matched') }}</p>
|
||||
<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('password-not-matched') }}</p>
|
||||
<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa :icon="faCheck" fixed-width/> {{ $t('passwordMatched') }}</p>
|
||||
<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa :icon="faExclamationTriangle" fixed-width/> {{ $t('passwordNotMatched') }}</p>
|
||||
</template>
|
||||
</mk-input>
|
||||
<mk-switch v-model="ToSAgreement" v-if="meta.tosUrl">
|
||||
@ -50,7 +49,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faLock, faExclamationTriangle, faSpinner, faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faLock, faExclamationTriangle, faSpinner, faCheck, faKey } from '@fortawesome/free-solid-svg-icons';
|
||||
const getPasswordStrength = require('syuilo-password-strength');
|
||||
import { toUnicode } from 'punycode';
|
||||
import i18n from '../i18n';
|
||||
@ -81,7 +80,7 @@ export default Vue.extend({
|
||||
passwordRetypeState: null,
|
||||
submitting: false,
|
||||
ToSAgreement: false,
|
||||
faLock, faExclamationTriangle, faSpinner, faCheck
|
||||
faLock, faExclamationTriangle, faSpinner, faCheck, faKey
|
||||
}
|
||||
},
|
||||
|
||||
@ -175,7 +174,7 @@ export default Vue.extend({
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('some-error')
|
||||
text: this.$t('error')
|
||||
});
|
||||
|
||||
if (this.meta.enableRecaptcha) {
|
||||
|
@ -48,7 +48,7 @@ export default Vue.extend({
|
||||
ago >= 10 ? this.$t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
|
||||
ago >= -1 ? this.$t('_ago.justNow') :
|
||||
ago < -1 ? this.$t('_ago.future') :
|
||||
this.$t('@.time.unknown'));
|
||||
this.$t('_ago.unknown'));
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -5,9 +5,9 @@
|
||||
<slot name="empty"></slot>
|
||||
</div>
|
||||
<div class="more" v-if="more" key="_more_">
|
||||
<mk-button :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()">
|
||||
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
|
||||
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
|
||||
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
|
||||
<template v-if="moreFetching"><mk-loading inline/></template>
|
||||
</mk-button>
|
||||
</div>
|
||||
</sequential-entrance>
|
||||
@ -15,7 +15,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkButton from './button.vue';
|
||||
import paging from '../../scripts/paging';
|
||||
|
||||
@ -37,12 +36,6 @@ export default Vue.extend({
|
||||
default: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faSpinner
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -55,5 +48,12 @@ export default Vue.extend({
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
> .more > .button {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 48px;
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<mk-follow-button class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
|
||||
</div>
|
||||
<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMore()" :disabled="moreFetching">
|
||||
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('@.loading') : $t('@.load-more') }}
|
||||
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }}
|
||||
</button>
|
||||
</div>
|
||||
</mk-container>
|
||||
|
@ -3,7 +3,7 @@ declare const _VERSION_: string;
|
||||
declare const _ENV_: string;
|
||||
|
||||
const address = new URL(location.href);
|
||||
const siteName = document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement;
|
||||
const siteName = (document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement)?.content;
|
||||
|
||||
export const host = address.host;
|
||||
export const hostname = address.hostname;
|
||||
@ -15,4 +15,4 @@ export const langs = _LANGS_;
|
||||
export const locale = JSON.parse(localStorage.getItem('locale'));
|
||||
export const version = _VERSION_;
|
||||
export const env = _ENV_;
|
||||
export const instanceName = siteName && siteName.content ? siteName.content : 'Misskey';
|
||||
export const instanceName = siteName === 'Misskey' ? null : siteName;
|
||||
|
@ -61,20 +61,22 @@ if (localStorage.getItem('theme') == null) {
|
||||
}
|
||||
|
||||
//#region Detect the user language
|
||||
let lang = null;
|
||||
let lang = localStorage.getItem('lang');
|
||||
|
||||
if (langs.map(x => x[0]).includes(navigator.language)) {
|
||||
if (lang == null) {
|
||||
if (langs.map(x => x[0]).includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
} else {
|
||||
lang = langs.map(x => x[0]).find(x => x.split('-')[0] == navigator.language);
|
||||
|
||||
if (lang == null) {
|
||||
// Fallback
|
||||
lang = 'en-US';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem('lang', lang);
|
||||
localStorage.setItem('lang', lang);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// Detect the user agent
|
||||
@ -147,7 +149,7 @@ os.init(async () => {
|
||||
store: os.store,
|
||||
metaInfo: {
|
||||
title: null,
|
||||
titleTemplate: title => title ? `${title} | ${instanceName}` : instanceName
|
||||
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
79
src/client/pages/about-misskey.vue
Normal file
79
src/client/pages/about-misskey.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="znqjceqz">
|
||||
<portal to="title">🍀 {{ $t('aboutMisskey') }}</portal>
|
||||
|
||||
<section class="_card">
|
||||
<div class="_title">🍀 {{ $t('aboutMisskey') }}</div>
|
||||
<div class="_content">
|
||||
<div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div>
|
||||
<div>🛠️ {{ $t('misskeyMembers') }}</div>
|
||||
<ul class="members">
|
||||
<li><mk-link url="https://github.com/syuilo" class="at">@syuilo</mk-link></li>
|
||||
<li><mk-link url="https://github.com/AyaMorisawa" class="at">@AyaMorisawa</mk-link></li>
|
||||
<li><mk-link url="https://github.com/mei23" class="at">@mei23</mk-link></li>
|
||||
<li><mk-link url="https://github.com/acid-chicken" class="at">@acid-chicken</mk-link></li>
|
||||
<li><mk-link url="https://github.com/tamaina" class="at">@tamaina</mk-link></li>
|
||||
<li><mk-link url="https://github.com/rinsuki" class="at">@rinsuki</mk-link></li>
|
||||
</ul>
|
||||
<div style="margin-top: 1em;">📦 {{ $t('misskeySource') }}</div>
|
||||
<mk-url url="https://github.com/syuilo/misskey"/>
|
||||
<div style="margin-top: 1em;">🌏 {{ $t('misskeyTranslation') }}</div>
|
||||
<mk-url url="https://crowdin.com/project/misskey"/>
|
||||
<div style="margin-top: 1em;">💴 {{ $t('misskeyDonate') }}</div>
|
||||
<mk-url url="https://www.patreon.com/syuilo"/>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<span><mfm text="<motion>❤</motion>"/> {{ $t('patrons') }}</span>
|
||||
<ul>
|
||||
<li>Gargron</li>
|
||||
<li>Satsuki Yanagi</li>
|
||||
<li>noellabo</li>
|
||||
<li>naga_rus</li>
|
||||
<li>Melilot</li>
|
||||
<li>AureoleArk</li>
|
||||
<li>Peter G.</li>
|
||||
<li>motcha</li>
|
||||
<li>Atsuko Tominaga</li>
|
||||
<li>dansup</li>
|
||||
<li>Nokotaro Takeda</li>
|
||||
<li>YUKIMOCHI</li>
|
||||
<li>nanami kan</li>
|
||||
<li>Hekovic</li>
|
||||
<li>wara</li>
|
||||
<li>Takashi Shibuya</li>
|
||||
<li>Noizeman</li>
|
||||
</ul>
|
||||
<span>{{ $t('morePatrons') }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { version } from '../config';
|
||||
import i18n from '../i18n';
|
||||
import MkLink from '../components/link.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
|
||||
components: {
|
||||
MkLink
|
||||
},
|
||||
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.$t('aboutMisskey') as string
|
||||
};
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
version,
|
||||
faInfoCircle
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
@ -6,7 +6,7 @@
|
||||
<section class="_card info" v-if="meta">
|
||||
<div class="_title"><fa :icon="faInfoCircle"/> {{ $t('instanceInfo') }}</div>
|
||||
<div class="_content" v-if="meta.description">
|
||||
<div>{{ meta.description }}</div>
|
||||
<div v-html="meta.description"></div>
|
||||
</div>
|
||||
<div class="_content table">
|
||||
<div><b>{{ $t('administrator') }}</b><span>{{ meta.maintainerName }}</span></div>
|
||||
@ -20,49 +20,6 @@
|
||||
<div><b>Misskey</b><span>v{{ version }}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="_card aboutMisskey">
|
||||
<div class="_title"><fa :icon="faInfoCircle"/> {{ $t('aboutMisskey') }}</div>
|
||||
<div class="_content">
|
||||
<div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div>
|
||||
<div>{{ $t('misskeyMembers') }}</div>
|
||||
<span class="members">
|
||||
<a href="https://github.com/syuilo" target="_blank" class="_link">@syuilo</a>
|
||||
<a href="https://github.com/AyaMorisawa" target="_blank" class="_link">@AyaMorisawa</a>
|
||||
<a href="https://github.com/mei23" target="_blank" class="_link">@mei23</a>
|
||||
<a href="https://github.com/acid-chicken" target="_blank" class="_link">@acid-chicken</a>
|
||||
<a href="https://github.com/tamaina" target="_blank" class="_link">@tamaina</a>
|
||||
<a href="https://github.com/rinsuki" target="_blank" class="_link">@rinsuki</a>
|
||||
</span>
|
||||
<div style="margin-top: 1em;">{{ $t('misskeySource') }}</div>
|
||||
<mk-url url="https://github.com/syuilo/misskey"/>
|
||||
<div style="margin-top: 1em;">{{ $t('misskeyDonate') }}</div>
|
||||
<mk-url url="https://www.patreon.com/syuilo"/>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<span><mfm text="<motion>❤</motion>"/> {{ $t('patrons') }}</span>
|
||||
<ul>
|
||||
<li>Gargron</li>
|
||||
<li>Satsuki Yanagi</li>
|
||||
<li>noellabo</li>
|
||||
<li>naga_rus</li>
|
||||
<li>Melilot</li>
|
||||
<li>AureoleArk</li>
|
||||
<li>Peter G.</li>
|
||||
<li>motcha</li>
|
||||
<li>Atsuko Tominaga</li>
|
||||
<li>dansup</li>
|
||||
<li>Nokotaro Takeda</li>
|
||||
<li>YUKIMOCHI</li>
|
||||
<li>nanami kan</li>
|
||||
<li>Hekovic</li>
|
||||
<li>wara</li>
|
||||
<li>Takashi Shibuya</li>
|
||||
<li>Noizeman</li>
|
||||
</ul>
|
||||
<span>{{ $t('morePatrons') }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -117,15 +74,5 @@ export default Vue.extend({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .aboutMisskey {
|
||||
> ._content {
|
||||
> .members {
|
||||
> a {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -10,7 +10,7 @@
|
||||
import Vue from 'vue';
|
||||
import XSetup from './index.welcome.setup.vue';
|
||||
import XEntrance from './index.welcome.entrance.vue';
|
||||
import { getInstanceName } from '../scripts/get-instance-name';
|
||||
import { instanceName } from '../config';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
@ -20,7 +20,7 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
instanceName: getInstanceName(),
|
||||
instanceName: instanceName || 'Misskey',
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -43,8 +43,8 @@
|
||||
<div class="_content">
|
||||
<mk-switch v-model="enableRecaptcha">{{ $t('enableRecaptcha') }}</mk-switch>
|
||||
<template v-if="enableRecaptcha">
|
||||
<mk-info>{{ $t('recaptcha-info') }}</mk-info>
|
||||
<mk-info warn>{{ $t('recaptcha-info2') }}</mk-info>
|
||||
<mk-info>{{ $t('recaptchaInfo') }}</mk-info>
|
||||
<mk-info warn>{{ $t('recaptchaInfo2') }}</mk-info>
|
||||
<mk-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><fa :icon="faKey"/></template>{{ $t('recaptchaSiteKey') }}</mk-input>
|
||||
<mk-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><fa :icon="faKey"/></template>{{ $t('recaptchaSecretKey') }}</mk-input>
|
||||
</template>
|
||||
@ -61,12 +61,12 @@
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faBolt"/> {{ $t('serviceworker') }}</div>
|
||||
<div class="_content">
|
||||
<mk-switch v-model="enableServiceWorker">{{ $t('enableServiceworker') }}<template #desc>{{ $t('serviceworker-info') }}</template></mk-switch>
|
||||
<mk-switch v-model="enableServiceWorker">{{ $t('enableServiceworker') }}<template #desc>{{ $t('serviceworkerInfo') }}</template></mk-switch>
|
||||
<template v-if="enableServiceWorker">
|
||||
<mk-info>{{ $t('vapid-info') }}<br><code>npm i web-push -g<br>web-push generate-vapid-keys</code></mk-info>
|
||||
<mk-info>{{ $t('vapidInfo') }}<br><code>npx web-push generate-vapid-keys</code></mk-info>
|
||||
<mk-horizon-group inputs class="fit-bottom">
|
||||
<mk-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>{{ $t('vapid-publickey') }}</mk-input>
|
||||
<mk-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>{{ $t('vapid-privatekey') }}</mk-input>
|
||||
<mk-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>Public key</mk-input>
|
||||
<mk-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>Private key</mk-input>
|
||||
</mk-horizon-group>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -100,7 +100,7 @@ export default Vue.extend({
|
||||
const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, '1')}${ext}`;
|
||||
const name = this.$store.state.settings.pasteDialog
|
||||
? await this.$root.dialog({
|
||||
title: this.$t('@.post-form.enter-file-name'),
|
||||
title: this.$t('enterFileName'),
|
||||
input: {
|
||||
default: formatted
|
||||
},
|
||||
|
@ -3,7 +3,7 @@
|
||||
<mk-avatar class="avatar" :user="message.user"/>
|
||||
<div class="content">
|
||||
<div class="balloon _panel" :data-no-text="message.text == null">
|
||||
<button class="delete-button" v-if="isMe" :title="$t('@.delete')" @click="del">
|
||||
<button class="delete-button" v-if="isMe" :title="$t('delete')" @click="del">
|
||||
<img src="/assets/desktop/remove.png" alt="Delete"/>
|
||||
</button>
|
||||
<div class="content" v-if="!message.isDeleted">
|
||||
|
@ -14,10 +14,10 @@
|
||||
|
||||
<div class="body">
|
||||
<mk-loading v-if="fetching"/>
|
||||
<p class="empty" v-if="!fetching && messages.length == 0"><fa icon="info-circle"/>{{ user ? $t('not-talked-user') : $t('not-talked-group') }}</p>
|
||||
<p class="empty" v-if="!fetching && messages.length == 0"><fa :icon="faInfoCircle"/>{{ $t('noMessagesYet') }}</p>
|
||||
<p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><fa :icon="faFlag"/>{{ $t('noMoreHistory') }}</p>
|
||||
<button class="more _button" :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('loadMore') }}
|
||||
</button>
|
||||
<x-list class="messages" :items="messages" v-slot="{ item: message, i }" direction="up" reversed>
|
||||
<x-message :message="message" :is-group="group != null" :key="message.id"/>
|
||||
@ -26,7 +26,7 @@
|
||||
<footer>
|
||||
<transition name="fade">
|
||||
<div class="new-message" v-show="showIndicator">
|
||||
<button class="_buttonPrimary" @click="onIndicatorClick"><i><fa :icon="faArrowCircleDown"/></i>{{ $t('new-message') }}</button>
|
||||
<button class="_buttonPrimary" @click="onIndicatorClick"><i><fa :icon="faArrowCircleDown"/></i>{{ $t('newMessageExists') }}</button>
|
||||
</div>
|
||||
</transition>
|
||||
<x-form v-if="!fetching" :user="user" :group="group" ref="form"/>
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faArrowCircleDown, faFlag, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faArrowCircleDown, faFlag, faUsers, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../i18n';
|
||||
import XList from '../components/date-separated-list.vue';
|
||||
import XMessage from './messaging-room.message.vue';
|
||||
@ -64,7 +64,7 @@ export default Vue.extend({
|
||||
connection: null,
|
||||
showIndicator: false,
|
||||
timer: null,
|
||||
faArrowCircleDown, faFlag, faUsers
|
||||
faArrowCircleDown, faFlag, faUsers, faInfoCircle
|
||||
};
|
||||
},
|
||||
|
||||
@ -139,7 +139,7 @@ export default Vue.extend({
|
||||
} else if (e.dataTransfer.files.length > 1) {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('only-one-file-attached')
|
||||
text: this.$t('onlyOneFileCanBeAttached')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -17,13 +17,13 @@
|
||||
|
||||
<mk-container :body-togglable="true">
|
||||
<template #header><fa :icon="faEnvelopeOpenText"/> {{ $t('invites') }}</template>
|
||||
<mk-pagination :pagination="invitePagination" #default="{items}" ref="invites">
|
||||
<div class="_frame" v-for="invite in items" :key="invite.id">
|
||||
<div class="_title">{{ invite.group.name }}</div>
|
||||
<div class="_content"><mk-avatars :user-ids="invite.group.userIds"/></div>
|
||||
<mk-pagination :pagination="invitationPagination" #default="{items}" ref="invitations">
|
||||
<div class="_frame" v-for="invitation in items" :key="invitation.id">
|
||||
<div class="_title">{{ invitation.group.name }}</div>
|
||||
<div class="_content"><mk-avatars :user-ids="invitation.group.userIds"/></div>
|
||||
<div class="_footer">
|
||||
<mk-button @click="acceptInvite(invite)" primary inline><fa :icon="faCheck"/> {{ $t('accept') }}</mk-button>
|
||||
<mk-button @click="rejectInvite(invite)" primary inline><fa :icon="faBan"/> {{ $t('reject') }}</mk-button>
|
||||
<mk-button @click="acceptInvite(invitation)" primary inline><fa :icon="faCheck"/> {{ $t('accept') }}</mk-button>
|
||||
<mk-button @click="rejectInvite(invitation)" primary inline><fa :icon="faBan"/> {{ $t('reject') }}</mk-button>
|
||||
</div>
|
||||
</div>
|
||||
</mk-pagination>
|
||||
@ -73,7 +73,7 @@ export default Vue.extend({
|
||||
endpoint: 'users/groups/joined',
|
||||
limit: 10,
|
||||
},
|
||||
invitePagination: {
|
||||
invitationPagination: {
|
||||
endpoint: 'i/user-group-invites',
|
||||
limit: 10,
|
||||
},
|
||||
@ -95,23 +95,23 @@ export default Vue.extend({
|
||||
iconOnly: true, autoClose: true
|
||||
});
|
||||
},
|
||||
acceptInvite(invite) {
|
||||
acceptInvite(invitation) {
|
||||
this.$root.api('users/groups/invitations/accept', {
|
||||
inviteId: invite.id
|
||||
invitationId: invitation.id
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
iconOnly: true, autoClose: true
|
||||
});
|
||||
this.$refs.invites.reload();
|
||||
this.$refs.invitations.reload();
|
||||
this.$refs.joined.reload();
|
||||
});
|
||||
},
|
||||
rejectInvite(invite) {
|
||||
rejectInvite(invitation) {
|
||||
this.$root.api('users/groups/invitations/reject', {
|
||||
inviteId: invite.id
|
||||
invitationId: invitation.id
|
||||
}).then(() => {
|
||||
this.$refs.invites.reload();
|
||||
this.$refs.invitations.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
<section class="_card">
|
||||
<div class="_content">
|
||||
<img src="https://xn--931a.moe/assets/not-found.jpg" alt=""/>
|
||||
<img src="https://xn--931a.moe/assets/not-found.png" alt=""/>
|
||||
<div>{{ $t('notFoundDescription') }}</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -23,10 +23,19 @@
|
||||
<mk-button @click="readAllMessagingMessages">{{ $t('markAsReadAllTalkMessages') }}</mk-button>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<mk-switch v-model="reduceAnimation">
|
||||
{{ $t('reduceUiAnimation') }}
|
||||
<mk-switch v-model="reduceAnimation">{{ $t('reduceUiAnimation') }}</mk-switch>
|
||||
<mk-switch v-model="useOsNativeEmojis">
|
||||
{{ $t('useOsNativeEmojis') }}
|
||||
<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
|
||||
</mk-switch>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<mk-select v-model="lang">
|
||||
<template #label>{{ $t('uiLanguage') }}</template>
|
||||
|
||||
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
|
||||
</mk-select>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@ -36,8 +45,9 @@ import { faImage, faCog } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkInput from '../../components/ui/input.vue';
|
||||
import MkButton from '../../components/ui/button.vue';
|
||||
import MkSwitch from '../../components/ui/switch.vue';
|
||||
import MkSelect from '../../components/ui/select.vue';
|
||||
import i18n from '../../i18n';
|
||||
import { apiUrl } from '../../config';
|
||||
import { apiUrl, langs } from '../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
@ -46,10 +56,13 @@ export default Vue.extend({
|
||||
MkInput,
|
||||
MkButton,
|
||||
MkSwitch,
|
||||
MkSelect,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
langs,
|
||||
lang: localStorage.getItem('lang'),
|
||||
wallpaperUploading: false,
|
||||
faImage, faCog
|
||||
}
|
||||
@ -70,6 +83,19 @@ export default Vue.extend({
|
||||
get() { return !this.$store.state.device.animation; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); }
|
||||
},
|
||||
|
||||
useOsNativeEmojis: {
|
||||
get() { return this.$store.state.device.useOsNativeEmojis; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'useOsNativeEmojis', value }); }
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
lang() {
|
||||
localStorage.setItem('lang', this.lang);
|
||||
localStorage.removeItem('locale');
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -20,6 +20,7 @@ export const router = new VueRouter({
|
||||
{ path: '/@:user/pages/:pageName/view-source', component: page('page-editor/page-editor'), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
|
||||
{ path: '/announcements', component: page('announcements') },
|
||||
{ path: '/about', component: page('about') },
|
||||
{ path: '/about-misskey', component: page('about-misskey') },
|
||||
{ path: '/featured', component: page('featured') },
|
||||
{ path: '/docs', component: page('docs') },
|
||||
{ path: '/docs/:doc', component: page('doc'), props: true },
|
||||
@ -62,11 +63,16 @@ export const router = new VueRouter({
|
||||
],
|
||||
// なんかHacky
|
||||
// 通常の使い方をすると scroll メソッドの behavior を設定できないため、自前で window.scroll するようにする
|
||||
// setTimeout しないと、アニメーション(トランジション)の関係でうまく動かない
|
||||
scrollBehavior(to) {
|
||||
window._scroll = () => { // さらにHacky
|
||||
if (to.name === 'index') {
|
||||
window.scroll({ top: indexScrollPos, behavior: 'instant' });
|
||||
const i = setInterval(() => {
|
||||
window.scroll({ top: indexScrollPos, behavior: 'instant' });
|
||||
}, 10);
|
||||
setTimeout(() => {
|
||||
clearInterval(i);
|
||||
}, 500);
|
||||
} else {
|
||||
window.scroll({ top: 0, behavior: 'instant' });
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
export function getInstanceName() {
|
||||
const siteName = document.querySelector('meta[property="og:site_name"]') as HTMLMetaElement;
|
||||
if (siteName && siteName.content) {
|
||||
return siteName.content;
|
||||
}
|
||||
|
||||
return 'Misskey';
|
||||
}
|
@ -1,15 +1,45 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
function getScrollContainer(el: Element | null): Element | null {
|
||||
if (el == null || el.tagName === 'BODY') return null;
|
||||
const style = window.getComputedStyle(el);
|
||||
if (style.getPropertyValue('overflow') === 'auto') {
|
||||
return el;
|
||||
} else {
|
||||
return getScrollContainer(el.parentElement);
|
||||
}
|
||||
}
|
||||
|
||||
function getScrollPosition(el: Element | null): number {
|
||||
const container = getScrollContainer(el);
|
||||
return container == null ? window.scrollY : container.scrollTop;
|
||||
}
|
||||
|
||||
function onScrollTop(el, cb) {
|
||||
const container = getScrollContainer(el) || window;
|
||||
const onScroll = ev => {
|
||||
if (!document.body.contains(el)) return;
|
||||
const pos = getScrollPosition(el);
|
||||
if (pos === 0) {
|
||||
cb();
|
||||
container.removeEventListener('scroll', onscroll);
|
||||
}
|
||||
};
|
||||
container.addEventListener('scroll', onScroll, { passive: true });
|
||||
}
|
||||
|
||||
export default (opts) => ({
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
queue: [],
|
||||
offset: 0,
|
||||
fetching: true,
|
||||
moreFetching: false,
|
||||
inited: false,
|
||||
more: false,
|
||||
backed: false,
|
||||
isBackTop: false,
|
||||
};
|
||||
},
|
||||
|
||||
@ -32,13 +62,17 @@ export default (opts) => ({
|
||||
created() {
|
||||
opts.displayLimit = opts.displayLimit || 30;
|
||||
this.init();
|
||||
|
||||
this.$on('hook:activated', () => {
|
||||
this.isBackTop = false;
|
||||
});
|
||||
|
||||
this.$on('hook:deactivated', () => {
|
||||
this.isBackTop = window.scrollY === 0;
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
isScrollTop() {
|
||||
return window.scrollY <= 8;
|
||||
},
|
||||
|
||||
updateItem(i, item) {
|
||||
Vue.set((this as any).items, i, item);
|
||||
},
|
||||
@ -107,21 +141,26 @@ export default (opts) => ({
|
||||
});
|
||||
},
|
||||
|
||||
prepend(item, silent = false) {
|
||||
if (opts.onPrepend) {
|
||||
const cancel = opts.onPrepend(this, item, silent);
|
||||
if (cancel) return;
|
||||
}
|
||||
prepend(item) {
|
||||
const isTop = this.isBackTop || (document.body.contains(this.$el) && (getScrollPosition(this.$el) === 0));
|
||||
|
||||
if (isTop) {
|
||||
// Prepend the item
|
||||
this.items.unshift(item);
|
||||
|
||||
if (this.isScrollTop()) {
|
||||
// オーバーフローしたら古い投稿は捨てる
|
||||
// オーバーフローしたら古いアイテムは捨てる
|
||||
if (this.items.length >= opts.displayLimit) {
|
||||
this.items = this.items.slice(0, opts.displayLimit);
|
||||
this.more = true;
|
||||
}
|
||||
} else {
|
||||
this.queue.push(item);
|
||||
onScrollTop(this.$el, () => {
|
||||
for (const item of this.queue) {
|
||||
this.prepend(item);
|
||||
}
|
||||
this.queue = [];
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2,7 +2,7 @@ export default ($root: any) => {
|
||||
if ($root.$store.getters.isSignedIn) return;
|
||||
|
||||
$root.dialog({
|
||||
title: $root.$t('@.signin-required'),
|
||||
title: $root.$t('signinRequired'),
|
||||
text: null
|
||||
});
|
||||
|
||||
|
@ -9,7 +9,7 @@ import MiOS from '../mios';
|
||||
*/
|
||||
export default class Stream extends EventEmitter {
|
||||
private stream: ReconnectingWebsocket;
|
||||
public state: string;
|
||||
public state: 'initializing' | 'reconnecting' | 'connected';
|
||||
private sharedConnectionPools: Pool[] = [];
|
||||
private sharedConnections: SharedConnection[] = [];
|
||||
private nonSharedConnections: NonSharedConnection[] = [];
|
||||
|
@ -31,7 +31,7 @@ const defaultDeviceSettings = {
|
||||
lang: null,
|
||||
loadRawImages: false,
|
||||
alwaysShowNsfw: false,
|
||||
useOsDefaultEmojis: false,
|
||||
useOsNativeEmojis: false,
|
||||
autoReload: false,
|
||||
accounts: [],
|
||||
recentEmojis: [],
|
||||
|
@ -26,7 +26,7 @@ import { UserList } from '../models/entities/user-list';
|
||||
import { UserListJoining } from '../models/entities/user-list-joining';
|
||||
import { UserGroup } from '../models/entities/user-group';
|
||||
import { UserGroupJoining } from '../models/entities/user-group-joining';
|
||||
import { UserGroupInvite } from '../models/entities/user-group-invite';
|
||||
import { UserGroupInvitation } from '../models/entities/user-group-invitation';
|
||||
import { Hashtag } from '../models/entities/hashtag';
|
||||
import { NoteFavorite } from '../models/entities/note-favorite';
|
||||
import { AbuseUserReport } from '../models/entities/abuse-user-report';
|
||||
@ -106,7 +106,7 @@ export const entities = [
|
||||
UserListJoining,
|
||||
UserGroup,
|
||||
UserGroupJoining,
|
||||
UserGroupInvite,
|
||||
UserGroupInvitation,
|
||||
UserNotePining,
|
||||
UserSecurityKey,
|
||||
UsedUsername,
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { fetchMeta } from './fetch-meta';
|
||||
import { ILocalUser } from '../models/entities/user';
|
||||
import { Users } from '../models';
|
||||
import { ensure } from '../prelude/ensure';
|
||||
|
||||
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
|
||||
if (meta.proxyAccountId == null) return null;
|
||||
const meta = await fetchMeta();
|
||||
return await Users.findOne(meta.proxyAccountId);
|
||||
if (meta.proxyAccountId == null) return null;
|
||||
return await Users.findOne(meta.proxyAccountId).then(ensure) as ILocalUser;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { User } from './user';
|
||||
import { id } from '../id';
|
||||
import { Note } from './note';
|
||||
import { FollowRequest } from './follow-request';
|
||||
import { UserGroupInvitation } from './user-group-invitation';
|
||||
|
||||
@Entity()
|
||||
export class Notification {
|
||||
@ -57,12 +58,13 @@ export class Notification {
|
||||
* pollVote - (自分または自分がWatchしている)投稿の投票に投票された
|
||||
* receiveFollowRequest - フォローリクエストされた
|
||||
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
||||
* groupInvited - グループに招待された
|
||||
*/
|
||||
@Column('enum', {
|
||||
enum: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted'],
|
||||
enum: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited'],
|
||||
comment: 'The type of the Notification.'
|
||||
})
|
||||
public type: 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollVote' | 'receiveFollowRequest' | 'followRequestAccepted';
|
||||
public type: 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollVote' | 'receiveFollowRequest' | 'followRequestAccepted' | 'groupInvited';
|
||||
|
||||
/**
|
||||
* 通知が読まれたかどうか
|
||||
@ -97,6 +99,18 @@ export class Notification {
|
||||
@JoinColumn()
|
||||
public followRequest: FollowRequest | null;
|
||||
|
||||
@Column({
|
||||
...id(),
|
||||
nullable: true
|
||||
})
|
||||
public userGroupInvitationId: UserGroupInvitation['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupInvitation, {
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
@JoinColumn()
|
||||
public userGroupInvitation: UserGroupInvitation | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 128, nullable: true
|
||||
})
|
||||
|
@ -5,12 +5,12 @@ import { id } from '../id';
|
||||
|
||||
@Entity()
|
||||
@Index(['userId', 'userGroupId'], { unique: true })
|
||||
export class UserGroupInvite {
|
||||
export class UserGroupInvitation {
|
||||
@PrimaryColumn(id())
|
||||
public id: string;
|
||||
|
||||
@Column('timestamp with time zone', {
|
||||
comment: 'The created date of the UserGroupInvite.'
|
||||
comment: 'The created date of the UserGroupInvitation.'
|
||||
})
|
||||
public createdAt: Date;
|
||||
|
@ -24,7 +24,7 @@ import { UserListRepository } from './repositories/user-list';
|
||||
import { UserListJoining } from './entities/user-list-joining';
|
||||
import { UserGroupRepository } from './repositories/user-group';
|
||||
import { UserGroupJoining } from './entities/user-group-joining';
|
||||
import { UserGroupInviteRepository } from './repositories/user-group-invite';
|
||||
import { UserGroupInvitationRepository } from './repositories/user-group-invitation';
|
||||
import { FollowRequestRepository } from './repositories/follow-request';
|
||||
import { MutingRepository } from './repositories/muting';
|
||||
import { BlockingRepository } from './repositories/blocking';
|
||||
@ -71,7 +71,7 @@ export const UserLists = getCustomRepository(UserListRepository);
|
||||
export const UserListJoinings = getRepository(UserListJoining);
|
||||
export const UserGroups = getCustomRepository(UserGroupRepository);
|
||||
export const UserGroupJoinings = getRepository(UserGroupJoining);
|
||||
export const UserGroupInvites = getCustomRepository(UserGroupInviteRepository);
|
||||
export const UserGroupInvitations = getCustomRepository(UserGroupInvitationRepository);
|
||||
export const UserNotePinings = getRepository(UserNotePining);
|
||||
export const UsedUsernames = getRepository(UsedUsername);
|
||||
export const Followings = getCustomRepository(FollowingRepository);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users, Notes } from '..';
|
||||
import { Users, Notes, UserGroupInvitations } from '..';
|
||||
import { Notification } from '../entities/notification';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
import { awaitAll } from '../../prelude/await-all';
|
||||
@ -39,7 +39,10 @@ export class NotificationRepository extends Repository<Notification> {
|
||||
...(notification.type === 'pollVote' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId!, notification.notifieeId),
|
||||
choice: notification.choice
|
||||
} : {})
|
||||
} : {}),
|
||||
...(notification.type === 'groupInvited' ? {
|
||||
invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!),
|
||||
} : {}),
|
||||
});
|
||||
}
|
||||
|
||||
|
24
src/models/repositories/user-group-invitation.ts
Normal file
24
src/models/repositories/user-group-invitation.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { UserGroupInvitation } from '../entities/user-group-invitation';
|
||||
import { UserGroups } from '..';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
|
||||
@EntityRepository(UserGroupInvitation)
|
||||
export class UserGroupInvitationRepository extends Repository<UserGroupInvitation> {
|
||||
public async pack(
|
||||
src: UserGroupInvitation['id'] | UserGroupInvitation,
|
||||
) {
|
||||
const invitation = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
|
||||
|
||||
return {
|
||||
id: invitation.id,
|
||||
group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId),
|
||||
};
|
||||
}
|
||||
|
||||
public packMany(
|
||||
invitations: any[],
|
||||
) {
|
||||
return Promise.all(invitations.map(x => this.pack(x)));
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { UserGroupInvite } from '../entities/user-group-invite';
|
||||
import { UserGroups } from '..';
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
|
||||
@EntityRepository(UserGroupInvite)
|
||||
export class UserGroupInviteRepository extends Repository<UserGroupInvite> {
|
||||
public async pack(
|
||||
src: UserGroupInvite['id'] | UserGroupInvite,
|
||||
) {
|
||||
const invite = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
|
||||
|
||||
return {
|
||||
id: invite.id,
|
||||
group: await UserGroups.pack(invite.userGroup || invite.userGroupId),
|
||||
};
|
||||
}
|
||||
|
||||
public packMany(
|
||||
invites: any[],
|
||||
) {
|
||||
return Promise.all(invites.map(x => this.pack(x)));
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import $ from 'cafy';
|
||||
import { ID } from '../../../../misc/cafy-id';
|
||||
import define from '../../define';
|
||||
import { UserGroupInvites } from '../../../../models';
|
||||
import { UserGroupInvitations } from '../../../../models';
|
||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||
|
||||
export const meta = {
|
||||
@ -33,13 +33,13 @@ export const meta = {
|
||||
};
|
||||
|
||||
export default define(meta, async (ps, user) => {
|
||||
const query = makePaginationQuery(UserGroupInvites.createQueryBuilder('invite'), ps.sinceId, ps.untilId)
|
||||
.andWhere(`invite.userId = :meId`, { meId: user.id })
|
||||
.leftJoinAndSelect('invite.userGroup', 'user_group');
|
||||
const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId)
|
||||
.andWhere(`invitation.userId = :meId`, { meId: user.id })
|
||||
.leftJoinAndSelect('invitation.userGroup', 'user_group');
|
||||
|
||||
const invites = await query
|
||||
const invitations = await query
|
||||
.take(ps.limit!)
|
||||
.getMany();
|
||||
|
||||
return await UserGroupInvites.packMany(invites);
|
||||
return await UserGroupInvitations.packMany(invitations);
|
||||
});
|
||||
|
@ -2,14 +2,14 @@ import $ from 'cafy';
|
||||
import { ID } from '../../../../../../misc/cafy-id';
|
||||
import define from '../../../../define';
|
||||
import { ApiError } from '../../../../error';
|
||||
import { UserGroupJoinings, UserGroupInvites } from '../../../../../../models';
|
||||
import { UserGroupJoinings, UserGroupInvitations } from '../../../../../../models';
|
||||
import { genId } from '../../../../../../misc/gen-id';
|
||||
import { UserGroupJoining } from '../../../../../../models/entities/user-group-joining';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーグループへの招待を承認します。',
|
||||
'en-US': 'Accept invite of a user group.'
|
||||
'en-US': 'Accept invitation of a user group.'
|
||||
},
|
||||
|
||||
tags: ['groups', 'users'],
|
||||
@ -19,11 +19,11 @@ export const meta = {
|
||||
kind: 'write:user-groups',
|
||||
|
||||
params: {
|
||||
inviteId: {
|
||||
invitationId: {
|
||||
validator: $.type(ID),
|
||||
desc: {
|
||||
'ja-JP': '招待ID',
|
||||
'en-US': 'The invite ID'
|
||||
'en-US': 'The invitation ID'
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -39,15 +39,15 @@ export const meta = {
|
||||
|
||||
export default define(meta, async (ps, user) => {
|
||||
// Fetch the invitation
|
||||
const invite = await UserGroupInvites.findOne({
|
||||
id: ps.inviteId,
|
||||
const invitation = await UserGroupInvitations.findOne({
|
||||
id: ps.invitationId,
|
||||
});
|
||||
|
||||
if (invite == null) {
|
||||
if (invitation == null) {
|
||||
throw new ApiError(meta.errors.noSuchInvitation);
|
||||
}
|
||||
|
||||
if (invite.userId !== user.id) {
|
||||
if (invitation.userId !== user.id) {
|
||||
throw new ApiError(meta.errors.noSuchInvitation);
|
||||
}
|
||||
|
||||
@ -56,8 +56,8 @@ export default define(meta, async (ps, user) => {
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
userId: user.id,
|
||||
userGroupId: invite.userGroupId
|
||||
userGroupId: invitation.userGroupId
|
||||
} as UserGroupJoining);
|
||||
|
||||
UserGroupInvites.delete(invite.id);
|
||||
UserGroupInvitations.delete(invitation.id);
|
||||
});
|
||||
|
@ -2,12 +2,12 @@ import $ from 'cafy';
|
||||
import { ID } from '../../../../../../misc/cafy-id';
|
||||
import define from '../../../../define';
|
||||
import { ApiError } from '../../../../error';
|
||||
import { UserGroupInvites } from '../../../../../../models';
|
||||
import { UserGroupInvitations } from '../../../../../../models';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーグループへの招待を拒否します。',
|
||||
'en-US': 'Reject invite of a user group.'
|
||||
'en-US': 'Reject invitation of a user group.'
|
||||
},
|
||||
|
||||
tags: ['groups', 'users'],
|
||||
@ -17,11 +17,11 @@ export const meta = {
|
||||
kind: 'write:user-groups',
|
||||
|
||||
params: {
|
||||
inviteId: {
|
||||
invitationId: {
|
||||
validator: $.type(ID),
|
||||
desc: {
|
||||
'ja-JP': '招待ID',
|
||||
'en-US': 'The invite ID'
|
||||
'en-US': 'The invitation ID'
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -37,17 +37,17 @@ export const meta = {
|
||||
|
||||
export default define(meta, async (ps, user) => {
|
||||
// Fetch the invitation
|
||||
const invite = await UserGroupInvites.findOne({
|
||||
id: ps.inviteId,
|
||||
const invitation = await UserGroupInvitations.findOne({
|
||||
id: ps.invitationId,
|
||||
});
|
||||
|
||||
if (invite == null) {
|
||||
if (invitation == null) {
|
||||
throw new ApiError(meta.errors.noSuchInvitation);
|
||||
}
|
||||
|
||||
if (invite.userId !== user.id) {
|
||||
if (invitation.userId !== user.id) {
|
||||
throw new ApiError(meta.errors.noSuchInvitation);
|
||||
}
|
||||
|
||||
await UserGroupInvites.delete(invite.id);
|
||||
await UserGroupInvitations.delete(invitation.id);
|
||||
});
|
||||
|
@ -3,9 +3,10 @@ import { ID } from '../../../../../misc/cafy-id';
|
||||
import define from '../../../define';
|
||||
import { ApiError } from '../../../error';
|
||||
import { getUser } from '../../../common/getters';
|
||||
import { UserGroups, UserGroupJoinings, UserGroupInvites } from '../../../../../models';
|
||||
import { UserGroups, UserGroupJoinings, UserGroupInvitations } from '../../../../../models';
|
||||
import { genId } from '../../../../../misc/gen-id';
|
||||
import { UserGroupInvite } from '../../../../../models/entities/user-group-invite';
|
||||
import { UserGroupInvitation } from '../../../../../models/entities/user-group-invitation';
|
||||
import { createNotification } from '../../../../../services/create-notification';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
@ -86,19 +87,24 @@ export default define(meta, async (ps, me) => {
|
||||
throw new ApiError(meta.errors.alreadyAdded);
|
||||
}
|
||||
|
||||
const invite = await UserGroupInvites.findOne({
|
||||
const existInvitation = await UserGroupInvitations.findOne({
|
||||
userGroupId: userGroup.id,
|
||||
userId: user.id
|
||||
});
|
||||
|
||||
if (invite) {
|
||||
if (existInvitation) {
|
||||
throw new ApiError(meta.errors.alreadyInvited);
|
||||
}
|
||||
|
||||
await UserGroupInvites.save({
|
||||
const invitation = await UserGroupInvitations.save({
|
||||
id: genId(),
|
||||
createdAt: new Date(),
|
||||
userId: user.id,
|
||||
userGroupId: userGroup.id
|
||||
} as UserGroupInvite);
|
||||
} as UserGroupInvitation);
|
||||
|
||||
// 通知を作成
|
||||
createNotification(user.id, me.id, 'groupInvited', {
|
||||
userGroupInvitationId: invitation.id
|
||||
});
|
||||
});
|
||||
|
@ -6,16 +6,18 @@ import { User } from '../models/entities/user';
|
||||
import { Note } from '../models/entities/note';
|
||||
import { Notification } from '../models/entities/notification';
|
||||
import { FollowRequest } from '../models/entities/follow-request';
|
||||
import { UserGroupInvitation } from '../models/entities/user-group-invitation';
|
||||
|
||||
export async function createNotification(
|
||||
notifieeId: User['id'],
|
||||
notifierId: User['id'],
|
||||
type: string,
|
||||
type: Notification['type'],
|
||||
content?: {
|
||||
noteId?: Note['id'];
|
||||
reaction?: string;
|
||||
choice?: number;
|
||||
followRequestId?: FollowRequest['id'];
|
||||
userGroupInvitationId?: UserGroupInvitation['id'];
|
||||
}
|
||||
) {
|
||||
if (notifieeId === notifierId) {
|
||||
@ -36,6 +38,7 @@ export async function createNotification(
|
||||
if (content.reaction) data.reaction = content.reaction;
|
||||
if (content.choice) data.choice = content.choice;
|
||||
if (content.followRequestId) data.followRequestId = content.followRequestId;
|
||||
if (content.userGroupInvitationId) data.userGroupInvitationId = content.userGroupInvitationId;
|
||||
}
|
||||
|
||||
// Create notification
|
||||
|
@ -124,7 +124,7 @@ module.exports = {
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
_VERSION_: JSON.stringify(meta.version),
|
||||
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]: [string, any]) => [k, v && v.meta && v.meta.lang])),
|
||||
_LANGS_: JSON.stringify(Object.entries(locales).map(([k, v]: [string, any]) => [k, v._lang_])),
|
||||
_ENV_: JSON.stringify(process.env.NODE_ENV)
|
||||
}),
|
||||
new VueLoaderPlugin(),
|
||||
|
Reference in New Issue
Block a user