Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
ec222378c4 | |||
ac930a1c6a | |||
5ccd5ad56e | |||
67da90530b | |||
b7f3753615 | |||
d21d38509c | |||
a59e1c0345 | |||
7ab613b394 | |||
c00ab0fbe2 | |||
87451b1223 | |||
d2b61229a3 | |||
980584020a | |||
a43d0dafa5 | |||
d5c1e7e579 | |||
55bdf0d618 | |||
44f7c13ad4 | |||
7bd1a3c8ac | |||
4f1981df03 | |||
8689a998aa | |||
079bb8d722 | |||
65c0b6c7da | |||
84958af4ce | |||
c53b59914b | |||
8ffd9ab2d9 | |||
0305caf504 | |||
3012e4ffe0 | |||
585f3c3d3e | |||
f1d07dfbed | |||
cddfc55110 | |||
a2ce072ae7 | |||
439563c5d6 | |||
962617b4f4 | |||
4a202f0f7e | |||
6e6b12519a | |||
f5f7654f4b | |||
5ac4c48ad1 | |||
7e18fd18b0 | |||
fb30c479ea | |||
f40dcbfe13 | |||
8755b5f353 | |||
691482bb28 | |||
4248bb8ce0 | |||
a5653e33d3 | |||
072dc1c7e6 | |||
d76fceae85 | |||
86107b2710 | |||
a473768bef | |||
f7fe13a177 | |||
acd29d22eb | |||
c228155514 | |||
b601b98d5c | |||
1cd3419688 | |||
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 | |||
9fb5579701 | |||
b3cf883a44 | |||
f65e27c07c |
43
CHANGELOG.md
43
CHANGELOG.md
@ -1,6 +1,49 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
12.10.0 (2020/02/15)
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
* アンテナの受信ソースにグループを指定できるように
|
||||
* 時計ウィジェットを追加
|
||||
* ログアウトせずに新しいアカウントを追加できるように
|
||||
* フォントサイズを設定できるように
|
||||
* APIキー設定を実装
|
||||
|
||||
### 🐛Fixes
|
||||
* v12アップデート後にトップページアクセスでOops!になっちゃうのを修正
|
||||
* drive/files APIのパフォーマンスを改善
|
||||
|
||||
12.9.0 (2020/02/14)
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
* カスタム絵文字の管理を強化
|
||||
* 動きのあるMFMを無効にするオプションを追加
|
||||
|
||||
### 🐛Fixes
|
||||
* タイムラインに自分の返信と自分への返信と投稿者自身への返信以外の返信が含まれている問題を修正
|
||||
* グループがない状態でグループチャットを開始しようとするとフリーズする問題を修正
|
||||
* 通知インジケーターがずれる問題を修正
|
||||
* AP: 投稿を削除したときに関係する投稿の削除アクティビティが連合されない問題を修正
|
||||
|
||||
12.8.0 (2020/02/13)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* タイムラインなどを遡っているときは新しいアイテムが来てもスクロールしないように
|
||||
* 表示言語を切り替えられるように
|
||||
* グループに招待されたときの通知を追加
|
||||
* フランス語と関西弁を有効に
|
||||
* OSネイティブの絵文字を使用オプションを追加
|
||||
|
||||
### 🐛Fixes
|
||||
* リストを追加するとエラーが出る問題を修正
|
||||
* タイムラインを放置すると先頭の投稿が見えなくなるのを修正
|
||||
|
||||
12.7.1 (2020/02/11)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 非ログイン時にページが表示されない問題を修正
|
||||
|
||||
12.7.0 (2020/02/10)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Čeština"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Dansk"
|
||||
|
@ -1 +1,2 @@
|
||||
---
|
||||
_lang_: "Deutsch"
|
||||
|
@ -1,21 +1,6 @@
|
||||
---
|
||||
_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"
|
||||
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🚀"
|
||||
_lang_: "English"
|
||||
introMisskey: "Welcome! Misskey is an open source and also decentralized microblogging service.\nWrite the \"notes\" to share what is happening now, or send out your own words to everyone 📡\nWith the \"reactions\", you can add your feelings to everyone's notes faster than anyone 👍\nLet's explore the new world 🚀"
|
||||
monthAndDay: "{month}/{day}"
|
||||
search: "Search"
|
||||
notifications: "Notifications"
|
||||
@ -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"
|
||||
@ -326,6 +311,7 @@ aboutMisskey: "About Misskey"
|
||||
aboutMisskeyText: "Misskey is an open-source software developed by syuilo since 2014."
|
||||
misskeyMembers: "It is currently developed an maintained by the members listed below:"
|
||||
misskeySource: "Source code is available here:"
|
||||
misskeyTranslation: "Help us with your contribution to translate Misskey:"
|
||||
misskeyDonate: "Help us to keep improving the software by donating here:"
|
||||
morePatrons: "We really appreciate the support of many other helpers not listed here. Thank you! 🥰"
|
||||
patrons: "Backers"
|
||||
@ -340,7 +326,7 @@ registerSecurityKey: "Register a security key"
|
||||
lastUsed: "Last used"
|
||||
unregister: "Unregister"
|
||||
passwordLessLogin: "Set up password-less login"
|
||||
resetPassword: "Reste password"
|
||||
resetPassword: "Reset password"
|
||||
newPasswordIs: "The new password is \"{password}\""
|
||||
post: "Notes"
|
||||
posted: "Posted!"
|
||||
@ -370,11 +356,66 @@ 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"
|
||||
groupInvited: "Invited to group"
|
||||
aboutX: "About {x}"
|
||||
useOsNativeEmojis: "Use the OS native Emojis"
|
||||
noGroups: "No groups"
|
||||
joinOrCreateGroup: "Get invited to join the groups or you can create your own group."
|
||||
noHistory: "No history items"
|
||||
disableAnimatedMfm: "Disable MFM which has animations"
|
||||
doing: "On my way"
|
||||
category: "Category"
|
||||
tags: "Tags"
|
||||
docSource: "Source of this document"
|
||||
createAccount: "Create account"
|
||||
existingAcount: "Existing accounts"
|
||||
regenerate: "Regenerate"
|
||||
_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!"
|
||||
@ -442,6 +483,7 @@ _antennaSources:
|
||||
homeTimeline: "Notes from following users"
|
||||
users: "Notes from specific users"
|
||||
userList: "Notes from specific list"
|
||||
userGroup: "Notes from users in the specified group"
|
||||
_weekday:
|
||||
sunday: "Sunday"
|
||||
monday: "Monday"
|
||||
|
@ -1,21 +1,6 @@
|
||||
---
|
||||
_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"
|
||||
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.🚀"
|
||||
_lang_: "Español"
|
||||
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"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"
|
||||
notifications: "Notificaciones"
|
||||
@ -326,6 +311,7 @@ aboutMisskey: "Sobre Misskey"
|
||||
aboutMisskeyText: "Misskey es un software de código abierto, desarrollado por syuilo desde el 2014"
|
||||
misskeyMembers: "Es creado y mantenido por los miembros aquí listados:"
|
||||
misskeySource: "El código fuente está disponible aquí:"
|
||||
misskeyTranslation: "Ayúdanos con tu contribución para traducir Misskey:"
|
||||
misskeyDonate: "Puedes contribuir al desarrollo de Misskey donando aquí:"
|
||||
morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
|
||||
patrons: "Patrocinadores"
|
||||
@ -370,21 +356,98 @@ 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"
|
||||
groupInvited: "Invitado al grupo"
|
||||
aboutX: "Acerca de {x}"
|
||||
useOsNativeEmojis: "Usa los emojis nativos de la plataforma"
|
||||
noGroups: "Sin grupos"
|
||||
joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo."
|
||||
noHistory: "No hay datos en el historial"
|
||||
disableAnimatedMfm: "Deshabilitar MFM que tiene animaciones"
|
||||
doing: "Voy en camino"
|
||||
category: "Categoría"
|
||||
tags: "Etiqueta"
|
||||
docSource: "Fuente de este documento"
|
||||
createAccount: "Crear cuenta"
|
||||
existingAcount: "Cuentas existentes"
|
||||
regenerate: "Regenerar"
|
||||
_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"
|
||||
step1_2: "Esta imagen se llama \"Linea de tiempo\" y muestra en orden cronológico las \"notas\" tuyas y de la gente que \"sigues\""
|
||||
step1_3: "Si no estás escribiendo ninguna nota y no estás siguiendo a nadie, es esperable que no se muestre nada en la linea de tiempo"
|
||||
step2_1: "Antes de crear notas y seguir a alguien, primero vamos a crear tu perfil"
|
||||
step2_2: "Si provees información sobre quien eres, será más fácil para que otros usuarios te sigan"
|
||||
step3_1: "¿Has podido crear tu perfil sin problemas?"
|
||||
step3_2: "Con esto, prueba hacer una nota. Aprieta el botón con forma de lápiz que está arriba de la imagen y abre el formulario."
|
||||
step3_3: "Si has escrito el contenido, aprieta el botón que está arriba a la derecha del formulario para postear."
|
||||
step3_4: "¿No se te ocurre un contenido? Prueba con decir \"Empecé a usar Misskey\""
|
||||
step4_1: "¿Has posteado?"
|
||||
step4_2: "Si tu nota puede verse en la linea de tiempo, fue todo un éxito."
|
||||
step5_1: "Luego, ponte a seguir a otra gente y haz que tu linea de tiempo esté más animada."
|
||||
step5_2: "Puedes ver las notas destacadas en {featured} y desde allí seguir a usuarios que te importan. También puedes buscar usuario destacados en {explore}."
|
||||
step5_3: "Para seguir a un usuario, haz click en su avatar para ver su página de usuario y allí apretar el botón \"seguir\""
|
||||
step5_4: "De esa manera, puede pasar un tiempo hasta que el usuario apruebe al seguidor."
|
||||
step6_1: "Si puedes ver en la linea de tiempo las notas de otros usuarios, fue todo un éxito."
|
||||
step6_2: "En las notas de otros usuarios puedes añadir una \"reacción\", para poder responder rápidamente."
|
||||
step6_3: "Para añadir una reacción, haz click en el botón \"+\" de la nota y elige la reacción que prefieras."
|
||||
step7_1: "Así terminó la explicación del funcionamiento básico de Misskey. Eso fue todo."
|
||||
step7_2: "Si quieres conocer más sobre Misskey, prueba con la sección {help}."
|
||||
step7_3: "Así, disfruta de Misskey 🚀"
|
||||
_2fa:
|
||||
alreadyRegistered: "Ya has completado la configuración."
|
||||
registerDevice: "Registrar dispositivo"
|
||||
registerKey: "Registrar clave"
|
||||
step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
|
||||
step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
|
||||
step3: "Para terminar, ingrese el token mostrado en la aplicación."
|
||||
step4: "Ahora cuando inicie sesión, ingrese el mismo token"
|
||||
securityKeyInfo: "Se puede configurar el inicio de sesión usando una clave de seguridad de hardware que soporte FIDO2 o con un certificado de huella digital o con un PIN"
|
||||
_permissions:
|
||||
"read:account": "Ver información de la cuenta"
|
||||
"write:account": "Editar información de la cuenta"
|
||||
@ -396,6 +459,8 @@ _permissions:
|
||||
"write:favorites": "Addministrar favoritos"
|
||||
"read:following": "Ver información de seguidor"
|
||||
"write:following": "Seguir o dejar de seguir"
|
||||
"read:messaging": "Ver chat"
|
||||
"write:messaging": "Administrar chat"
|
||||
"read:mutes": "Ver usuarios silenciados"
|
||||
"write:mutes": "Administrar usuarios silenciados"
|
||||
"write:notes": "Crear/borrar notas"
|
||||
@ -418,6 +483,7 @@ _antennaSources:
|
||||
homeTimeline: "Notas de los usuarios que sigues"
|
||||
users: "Notas de un usuario o varios"
|
||||
userList: "Notas de los usuarios de una lista"
|
||||
userGroup: "Notas de los usuarios de una grupo"
|
||||
_weekday:
|
||||
sunday: "Domingo"
|
||||
monday: "Lunes"
|
||||
@ -471,6 +537,7 @@ _visibility:
|
||||
followersDescription: "Visible sólo para tus seguidores"
|
||||
specified: "Mensaje directo"
|
||||
specifiedDescription: "Visible sólo para los usuarios elegidos"
|
||||
localOnly: "Solo local"
|
||||
_postForm:
|
||||
replyPlaceholder: "Responder a esta nota"
|
||||
quotePlaceholder: "Citar esta nota"
|
||||
|
@ -1 +1,851 @@
|
||||
---
|
||||
_lang_: "Français"
|
||||
introMisskey: "Bienvenue! Misskey est un service de microblogage décentralisé open source.\nÉcrivez des «notes» pour partager ce qui vous arrive maintenant ou pour parler de vous à tout le monde 📡\nAvec la fonction «réactions», vous pouvez également ajouter une réaction rapide aux notes de chacun 👍\nExplorez un nouveau monde 🚀"
|
||||
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"
|
||||
intro: "L'installation de Misskey est terminée! Créons le compte administrateur."
|
||||
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"
|
||||
proxyRemoteFilesDescription: "Si vous activez ce paramètre, les fichiers distants non stockés ou supprimés en raison d'une capacité excédentaire seront affichés via un proxy local et généreront une miniature. Cela n'affectera pas le stockage du serveur."
|
||||
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"
|
||||
pinnedUsersDescription: "Décrivez les utilisateurs que vous souhaitez définir sur la page \"Découvrir\" séparés par une nouvelle ligne"
|
||||
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"
|
||||
aboutMisskeyText: "Misskey est un logiciel open source, développé par syuilo depuis 2014."
|
||||
misskeyMembers: "Il est développé et maintenu par les membres répertoriés ici:"
|
||||
misskeySource: "Le code source est disponible ici:"
|
||||
misskeyTranslation: "Aidez-nous avec votre contribution à traduire Misskey:"
|
||||
misskeyDonate: "Vous pouvez contribuer au développement de Misskey en faisant un don ici:"
|
||||
morePatrons: "Nous apprécions vraiment le soutien de nombreux autres les soutiens non répertoriés ici. Merci beaucoup à tous! 🥰"
|
||||
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"
|
||||
autoNoteWatchDescription: "Soyez informé des notes auxquelles vous avez réagi ou répondu."
|
||||
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"
|
||||
groupInvited: "Invité au groupe"
|
||||
aboutX: "À propos de {x}"
|
||||
useOsNativeEmojis: "Utilisez les emojis natifs de la plateforme"
|
||||
noGroups: "Pas de groupes"
|
||||
joinOrCreateGroup: "Soyez invité à rejoindre les groupes ou vous pouvez créer votre propre groupe."
|
||||
noHistory: "Pas d'historique"
|
||||
disableAnimatedMfm: "Désactiver MFM qui a des animations"
|
||||
doing: "Attends une seconde"
|
||||
category: "Catégories"
|
||||
tags: "Étiquettes"
|
||||
docSource: "Source de ce document"
|
||||
createAccount: "Créer compte"
|
||||
existingAcount: "Comptes existants"
|
||||
regenerate: "Régénérer"
|
||||
_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"
|
||||
step1_1: "Bienvenue,"
|
||||
_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"
|
||||
userGroup: "Notes pour les utilisateurs de la groupe 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"
|
||||
variables-info: "Vous pouvez créer une page dynamique à l'aide de variables. En tapant le <b>{nom de variable}</b> dans le texte, vous pouvez y incorporer la valeur de la variable. Par exemple, si dans le texte <b>Bonjour {chose} monde!</b> la valeur de la variable (chose) est <b>ai</b>, le texte devient est <b>Bonjour ai monde!</b>."
|
||||
variables-info2: "L'évaluation des variables (le calcul des valeurs) se fait de haut en bas, donc l'variable ne peut pas se référer à une autre qui est en dessous. Par exemple, lorsque les variables <b>A、B、C</b> sont définies, <b>C</b> peut faire référence à <b>A</b> ou <b>B</b>, mais <b>A</b> ne peut pas faire référence à <b>B</b> ou <b>C</b>."
|
||||
variables-info3: "Pour recevoir une entrée utilisateur, ajoutez un bloc \"Entrée\" sur la page et définissez le nom des variables que vous souhaitez stocker dans le champ \"Nom de la variable\" (les variables seront créées automatiquement). Les actions seront exécutées en fonction de l'entrée utilisateur de ces variables."
|
||||
variables-info4: "Les fonctions vous permettent d'organiser le processus de calcul des valeurs sous une forme réutilisable. Pour créer une fonction, créez une variable de type \"fonction\". Une fonction peut avoir un slot (argument) et sa valeur peut être utilisée comme variable dans la fonction. Il existe également une fonction qui prend une fonction comme argument dans la norme AiScript (appelée fonction d'ordre supérieur). En plus des fonctions prédéfinies, elles peuvent être définies instantanément dans ces emplacements de fonction d'ordre supérieur."
|
||||
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,69 @@ 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ネイティブの絵文字を使用"
|
||||
noGroups: "グループがありません"
|
||||
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
|
||||
noHistory: "履歴はありません"
|
||||
disableAnimatedMfm: "動きのあるMFMを無効にする"
|
||||
doing: "やっています"
|
||||
category: "カテゴリ"
|
||||
tags: "タグ"
|
||||
docSource: "このドキュメントのソース"
|
||||
createAccount: "アカウントを作成"
|
||||
existingAcount: "既存のアカウント"
|
||||
regenerate: "再生成"
|
||||
fontSize: "フォントサイズ"
|
||||
|
||||
_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の使い方"
|
||||
@ -449,6 +491,7 @@ _antennaSources:
|
||||
homeTimeline: "フォローしているユーザーのノート"
|
||||
users: "指定した一人または複数のユーザーのノート"
|
||||
userList: "指定したリストのユーザーのノート"
|
||||
userGroup: "指定したグループのユーザーのノート"
|
||||
|
||||
_weekday:
|
||||
sunday: "日曜日"
|
||||
|
@ -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: "검색"
|
||||
@ -323,9 +308,10 @@ popularTags: "인기 태그"
|
||||
userList: "리스트"
|
||||
about: "정보"
|
||||
aboutMisskey: "Misskey에 대하여"
|
||||
aboutMisskeyText: "Misskey는 syuilo에 의해 2014년부터 개발된 오픈 소스 소프트웨어입니다."
|
||||
misskeyMembers: "현재는 아래 멤버들에 의해 개발 및 유지보수 되고 있습니다."
|
||||
misskeySource: "소스 코드는 여기에서 보실 수 있습니다:"
|
||||
aboutMisskeyText: "Misskey는 syuilo에 의해서 2014년부터 개발되어 온 오픈소스 소프트웨어 입니다."
|
||||
misskeyMembers: "현재는 아래 멤버들에 의해 개발 및 유지보수 되고 있습니다:"
|
||||
misskeySource: "소스코드는 여기에 공개되어 있습니다:"
|
||||
misskeyTranslation: "Misskey의 번역을 함께해 주시길 부탁드립니다:"
|
||||
misskeyDonate: "Misskey에 기부하심으로써 개발에 도움을 주실 수 있습니다:"
|
||||
morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
|
||||
patrons: "후원자들"
|
||||
@ -370,11 +356,66 @@ 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 기본 이모지를 사용"
|
||||
noGroups: "그룹이 없습니다"
|
||||
joinOrCreateGroup: "다른 그룹의 초대를 받거나, 직접 새 그룹을 만들어 보세요."
|
||||
noHistory: "기록이 없습니다"
|
||||
disableAnimatedMfm: "움직임이 있는 MFM을 비활성화"
|
||||
doing: "잠시만요"
|
||||
category: "카테고리"
|
||||
tags: "태그"
|
||||
docSource: "이 문서의 소스"
|
||||
createAccount: "계정 만들기"
|
||||
existingAcount: "기존 계정"
|
||||
regenerate: "다시 생성"
|
||||
_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: "환영합니다!"
|
||||
@ -406,7 +447,7 @@ _2fa:
|
||||
step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
|
||||
step3: "앱에 표시된 토큰을 입력하시면 완료됩니다."
|
||||
step4: "다음 로그인부터는 토큰을 입력해야 합니다."
|
||||
securityKeyInfo: "FIDO2를 지원하는 하드웨어 시큐리티 키 혹은 휴대전화의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
|
||||
securityKeyInfo: "FIDO2를 지원하는 하드웨어 보안 키 혹은 디바이스의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다."
|
||||
_permissions:
|
||||
"read:account": "계정의 정보를 봅니다"
|
||||
"write:account": "계정의 정보를 변경합니다"
|
||||
@ -442,6 +483,7 @@ _antennaSources:
|
||||
homeTimeline: "팔로우중인 유저의 노트"
|
||||
users: "지정한 한 명 혹은 여러 명의 유저의 노트"
|
||||
userList: "지정한 리스트에 속한 유저의 노트"
|
||||
userGroup: "지정한 그룹에 속한 유저의 노트"
|
||||
_weekday:
|
||||
sunday: "일요일"
|
||||
monday: "월요일"
|
||||
|
@ -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: "搜索"
|
||||
@ -198,7 +183,7 @@ newPassword: "新密码"
|
||||
newPasswordRetype: "重新输入密码:"
|
||||
attachFile: "插入附件"
|
||||
more: "更多!"
|
||||
featured: "高亮"
|
||||
featured: "热门"
|
||||
usernameOrUserId: "用户名或用户ID"
|
||||
noSuchUser: "用户不存在"
|
||||
lookup: "查询"
|
||||
@ -249,6 +234,8 @@ banner: "Banner"
|
||||
nsfw: "阅读注意"
|
||||
disconnectedFromServer: "已从服务器断开连接"
|
||||
reloadConfirm: "确定要重新加载吗"
|
||||
watch: "关注"
|
||||
unwatch: "取消关注"
|
||||
accept: "允许"
|
||||
reject: "拒绝"
|
||||
instanceName: "实例名称"
|
||||
@ -301,6 +288,8 @@ popularUsers: "热门用户"
|
||||
recentlyUpdatedUsers: "最近投稿用户"
|
||||
recentlyRegisteredUsers: "最近登录用户"
|
||||
recentlyDiscoveredUsers: "最近发现的用户"
|
||||
exploreUsersCount: "有{count}个用户"
|
||||
exploreFediverse: "探索Fediverse"
|
||||
popularTags: "热门标签"
|
||||
userList: "列表"
|
||||
about: "关于"
|
||||
@ -325,6 +314,8 @@ newPasswordIs: "新的密码是「{password}」"
|
||||
post: "投稿"
|
||||
posted: "已投稿"
|
||||
autoReloadWhenDisconnected: "断开连接时自动重新加载"
|
||||
autoNoteWatch: "自动关注帖子"
|
||||
autoNoteWatchDescription: "让您能够收到关于「反应」和回复其他用户的帖子的通知。"
|
||||
reduceUiAnimation: "减少UI动画"
|
||||
share: "分享"
|
||||
notFound: "未找到"
|
||||
@ -344,11 +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: "欢迎!"
|
||||
@ -431,6 +441,7 @@ _visibility:
|
||||
home: "首页"
|
||||
followers: "关注者"
|
||||
specified: "指定用户"
|
||||
localOnly: "仅限本地"
|
||||
_profile:
|
||||
name: "名称"
|
||||
username: "用户名"
|
||||
@ -445,10 +456,17 @@ _exportOrImport:
|
||||
blockingList: "屏蔽"
|
||||
userLists: "列表"
|
||||
_charts:
|
||||
federationInstancesIncDec: "联合:增加/减少"
|
||||
federationInstancesTotal: "联合总数"
|
||||
usersIncDec: "用户数量:增加/减少"
|
||||
usersTotal: "用户总数"
|
||||
activeUsers: "活跃用户数"
|
||||
notesIncDec: "帖子:增加/减少"
|
||||
notesTotal: "帖子总数"
|
||||
_instanceCharts:
|
||||
users: "用户数量:增加/减少"
|
||||
usersTotal: "用户总数"
|
||||
notes: "帖子:增加/减少"
|
||||
ff: "关注/被关注:数量变化"
|
||||
ffTotal: "关注/被关注:总数"
|
||||
cacheSize: "缓存大小:增加/减少"
|
||||
@ -460,6 +478,7 @@ _timelines:
|
||||
_pages:
|
||||
newPage: "创建页面"
|
||||
editPage: "编辑页面"
|
||||
readPage: "查看源"
|
||||
page-created: "页面已创建"
|
||||
page-updated: "页面已更新"
|
||||
name-already-exists: "该页面URL已存在"
|
||||
@ -501,7 +520,10 @@ _pages:
|
||||
posted-from-post-form: "已发布"
|
||||
blocks:
|
||||
text: "文本"
|
||||
textarea: "文本区域"
|
||||
section: "章节"
|
||||
image: "图片"
|
||||
button: "按钮"
|
||||
if: "如果"
|
||||
_if:
|
||||
variable: "变量"
|
||||
@ -710,11 +732,14 @@ _pages:
|
||||
ref: "变量"
|
||||
fn: "函数"
|
||||
_fn:
|
||||
slots: "槽函数"
|
||||
slots-info: "请使用换行符分隔每个槽函数"
|
||||
arg1: "输出"
|
||||
for: "重复"
|
||||
_for:
|
||||
arg1: "次数"
|
||||
arg2: "处理"
|
||||
typeError: "槽函数{slot}需要传入“{expect}”,但是实际传入为“{actual}”!"
|
||||
thereIsEmptySlot: "槽函数{slot}为空!"
|
||||
types:
|
||||
string: "文字"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
28
migration/1581695816408-user-group-antenna.ts
Normal file
28
migration/1581695816408-user-group-antenna.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class userGroupAntenna1581695816408 implements MigrationInterface {
|
||||
name = 'userGroupAntenna1581695816408'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`, undefined);
|
||||
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, undefined);
|
||||
await queryRunner.query(`CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list', 'group')`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, undefined);
|
||||
await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying array NOT NULL DEFAULT '{}'`, undefined);
|
||||
await queryRunner.query(`CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list')`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, undefined);
|
||||
await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined);
|
||||
await queryRunner.query(`ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, undefined);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`, undefined);
|
||||
}
|
||||
|
||||
}
|
14
migration/1581708415836-drive-user-folder-id-index.ts
Normal file
14
migration/1581708415836-drive-user-folder-id-index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class driveUserFolderIdIndex1581708415836 implements MigrationInterface {
|
||||
name = 'driveUserFolderIdIndex1581708415836'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE INDEX "IDX_55720b33a61a7c806a8215b825" ON "drive_file" ("userId", "folderId", "id") `, undefined);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_55720b33a61a7c806a8215b825"`, undefined);
|
||||
}
|
||||
|
||||
}
|
54
package.json
54
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||
"version": "12.7.0",
|
||||
"version": "12.10.0",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -30,16 +30,16 @@
|
||||
"lodash": "^4.17.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "7.5.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.26",
|
||||
"@fortawesome/free-brands-svg-icons": "5.12.0",
|
||||
"@fortawesome/free-regular-svg-icons": "5.12.0",
|
||||
"@fortawesome/free-solid-svg-icons": "5.12.0",
|
||||
"@elastic/elasticsearch": "7.6.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.27",
|
||||
"@fortawesome/free-brands-svg-icons": "5.12.1",
|
||||
"@fortawesome/free-regular-svg-icons": "5.12.1",
|
||||
"@fortawesome/free-solid-svg-icons": "5.12.1",
|
||||
"@fortawesome/vue-fontawesome": "0.1.9",
|
||||
"@juggle/resize-observer": "3.0.2",
|
||||
"@koa/cors": "3.0.0",
|
||||
"@koa/multer": "2.0.2",
|
||||
"@koa/router": "8.0.6",
|
||||
"@koa/router": "8.0.8",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.12.0",
|
||||
"@types/cbor": "5.0.0",
|
||||
@ -54,7 +54,7 @@
|
||||
"@types/js-yaml": "3.12.2",
|
||||
"@types/jsdom": "12.2.4",
|
||||
"@types/katex": "0.11.0",
|
||||
"@types/koa": "2.11.0",
|
||||
"@types/koa": "2.11.1",
|
||||
"@types/koa-bodyparser": "4.3.0",
|
||||
"@types/koa-compress": "2.0.9",
|
||||
"@types/koa-cors": "0.0.0",
|
||||
@ -69,7 +69,7 @@
|
||||
"@types/lolex": "5.1.0",
|
||||
"@types/markdown-it": "0.0.9",
|
||||
"@types/mocha": "7.0.1",
|
||||
"@types/node": "13.7.0",
|
||||
"@types/node": "13.7.1",
|
||||
"@types/nodemailer": "6.4.0",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/oauth": "0.9.1",
|
||||
@ -80,7 +80,7 @@
|
||||
"@types/qrcode": "1.3.4",
|
||||
"@types/random-seed": "0.3.3",
|
||||
"@types/ratelimiter": "2.1.28",
|
||||
"@types/redis": "2.8.14",
|
||||
"@types/redis": "2.8.15",
|
||||
"@types/rename": "1.0.1",
|
||||
"@types/request": "2.48.4",
|
||||
"@types/request-promise-native": "1.0.17",
|
||||
@ -95,18 +95,18 @@
|
||||
"@types/tmp": "0.1.0",
|
||||
"@types/uuid": "3.4.7",
|
||||
"@types/web-push": "3.3.0",
|
||||
"@types/webpack": "4.41.3",
|
||||
"@types/webpack": "4.41.6",
|
||||
"@types/webpack-stream": "3.2.10",
|
||||
"@types/websocket": "1.0.0",
|
||||
"@types/ws": "7.2.1",
|
||||
"@typescript-eslint/parser": "2.18.0",
|
||||
"@typescript-eslint/parser": "2.19.2",
|
||||
"agentkeepalive": "4.1.0",
|
||||
"animejs": "3.1.0",
|
||||
"apexcharts": "3.15.3",
|
||||
"apexcharts": "3.15.6",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.610.0",
|
||||
"aws-sdk": "2.617.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bull": "3.12.1",
|
||||
"cafy": "15.2.1",
|
||||
@ -115,7 +115,7 @@
|
||||
"chalk": "3.0.0",
|
||||
"chart.js": "2.9.3",
|
||||
"cli-highlight": "2.1.4",
|
||||
"commander": "4.1.0",
|
||||
"commander": "4.1.1",
|
||||
"content-disposition": "0.5.3",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "3.4.2",
|
||||
@ -128,7 +128,7 @@
|
||||
"eventemitter3": "4.0.0",
|
||||
"feed": "4.1.0",
|
||||
"fibers": "4.0.2",
|
||||
"file-type": "13.1.2",
|
||||
"file-type": "14.1.2",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"glob": "7.1.6",
|
||||
"gulp": "4.0.2",
|
||||
@ -144,12 +144,12 @@
|
||||
"hard-source-webpack-plugin": "0.13.1",
|
||||
"html-minifier": "4.0.0",
|
||||
"http-signature": "1.3.1",
|
||||
"https-proxy-agent": "4.0.0",
|
||||
"https-proxy-agent": "5.0.0",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"is-root": "2.1.0",
|
||||
"is-svg": "4.2.1",
|
||||
"js-yaml": "3.13.1",
|
||||
"jsdom": "16.0.1",
|
||||
"jsdom": "16.1.0",
|
||||
"json5": "2.1.1",
|
||||
"json5-loader": "3.0.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
@ -198,16 +198,16 @@
|
||||
"randomcolor": "0.5.4",
|
||||
"ratelimiter": "3.4.0",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "4.3.0",
|
||||
"redis": "2.8.0",
|
||||
"reconnecting-websocket": "4.4.0",
|
||||
"redis": "3.0.2",
|
||||
"redis-lock": "0.1.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rename": "1.0.4",
|
||||
"request": "2.88.0",
|
||||
"request": "2.88.2",
|
||||
"request-promise-native": "1.0.8",
|
||||
"request-stats": "3.0.0",
|
||||
"require-all": "3.0.0",
|
||||
"rimraf": "3.0.1",
|
||||
"rimraf": "3.0.2",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"sass": "1.25.0",
|
||||
@ -221,7 +221,7 @@
|
||||
"style-loader": "1.1.3",
|
||||
"summaly": "2.3.1",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "4.21.1",
|
||||
"systeminformation": "4.21.2",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "2.3.4",
|
||||
"textarea-caret": "3.1.0",
|
||||
@ -245,7 +245,7 @@
|
||||
"vue-cropperjs": "4.0.1",
|
||||
"vue-i18n": "8.15.3",
|
||||
"vue-json-pretty": "1.6.3",
|
||||
"vue-loader": "15.8.3",
|
||||
"vue-loader": "15.9.0",
|
||||
"vue-marquee-text-component": "1.1.1",
|
||||
"vue-meta": "2.3.2",
|
||||
"vue-prism-component": "1.1.1",
|
||||
@ -256,10 +256,10 @@
|
||||
"vue-template-compiler": "2.6.11",
|
||||
"vuedraggable": "2.23.2",
|
||||
"vuex": "3.1.2",
|
||||
"vuex-persistedstate": "2.7.0",
|
||||
"vuex-persistedstate": "2.7.1",
|
||||
"web-push": "3.4.3",
|
||||
"webpack": "4.41.5",
|
||||
"webpack-cli": "3.3.10",
|
||||
"webpack": "4.41.6",
|
||||
"webpack-cli": "3.3.11",
|
||||
"websocket": "1.0.31",
|
||||
"ws": "7.2.1",
|
||||
"xev": "2.0.1"
|
||||
|
@ -25,6 +25,7 @@
|
||||
<input type="search" :placeholder="$t('search')" v-model="searchQuery" v-autocomplete="{ model: 'searchQuery' }" :disabled="searchWait" @keypress="searchKeypress"/>
|
||||
</div>
|
||||
<button v-if="$store.getters.isSignedIn" class="post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
|
||||
<x-clock v-if="isDesktop" class="clock"/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -107,7 +108,7 @@
|
||||
|
||||
<div class="widgets">
|
||||
<div ref="widgets" :class="{ edit: widgetsEditMode }">
|
||||
<template v-if="enableWidgets && $store.getters.isSignedIn">
|
||||
<template v-if="isDesktop && $store.getters.isSignedIn">
|
||||
<template v-if="widgetsEditMode">
|
||||
<mk-button primary @click="addWidget" class="add"><fa :icon="faPlus"/></mk-button>
|
||||
<x-draggable
|
||||
@ -157,7 +158,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';
|
||||
@ -166,6 +167,7 @@ export default Vue.extend({
|
||||
i18n,
|
||||
|
||||
components: {
|
||||
XClock: () => import('./components/header-clock.vue').then(m => m.default),
|
||||
XNotifications: () => import('./components/notifications.vue').then(m => m.default),
|
||||
MkButton: () => import('./components/ui/button.vue').then(m => m.default),
|
||||
XDraggable: () => import('vuedraggable'),
|
||||
@ -184,7 +186,7 @@ export default Vue.extend({
|
||||
searchQuery: '',
|
||||
searchWait: false,
|
||||
widgetsEditMode: false,
|
||||
enableWidgets: window.innerWidth >= 1100,
|
||||
isDesktop: window.innerWidth >= 1100,
|
||||
canBack: false,
|
||||
disconnectedDialog: null as Promise<void> | null,
|
||||
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer
|
||||
@ -247,11 +249,15 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
this.$root.stream.on('_disconnected_', () => {
|
||||
if (!this.disconnectedDialog) {
|
||||
if (this.$store.state.device.autoReload) {
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
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,18 +269,18 @@ export default Vue.extend({
|
||||
}
|
||||
this.disconnectedDialog = null;
|
||||
});
|
||||
}
|
||||
}, 150)
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// https://stackoverflow.com/questions/33891709/when-flexbox-items-wrap-in-column-mode-container-does-not-grow-its-width
|
||||
if (this.enableWidgets) {
|
||||
if (this.isDesktop) {
|
||||
const adjustWidgetsWidth = () => {
|
||||
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);
|
||||
@ -289,7 +295,7 @@ export default Vue.extend({
|
||||
const ro = new ResizeObserver((entries, observer) => {
|
||||
adjustTitlePosition();
|
||||
});
|
||||
|
||||
|
||||
ro.observe(this.$refs.contents);
|
||||
|
||||
window.addEventListener('resize', adjustTitlePosition);
|
||||
@ -344,7 +350,7 @@ export default Vue.extend({
|
||||
const accountItems = accounts.map(account => ({
|
||||
type: 'user',
|
||||
user: account,
|
||||
action: () => { this.switchAccount(account) }
|
||||
action: () => { this.switchAccount(account); }
|
||||
}));
|
||||
|
||||
this.$root.menu({
|
||||
@ -358,12 +364,28 @@ export default Vue.extend({
|
||||
text: this.$t('settings'),
|
||||
to: '/my/settings',
|
||||
icon: faCog,
|
||||
}, null, {
|
||||
}, null, ...accountItems, {
|
||||
type: 'item',
|
||||
text: this.$t('addAcount'),
|
||||
icon: faPlus,
|
||||
action: () => { this.addAcount() },
|
||||
}], ...accountItems],
|
||||
text: this.$t('addAcount'),
|
||||
action: () => {
|
||||
this.$root.menu({
|
||||
items: [{
|
||||
type: 'item',
|
||||
text: this.$t('existingAcount'),
|
||||
action: () => { this.addAcount(); },
|
||||
}, {
|
||||
type: 'item',
|
||||
text: this.$t('createAccount'),
|
||||
action: () => { this.createAccount(); },
|
||||
}],
|
||||
align: 'left',
|
||||
fixed: true,
|
||||
width: 240,
|
||||
source: ev.currentTarget || ev.target,
|
||||
});
|
||||
},
|
||||
}]],
|
||||
align: 'left',
|
||||
fixed: true,
|
||||
width: 240,
|
||||
@ -477,9 +499,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,
|
||||
@ -498,9 +525,25 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
async switchAccount(account) {
|
||||
const token = this.$store.state.device.accounts.find(x => x.id === account.id).token;
|
||||
this.$root.api('i', {}, token).then(i => {
|
||||
async createAccount() {
|
||||
this.$root.new(await import('./components/signup-dialog.vue').then(m => m.default)).$once('signup', res => {
|
||||
this.$store.dispatch('addAcount', res);
|
||||
this.switchAccountWithToken(res.i);
|
||||
});
|
||||
},
|
||||
|
||||
async switchAccount(account: any) {
|
||||
const token = this.$store.state.device.accounts.find((x: any) => x.id === account.id).token;
|
||||
this.switchAccountWithToken(token);
|
||||
},
|
||||
|
||||
switchAccountWithToken(token: string) {
|
||||
this.$root.dialog({
|
||||
type: 'waiting',
|
||||
iconOnly: true
|
||||
});
|
||||
|
||||
this.$root.api('i', {}, token).then((i: any) => {
|
||||
this.$store.dispatch('switchAccount', {
|
||||
...i,
|
||||
token: token
|
||||
@ -547,6 +590,7 @@ export default Vue.extend({
|
||||
'calendar',
|
||||
'rss',
|
||||
'trends',
|
||||
'clock'
|
||||
];
|
||||
|
||||
this.$root.menu({
|
||||
@ -744,10 +788,9 @@ export default Vue.extend({
|
||||
position: relative;
|
||||
|
||||
> input {
|
||||
$margin: 8px;
|
||||
width: 200px;
|
||||
width: 220px;
|
||||
box-sizing: border-box;
|
||||
margin-right: $margin;
|
||||
margin-right: 8px;
|
||||
padding: 0 12px 0 42px;
|
||||
font-size: 1rem;
|
||||
line-height: 38px;
|
||||
@ -778,6 +821,10 @@ export default Vue.extend({
|
||||
border-radius: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
> .clock {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
src/client/assets/remove.png
Normal file
BIN
src/client/assets/remove.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 424 B |
144
src/client/components/analog-clock.vue
Normal file
144
src/client/components/analog-clock.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<svg class="mbcofsoe" viewBox="0 0 10 10" preserveAspectRatio="none">
|
||||
<circle v-for="(angle, i) in graduations"
|
||||
:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))"
|
||||
:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))"
|
||||
:r="i % 5 == 0 ? 0.125 : 0.05"
|
||||
:fill="i % 5 == 0 ? majorGraduationColor : minorGraduationColor"
|
||||
:key="i"/>
|
||||
|
||||
<line
|
||||
:x1="5 - (Math.sin(sAngle) * (sHandLengthRatio * handsTailLength))"
|
||||
:y1="5 + (Math.cos(sAngle) * (sHandLengthRatio * handsTailLength))"
|
||||
:x2="5 + (Math.sin(sAngle) * ((sHandLengthRatio * 5) - handsPadding))"
|
||||
:y2="5 - (Math.cos(sAngle) * ((sHandLengthRatio * 5) - handsPadding))"
|
||||
:stroke="sHandColor"
|
||||
stroke-width="0.05"/>
|
||||
|
||||
<line
|
||||
:x1="5 - (Math.sin(mAngle) * (mHandLengthRatio * handsTailLength))"
|
||||
:y1="5 + (Math.cos(mAngle) * (mHandLengthRatio * handsTailLength))"
|
||||
:x2="5 + (Math.sin(mAngle) * ((mHandLengthRatio * 5) - handsPadding))"
|
||||
:y2="5 - (Math.cos(mAngle) * ((mHandLengthRatio * 5) - handsPadding))"
|
||||
:stroke="mHandColor"
|
||||
stroke-width="0.1"/>
|
||||
|
||||
<line
|
||||
:x1="5 - (Math.sin(hAngle) * (hHandLengthRatio * handsTailLength))"
|
||||
:y1="5 + (Math.cos(hAngle) * (hHandLengthRatio * handsTailLength))"
|
||||
:x2="5 + (Math.sin(hAngle) * ((hHandLengthRatio * 5) - handsPadding))"
|
||||
:y2="5 - (Math.cos(hAngle) * ((hHandLengthRatio * 5) - handsPadding))"
|
||||
:stroke="hHandColor"
|
||||
stroke-width="0.1"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
smooth: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
now: new Date(),
|
||||
enabled: true,
|
||||
|
||||
graduationsPadding: 0.5,
|
||||
handsPadding: 1,
|
||||
handsTailLength: 0.7,
|
||||
hHandLengthRatio: 0.75,
|
||||
mHandLengthRatio: 1,
|
||||
sHandLengthRatio: 1
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dark(): boolean {
|
||||
return tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--bg')).isDark();
|
||||
},
|
||||
|
||||
majorGraduationColor(): string {
|
||||
return this.dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||
},
|
||||
minorGraduationColor(): string {
|
||||
return this.dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
},
|
||||
|
||||
sHandColor(): string {
|
||||
return this.dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
|
||||
},
|
||||
mHandColor(): string {
|
||||
return tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--fg')).toHexString();
|
||||
},
|
||||
hHandColor(): string {
|
||||
return tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')).toHexString();
|
||||
},
|
||||
|
||||
ms(): number {
|
||||
return this.now.getMilliseconds() * (this.smooth ? 1 : 0);
|
||||
},
|
||||
s(): number {
|
||||
return this.now.getSeconds();
|
||||
},
|
||||
m(): number {
|
||||
return this.now.getMinutes();
|
||||
},
|
||||
h(): number {
|
||||
return this.now.getHours();
|
||||
},
|
||||
|
||||
hAngle(): number {
|
||||
return Math.PI * (this.h % 12 + (this.m + (this.s + this.ms / 1000) / 60) / 60) / 6;
|
||||
},
|
||||
mAngle(): number {
|
||||
return Math.PI * (this.m + (this.s + this.ms / 1000) / 60) / 30;
|
||||
},
|
||||
sAngle(): number {
|
||||
return Math.PI * (this.s + this.ms / 1000) / 30;
|
||||
},
|
||||
|
||||
graduations(): any {
|
||||
const angles = [];
|
||||
for (let i = 0; i < 60; i++) {
|
||||
const angle = Math.PI * i / 30;
|
||||
angles.push(angle);
|
||||
}
|
||||
|
||||
return angles;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const update = () => {
|
||||
if (this.enabled) {
|
||||
this.tick();
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
};
|
||||
update();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.enabled = false;
|
||||
},
|
||||
|
||||
methods: {
|
||||
tick() {
|
||||
this.now = new Date();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mbcofsoe {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
@ -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="" class="_ghost"/>
|
||||
<p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p>
|
||||
<mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button>
|
||||
</div>
|
||||
@ -45,8 +45,6 @@ export default Vue.extend({
|
||||
height: 150px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -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>
|
||||
|
||||
|
100
src/client/components/header-clock.vue
Normal file
100
src/client/components/header-clock.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="eqryymyo">
|
||||
<div class="header">
|
||||
<time ref="time" class="_ghost">
|
||||
<span class="yyyymmdd">{{ yyyy }}/{{ mm }}/{{ dd }}</span>
|
||||
<br>
|
||||
<span class="hhnn">{{ hh }}<span :style="{ visibility: now.getSeconds() % 2 == 0 ? 'visible' : 'hidden' }">:</span>{{ nn }}</span>
|
||||
</time>
|
||||
</div>
|
||||
<div class="content _panel">
|
||||
<mk-clock/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import MkClock from './analog-clock.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
MkClock
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
now: new Date(),
|
||||
clock: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
yyyy(): number {
|
||||
return this.now.getFullYear();
|
||||
},
|
||||
mm(): string {
|
||||
return ('0' + (this.now.getMonth() + 1)).slice(-2);
|
||||
},
|
||||
dd(): string {
|
||||
return ('0' + this.now.getDate()).slice(-2);
|
||||
},
|
||||
hh(): string {
|
||||
return ('0' + this.now.getHours()).slice(-2);
|
||||
},
|
||||
nn(): string {
|
||||
return ('0' + this.now.getMinutes()).slice(-2);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.tick();
|
||||
this.clock = setInterval(this.tick, 1000);
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.clock);
|
||||
},
|
||||
methods: {
|
||||
tick() {
|
||||
this.now = new Date();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.eqryymyo {
|
||||
display: inline-block;
|
||||
overflow: visible;
|
||||
|
||||
> .header {
|
||||
padding: 0 12px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
|
||||
&:hover + .content {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
> time {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
height: 48px;
|
||||
|
||||
> .yyyymmdd {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .content {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: auto;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
margin: 16px 0 0 0;
|
||||
padding: 16px;
|
||||
width: 230px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -125,11 +125,13 @@ export default Vue.extend({
|
||||
|
||||
> .item {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 8px 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
font-size: 0.9em;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -82,10 +82,10 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
attrs: {
|
||||
style: `display: inline-block; font-size: 150%;`
|
||||
},
|
||||
directives: [this.$store.state.settings.disableAnimatedMfm ? {} : {
|
||||
directives: [this.$store.state.device.animatedMfm ? {
|
||||
name: 'animate-css',
|
||||
value: { classes: 'tada', iteration: 'infinite' }
|
||||
}]
|
||||
}: {}]
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
@ -110,10 +110,10 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
attrs: {
|
||||
style: 'display: inline-block;'
|
||||
},
|
||||
directives: [this.$store.state.settings.disableAnimatedMfm ? {} : {
|
||||
directives: [this.$store.state.device.animatedMfm ? {
|
||||
name: 'animate-css',
|
||||
value: { classes: 'rubberBand', iteration: 'infinite' }
|
||||
}]
|
||||
} : {}]
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
@ -122,9 +122,8 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
token.node.props.attr == 'left' ? 'reverse' :
|
||||
token.node.props.attr == 'alternate' ? 'alternate' :
|
||||
'normal';
|
||||
const style = (this.$store.state.settings.disableAnimatedMfm)
|
||||
? ''
|
||||
: `animation: spin 1.5s linear infinite; animation-direction: ${direction};`;
|
||||
const style = this.$store.state.device.animatedMfm
|
||||
? `animation: spin 1.5s linear infinite; animation-direction: ${direction};` : '';
|
||||
return (createElement as any)('span', {
|
||||
attrs: {
|
||||
style: 'display: inline-block;' + style
|
||||
@ -135,7 +134,7 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
case 'jump': {
|
||||
return (createElement as any)('span', {
|
||||
attrs: {
|
||||
style: (this.$store.state.settings.disableAnimatedMfm) ? 'display: inline-block;' : 'display: inline-block; animation: jump 0.75s linear infinite;'
|
||||
style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: jump 0.75s linear infinite;' : 'display: inline-block;'
|
||||
},
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
@ -863,7 +863,7 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--mkykhqkw);
|
||||
color: var(--fgHighlighted);
|
||||
}
|
||||
|
||||
> .count {
|
||||
|
@ -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="" class="_ghost"/>
|
||||
<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;
|
||||
@ -94,8 +88,6 @@ export default Vue.extend({
|
||||
height: 128px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,23 +105,11 @@ export default Vue.extend({
|
||||
}
|
||||
}
|
||||
|
||||
> footer {
|
||||
text-align: center;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> button {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
border-radius: var(--radius);
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
> .more > .button {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
height: 48px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</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)';
|
||||
el.addEventListener('transitionend', () => {
|
||||
el.style.transition = '';
|
||||
this.$options.i--;
|
||||
done();
|
||||
}, { once: true });
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => { // 必要
|
||||
el.style.opacity = 1;
|
||||
el.style.transform = 'translateY(0px)';
|
||||
});
|
||||
}
|
||||
},
|
||||
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
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<x-window @closed="() => { $emit('closed'); destroyDom(); }">
|
||||
<x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }">
|
||||
<template #header>{{ $t('signup') }}</template>
|
||||
<x-signup/>
|
||||
<x-signup :auto-set="autoSet" @signup="onSignup"/>
|
||||
</x-window>
|
||||
</template>
|
||||
|
||||
@ -18,5 +18,20 @@ export default Vue.extend({
|
||||
XSignup,
|
||||
XWindow,
|
||||
},
|
||||
|
||||
props: {
|
||||
autoSet: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onSignup(res) {
|
||||
this.$emit('signup', res);
|
||||
this.$refs.window.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -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,15 @@ export default Vue.extend({
|
||||
passwordRetypeState: null,
|
||||
submitting: false,
|
||||
ToSAgreement: false,
|
||||
faLock, faExclamationTriangle, faSpinner, faCheck
|
||||
faLock, faExclamationTriangle, faSpinner, faCheck, faKey
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
autoSet: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
|
||||
@ -98,6 +105,15 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.autoSet) {
|
||||
this.$once('signup', res => {
|
||||
localStorage.setItem('i', res.i);
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
const script = document.createElement('script');
|
||||
@ -167,15 +183,14 @@ export default Vue.extend({
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}).then(res => {
|
||||
localStorage.setItem('i', res.i);
|
||||
location.href = '/';
|
||||
this.$emit('signup', res);
|
||||
});
|
||||
}).catch(() => {
|
||||
this.submitting = false;
|
||||
|
||||
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() {
|
||||
|
@ -105,7 +105,7 @@ export default Vue.extend({
|
||||
padding: 8px 14px;
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
font-size: 0.9em;
|
||||
line-height: 24px;
|
||||
box-shadow: none;
|
||||
text-decoration: none;
|
||||
|
@ -239,6 +239,10 @@ export default Vue.extend({
|
||||
position: relative;
|
||||
margin: 32px 0;
|
||||
|
||||
&:not(.inline):first-child {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
> .icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -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>
|
||||
|
@ -77,6 +77,10 @@ export default Vue.extend({
|
||||
position: relative;
|
||||
margin: 32px 0;
|
||||
|
||||
&:not(.inline):first-child {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
> .icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -85,6 +85,10 @@ export default Vue.extend({
|
||||
margin: 42px 0 32px 0;
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@ -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)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = langs.map(x => x[0]).find(x => x.split('-')[0] == navigator.language);
|
||||
if (lang == null) {
|
||||
if (langs.map(x => x[0]).includes(navigator.language)) {
|
||||
lang = navigator.language;
|
||||
} else {
|
||||
lang = langs.map(x => x[0]).find(x => x.split('-')[0] == navigator.language);
|
||||
|
||||
if (lang == null) {
|
||||
// Fallback
|
||||
lang = 'en-US';
|
||||
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 {
|
||||
|
@ -43,6 +43,15 @@ export default class MiOS extends EventEmitter {
|
||||
*/
|
||||
@autobind
|
||||
public async init(callback) {
|
||||
const finish = () => {
|
||||
callback();
|
||||
|
||||
this.store.dispatch('instance/fetch').then(() => {
|
||||
// Init service worker
|
||||
if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
|
||||
});
|
||||
};
|
||||
|
||||
this.store = initStore(this);
|
||||
|
||||
// ユーザーをフェッチしてコールバックする
|
||||
@ -50,7 +59,7 @@ export default class MiOS extends EventEmitter {
|
||||
let me = null;
|
||||
|
||||
// Return when not signed in
|
||||
if (token == null) {
|
||||
if (token == null || token === 'null') {
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -95,12 +104,7 @@ export default class MiOS extends EventEmitter {
|
||||
this.initStream();
|
||||
|
||||
// Finish init
|
||||
callback();
|
||||
|
||||
this.store.dispatch('instance/fetch').then(() => {
|
||||
// Init service worker
|
||||
if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
|
||||
});
|
||||
finish();
|
||||
};
|
||||
|
||||
// キャッシュがあったとき
|
||||
@ -129,7 +133,7 @@ export default class MiOS extends EventEmitter {
|
||||
this.initStream();
|
||||
|
||||
// Finish init
|
||||
callback();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
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>
|
||||
|
@ -3,9 +3,13 @@
|
||||
<portal to="icon"><fa :icon="faFileAlt"/></portal>
|
||||
<portal to="title">{{ title }}</portal>
|
||||
<main class="_card">
|
||||
<div class="_title"><fa :icon="faFileAlt"/> {{ title }}</div>
|
||||
<div class="_content">
|
||||
<div v-html="body" class="qyqbqfal"></div>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<mk-link :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${doc}.ja-JP.md`" class="at">{{ $t('docSource') }}</mk-link>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
@ -14,19 +18,27 @@
|
||||
import Vue from 'vue';
|
||||
import { faFileAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import i18n from '../i18n';
|
||||
import { url, lang } from '../config';
|
||||
import MkLink from '../components/link.vue';
|
||||
|
||||
const markdown = MarkdownIt({
|
||||
html: true
|
||||
});
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.title,
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
MkLink
|
||||
},
|
||||
|
||||
props: {
|
||||
doc: {
|
||||
type: String,
|
||||
|
@ -62,7 +62,9 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
signup() {
|
||||
this.$root.new(XSignupDialog);
|
||||
this.$root.new(XSignupDialog, {
|
||||
autoSet: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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',
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<mk-button @click="add()" primary style="margin: 0 auto 16px auto;"><fa :icon="faPlus"/> {{ $t('add') }}</mk-button>
|
||||
<section class="_card announcements">
|
||||
<div class="_content announcement" v-for="announcement in announcements">
|
||||
<mk-input v-model="announcement.title" style="margin-top: 8px;">
|
||||
<mk-input v-model="announcement.title">
|
||||
<span>{{ $t('title') }}</span>
|
||||
</mk-input>
|
||||
<mk-textarea v-model="announcement.text">
|
||||
|
@ -2,10 +2,10 @@
|
||||
<div class="mk-instance-emojis">
|
||||
<portal to="icon"><fa :icon="faLaugh"/></portal>
|
||||
<portal to="title">{{ $t('customEmojis') }}</portal>
|
||||
|
||||
<section class="_card local">
|
||||
<div class="_title"><fa :icon="faLaugh"/> {{ $t('customEmojis') }}</div>
|
||||
<div class="_content">
|
||||
<input ref="file" type="file" style="display: none;" @change="onChangeFile"/>
|
||||
<mk-pagination :pagination="pagination" class="emojis" ref="emojis">
|
||||
<template #empty><span>{{ $t('noCustomEmojis') }}</span></template>
|
||||
<template #default="{items}">
|
||||
@ -13,20 +13,30 @@
|
||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||
<div class="body">
|
||||
<span class="name">{{ emoji.name }}</span>
|
||||
<span class="info">
|
||||
<b class="category">{{ emoji.category }}</b>
|
||||
<span class="aliases">{{ emoji.aliases.join(' ') }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</mk-pagination>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<mk-button inline primary @click="add()"><fa :icon="faPlus"/> {{ $t('addEmoji') }}</mk-button>
|
||||
<div class="_content" v-if="selected">
|
||||
<mk-input v-model="name"><span>{{ $t('name') }}</span></mk-input>
|
||||
<mk-input v-model="category" :datalist="categories"><span>{{ $t('category') }}</span></mk-input>
|
||||
<mk-input v-model="aliases"><span>{{ $t('tags') }}</span></mk-input>
|
||||
<mk-button inline primary @click="update"><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
|
||||
<mk-button inline :disabled="selected == null" @click="del()"><fa :icon="faTrashAlt"/> {{ $t('delete') }}</mk-button>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<mk-button inline primary @click="add"><fa :icon="faPlus"/> {{ $t('addEmoji') }}</mk-button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="_card remote">
|
||||
<div class="_title"><fa :icon="faLaugh"/> {{ $t('customEmojisOfRemote') }}</div>
|
||||
<div class="_content">
|
||||
<mk-input v-model="host" :debounce="true" style="margin-top: 0;"><span>{{ $t('host') }}</span></mk-input>
|
||||
<mk-input v-model="host" :debounce="true"><span>{{ $t('host') }}</span></mk-input>
|
||||
<mk-pagination :pagination="remotePagination" class="emojis" ref="remoteEmojis">
|
||||
<template #empty><span>{{ $t('noCustomEmojis') }}</span></template>
|
||||
<template #default="{items}">
|
||||
@ -34,7 +44,7 @@
|
||||
<img :src="emoji.url" class="img" :alt="emoji.name"/>
|
||||
<div class="body">
|
||||
<span class="name">{{ emoji.name }}</span>
|
||||
<span class="host">{{ emoji.host }}</span>
|
||||
<span class="info">{{ emoji.host }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -49,12 +59,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faPlus, faSave } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTrashAlt, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
||||
import MkButton from '../../components/ui/button.vue';
|
||||
import MkInput from '../../components/ui/input.vue';
|
||||
import MkPagination from '../../components/ui/pagination.vue';
|
||||
import { apiUrl } from '../../config';
|
||||
import { selectFile } from '../../scripts/select-file';
|
||||
import { unique } from '../../../prelude/array';
|
||||
|
||||
export default Vue.extend({
|
||||
metaInfo() {
|
||||
@ -71,9 +82,11 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
name: null,
|
||||
selected: null,
|
||||
selectedRemote: null,
|
||||
name: null,
|
||||
category: null,
|
||||
aliases: null,
|
||||
host: '',
|
||||
pagination: {
|
||||
endpoint: 'admin/emoji/list',
|
||||
@ -86,52 +99,48 @@ export default Vue.extend({
|
||||
host: this.host ? this.host : null
|
||||
})
|
||||
},
|
||||
faTrashAlt, faPlus, faLaugh
|
||||
faTrashAlt, faPlus, faLaugh, faSave
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
categories() {
|
||||
if (this.$store.state.instance.meta) {
|
||||
return unique(this.$store.state.instance.meta.emojis.map((x: any) => x.category || '').filter((x: string) => x !== ''));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
host() {
|
||||
this.$refs.remoteEmojis.reload();
|
||||
},
|
||||
|
||||
selected() {
|
||||
this.name = this.selected ? this.selected.name : null;
|
||||
this.category = this.selected ? this.selected.category : null;
|
||||
this.aliases = this.selected ? this.selected.aliases.join(' ') : null;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async add() {
|
||||
const { canceled: canceled, result: name } = await this.$root.dialog({
|
||||
title: this.$t('emojiName'),
|
||||
input: true
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
this.name = name;
|
||||
|
||||
(this.$refs.file as any).click();
|
||||
},
|
||||
|
||||
onChangeFile() {
|
||||
const [file] = Array.from((this.$refs.file as any).files);
|
||||
if (file == null) return;
|
||||
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
data.append('name', this.name);
|
||||
data.append('i', this.$store.state.i.token);
|
||||
async add(e) {
|
||||
const files = await selectFile(this, e.currentTarget || e.target, null, true);
|
||||
|
||||
const dialog = this.$root.dialog({
|
||||
type: 'waiting',
|
||||
text: this.$t('uploading') + '...',
|
||||
text: this.$t('doing') + '...',
|
||||
showOkButton: false,
|
||||
showCancelButton: false,
|
||||
cancelableByBgClick: false
|
||||
});
|
||||
|
||||
fetch(apiUrl + '/admin/emoji/add', {
|
||||
method: 'POST',
|
||||
body: data
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(f => {
|
||||
|
||||
Promise.all(files.map(file => this.$root.api('admin/emoji/add', {
|
||||
fileId: file.id,
|
||||
})))
|
||||
.then(() => {
|
||||
this.$refs.emojis.reload();
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
@ -143,6 +152,22 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
async update() {
|
||||
await this.$root.api('admin/emoji/update', {
|
||||
id: this.selected.id,
|
||||
name: this.name,
|
||||
category: this.category,
|
||||
aliases: this.aliases.split(' '),
|
||||
});
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
iconOnly: true, autoClose: true
|
||||
});
|
||||
|
||||
this.$refs.emojis.reload();
|
||||
},
|
||||
|
||||
async del() {
|
||||
const { canceled } = await this.$root.dialog({
|
||||
type: 'warning',
|
||||
@ -207,6 +232,18 @@ export default Vue.extend({
|
||||
> .name {
|
||||
display: block;
|
||||
}
|
||||
|
||||
> .info {
|
||||
opacity: 0.5;
|
||||
|
||||
> .category {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
> .aliases {
|
||||
font-style: oblique;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -241,7 +278,7 @@ export default Vue.extend({
|
||||
display: block;
|
||||
}
|
||||
|
||||
> .host {
|
||||
> .info {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<section class="_card info">
|
||||
<div class="_title"><fa :icon="faInfoCircle"/> {{ $t('basicInfo') }}</div>
|
||||
<div class="_content">
|
||||
<mk-input v-model="name" style="margin-top: 8px;">{{ $t('instanceName') }}</mk-input>
|
||||
<mk-input v-model="name">{{ $t('instanceName') }}</mk-input>
|
||||
<mk-textarea v-model="description">{{ $t('instanceDescription') }}</mk-textarea>
|
||||
<mk-input v-model="iconUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('iconUrl') }}</mk-input>
|
||||
<mk-input v-model="bannerUrl"><template #icon><fa :icon="faLink"/></template>{{ $t('bannerUrl') }}</mk-input>
|
||||
@ -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>
|
||||
@ -78,7 +78,7 @@
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faThumbtack"/> {{ $t('pinnedUsers') }}</div>
|
||||
<div class="_content">
|
||||
<mk-textarea v-model="pinnedUsers" style="margin-top: 0;">
|
||||
<mk-textarea v-model="pinnedUsers">
|
||||
<template #desc>{{ $t('pinnedUsersDescription') }} <button class="_textButton" @click="addPinUser">{{ $t('addUser') }}</button></template>
|
||||
</mk-textarea>
|
||||
</div>
|
||||
@ -111,7 +111,7 @@
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faBan"/> {{ $t('blockedInstances') }}</div>
|
||||
<div class="_content">
|
||||
<mk-textarea v-model="blockedHosts" style="margin-top: 0;">
|
||||
<mk-textarea v-model="blockedHosts">
|
||||
<template #desc>{{ $t('blockedInstancesDescription') }}</template>
|
||||
</mk-textarea>
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<section class="_card lookup">
|
||||
<div class="_title"><fa :icon="faSearch"/> {{ $t('lookup') }}</div>
|
||||
<div class="_content">
|
||||
<mk-input class="target" v-model="target" type="text" @enter="showUser()" style="margin-top: 0;">
|
||||
<mk-input class="target" v-model="target" type="text" @enter="showUser()">
|
||||
<span>{{ $t('usernameOrUserId') }}</span>
|
||||
</mk-input>
|
||||
<mk-button @click="showUser()" primary><fa :icon="faSearch"/> {{ $t('lookup') }}</mk-button>
|
||||
|
@ -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,8 +3,8 @@
|
||||
<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">
|
||||
<img src="/assets/desktop/remove.png" alt="Delete"/>
|
||||
<button class="delete-button" v-if="isMe" :title="$t('delete')" @click="del">
|
||||
<img src="/assets/remove.png" alt="Delete"/>
|
||||
</button>
|
||||
<div class="content" v-if="!message.isDeleted">
|
||||
<mfm class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/>
|
||||
@ -230,7 +230,7 @@ export default Vue.extend({
|
||||
> footer {
|
||||
display: block;
|
||||
margin: 2px 0 0 0;
|
||||
font-size: 10px;
|
||||
font-size: 0.65em;
|
||||
|
||||
> .read {
|
||||
margin: 0 8px;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -31,7 +31,10 @@
|
||||
</div>
|
||||
</router-link>
|
||||
</sequential-entrance>
|
||||
<p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
|
||||
<div class="no-history" v-if="!fetching && messages.length == 0">
|
||||
<img src="https://xn--931a.moe/assets/info.png" alt="" class="_ghost"/>
|
||||
<div>{{ $t('noHistory') }}</div>
|
||||
</div>
|
||||
<mk-loading v-if="fetching"/>
|
||||
</div>
|
||||
</template>
|
||||
@ -139,6 +142,14 @@ export default Vue.extend({
|
||||
async startGroup() {
|
||||
const groups1 = await this.$root.api('users/groups/owned');
|
||||
const groups2 = await this.$root.api('users/groups/joined');
|
||||
if (groups1.length === 0 && groups2.length === 0) {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
title: this.$t('noGroups'),
|
||||
text: this.$t('joinOrCreateGroup'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { canceled, result: group } = await this.$root.dialog({
|
||||
type: null,
|
||||
title: this.$t('group'),
|
||||
@ -277,24 +288,18 @@ export default Vue.extend({
|
||||
}
|
||||
|
||||
> .no-history {
|
||||
margin: 0;
|
||||
padding: 2em 1em;
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
|
||||
> img {
|
||||
vertical-align: bottom;
|
||||
height: 128px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
> .search {
|
||||
> .result {
|
||||
> .users {
|
||||
> li {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .history {
|
||||
> .message {
|
||||
&:not([data-is-me]):not([data-is-read]) {
|
||||
@ -306,7 +311,7 @@ export default Vue.extend({
|
||||
|
||||
> div {
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
font-size: 0.9em;
|
||||
|
||||
> .avatar {
|
||||
margin: 0 12px 0 0;
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="shaynizk _card">
|
||||
<div class="_title" v-if="antenna.name">{{ antenna.name }}</div>
|
||||
<div class="_content body">
|
||||
<mk-input v-model="name" style="margin-top: 8px;">
|
||||
<mk-input v-model="name">
|
||||
<span>{{ $t('name') }}</span>
|
||||
</mk-input>
|
||||
<mk-select v-model="src">
|
||||
@ -11,12 +11,17 @@
|
||||
<option value="home">{{ $t('_antennaSources.homeTimeline') }}</option>
|
||||
<option value="users">{{ $t('_antennaSources.users') }}</option>
|
||||
<option value="list">{{ $t('_antennaSources.userList') }}</option>
|
||||
<option value="group">{{ $t('_antennaSources.userGroup') }}</option>
|
||||
</mk-select>
|
||||
<mk-select v-model="userListId" v-if="src === 'list'">
|
||||
<template #label>{{ $t('userList') }}</template>
|
||||
<option v-for="list in userLists" :value="list.id" :key="list.id">{{ list.name }}</option>
|
||||
</mk-select>
|
||||
<mk-textarea v-model="users" v-if="src === 'users'">
|
||||
<mk-select v-model="userGroupId" v-else-if="src === 'group'">
|
||||
<template #label>{{ $t('userGroup') }}</template>
|
||||
<option v-for="group in userGroups" :value="group.id" :key="group.id">{{ group.name }}</option>
|
||||
</mk-select>
|
||||
<mk-textarea v-model="users" v-else-if="src === 'users'">
|
||||
<span>{{ $t('users') }}</span>
|
||||
<template #desc>{{ $t('antennaUsersDescription') }} <button class="_textButton" @click="addUser">{{ $t('addUser') }}</button></template>
|
||||
</mk-textarea>
|
||||
@ -67,6 +72,7 @@ export default Vue.extend({
|
||||
name: '',
|
||||
src: '',
|
||||
userListId: null,
|
||||
userGroupId: null,
|
||||
users: '',
|
||||
keywords: '',
|
||||
caseSensitive: false,
|
||||
@ -74,6 +80,7 @@ export default Vue.extend({
|
||||
withFile: false,
|
||||
notify: false,
|
||||
userLists: null,
|
||||
userGroups: null,
|
||||
faSave, faTrash
|
||||
};
|
||||
},
|
||||
@ -83,6 +90,13 @@ export default Vue.extend({
|
||||
if (this.src === 'list' && this.userLists === null) {
|
||||
this.userLists = await this.$root.api('users/lists/list');
|
||||
}
|
||||
|
||||
if (this.src === 'group' && this.userGroups === null) {
|
||||
const groups1 = await this.$root.api('users/groups/owned');
|
||||
const groups2 = await this.$root.api('users/groups/joined');
|
||||
|
||||
this.userGroups = [...groups1, ...groups2];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -90,6 +104,7 @@ export default Vue.extend({
|
||||
this.name = this.antenna.name;
|
||||
this.src = this.antenna.src;
|
||||
this.userListId = this.antenna.userListId;
|
||||
this.userGroupId = this.antenna.userGroupId;
|
||||
this.users = this.antenna.users.join('\n');
|
||||
this.keywords = this.antenna.keywords.map(x => x.join(' ')).join('\n');
|
||||
this.caseSensitive = this.antenna.caseSensitive;
|
||||
@ -105,6 +120,7 @@ export default Vue.extend({
|
||||
name: this.name,
|
||||
src: this.src,
|
||||
userListId: this.userListId,
|
||||
userGroupId: this.userGroupId,
|
||||
withReplies: this.withReplies,
|
||||
withFile: this.withFile,
|
||||
notify: this.notify,
|
||||
@ -119,6 +135,7 @@ export default Vue.extend({
|
||||
name: this.name,
|
||||
src: this.src,
|
||||
userListId: this.userListId,
|
||||
userGroupId: this.userGroupId,
|
||||
withReplies: this.withReplies,
|
||||
withFile: this.withFile,
|
||||
notify: this.notify,
|
||||
|
@ -50,6 +50,7 @@ export default Vue.extend({
|
||||
name: '',
|
||||
src: 'all',
|
||||
userListId: null,
|
||||
userGroupId: null,
|
||||
users: [],
|
||||
keywords: [],
|
||||
withReplies: false,
|
||||
|
@ -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="" class="_ghost"/>
|
||||
<div>{{ $t('notFoundDescription') }}</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -45,8 +45,6 @@ export default Vue.extend({
|
||||
height: 150px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
src/client/pages/settings/api.vue
Normal file
46
src/client/pages/settings/api.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faKey"/> API</div>
|
||||
<div class="_content">
|
||||
<mk-input :value="$store.state.i.token" readonly>
|
||||
<span>{{ $t('token') }}</span>
|
||||
</mk-input>
|
||||
<mk-button @click="regenerateToken"><fa :icon="faSyncAlt"/> {{ $t('regenerate') }}</mk-button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faKey, faSyncAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../i18n';
|
||||
import MkButton from '../../components/ui/button.vue';
|
||||
import MkInput from '../../components/ui/input.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
components: {
|
||||
MkButton, MkInput
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
faKey, faSyncAlt
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
regenerateToken() {
|
||||
this.$root.dialog({
|
||||
title: this.$t('password'),
|
||||
input: {
|
||||
type: 'password'
|
||||
}
|
||||
}).then(({ canceled, result: password }) => {
|
||||
if (canceled) return;
|
||||
this.$root.api('i/regenerate_token', {
|
||||
password: password
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
@ -2,7 +2,7 @@
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faCog"/> {{ $t('general') }}</div>
|
||||
<div class="_content">
|
||||
<mk-input type="file" @change="onWallpaperChange" style="margin-top: 0;">
|
||||
<mk-input type="file" @change="onWallpaperChange">
|
||||
<span>{{ $t('wallpaper') }}</span>
|
||||
<template #icon><fa :icon="faImage"/></template>
|
||||
<template #desc v-if="wallpaperUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
|
||||
@ -23,10 +23,27 @@
|
||||
<mk-button @click="readAllMessagingMessages">{{ $t('markAsReadAllTalkMessages') }}</mk-button>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<mk-switch v-model="reduceAnimation">
|
||||
{{ $t('reduceUiAnimation') }}
|
||||
<mk-switch v-model="disableAnimatedMfm">{{ $t('disableAnimatedMfm') }}</mk-switch>
|
||||
<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>
|
||||
<div class="_content">
|
||||
<div>{{ $t('fontSize') }}</div>
|
||||
<mk-radio v-model="fontSize" value="small"><span style="font-size: 14px;">Aa</span></mk-radio>
|
||||
<mk-radio v-model="fontSize" :value="null"><span style="font-size: 16px;">Aa</span></mk-radio>
|
||||
<mk-radio v-model="fontSize" value="large"><span style="font-size: 18px;">Aa</span></mk-radio>
|
||||
<mk-radio v-model="fontSize" value="veryLarge"><span style="font-size: 20px;">Aa</span></mk-radio>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@ -36,8 +53,10 @@ 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 MkRadio from '../../components/ui/radio.vue';
|
||||
import i18n from '../../i18n';
|
||||
import { apiUrl } from '../../config';
|
||||
import { apiUrl, langs } from '../../config';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
@ -46,10 +65,15 @@ export default Vue.extend({
|
||||
MkInput,
|
||||
MkButton,
|
||||
MkSwitch,
|
||||
MkSelect,
|
||||
MkRadio,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
langs,
|
||||
lang: localStorage.getItem('lang'),
|
||||
fontSize: localStorage.getItem('fontSize'),
|
||||
wallpaperUploading: false,
|
||||
faImage, faCog
|
||||
}
|
||||
@ -70,6 +94,33 @@ export default Vue.extend({
|
||||
get() { return !this.$store.state.device.animation; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'animation', value: !value }); }
|
||||
},
|
||||
|
||||
disableAnimatedMfm: {
|
||||
get() { return !this.$store.state.device.animatedMfm; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'animatedMfm', 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();
|
||||
},
|
||||
|
||||
fontSize() {
|
||||
if (this.fontSize == null) {
|
||||
localStorage.removeItem('fontSize');
|
||||
} else {
|
||||
localStorage.setItem('fontSize', this.fontSize);
|
||||
}
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -2,8 +2,7 @@
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faBoxes"/> {{ $t('importAndExport') }}</div>
|
||||
<div class="_content">
|
||||
<input ref="file" type="file" style="display: none;" @change="onChangeFile"/>
|
||||
<mk-select v-model="exportTarget" style="margin-top: 0;">
|
||||
<mk-select v-model="exportTarget">
|
||||
<option value="notes">{{ $t('_exportOrImport.allNotes') }}</option>
|
||||
<option value="following">{{ $t('_exportOrImport.followingList') }}</option>
|
||||
<option value="user-lists">{{ $t('_exportOrImport.userLists') }}</option>
|
||||
@ -13,6 +12,7 @@
|
||||
<mk-button inline @click="doExport()"><fa :icon="faDownload"/> {{ $t('export') }}</mk-button>
|
||||
<mk-button inline @click="doImport()" :disabled="!['following', 'user-lists'].includes(exportTarget)"><fa :icon="faUpload"/> {{ $t('import') }}</mk-button>
|
||||
</div>
|
||||
<input ref="file" type="file" style="display: none;" @change="onChangeFile"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
<x-security/>
|
||||
<x-2fa/>
|
||||
<x-integration/>
|
||||
<x-api/>
|
||||
|
||||
<mk-button @click="cacheClear()" primary class="cacheClear">{{ $t('cacheClear') }}</mk-button>
|
||||
<mk-button @click="$root.signout()" primary class="logout">{{ $t('logout') }}</mk-button>
|
||||
@ -34,6 +35,7 @@ import XSecurity from './security.vue';
|
||||
import XTheme from './theme.vue';
|
||||
import X2fa from './2fa.vue';
|
||||
import XIntegration from './integration.vue';
|
||||
import XApi from './api.vue';
|
||||
import MkButton from '../../components/ui/button.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
@ -55,6 +57,7 @@ export default Vue.extend({
|
||||
XTheme,
|
||||
X2fa,
|
||||
XIntegration,
|
||||
XApi,
|
||||
MkButton,
|
||||
},
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faLaugh"/> {{ $t('reaction') }}</div>
|
||||
<div class="_content">
|
||||
<mk-textarea v-model="reactions" style="margin-top: 16px;">{{ $t('reaction') }}<template #desc>{{ $t('reactionSettingDescription') }}</template></mk-textarea>
|
||||
<mk-textarea v-model="reactions">{{ $t('reaction') }}<template #desc>{{ $t('reactionSettingDescription') }}</template></mk-textarea>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<mk-button @click="save()" primary inline :disabled="!changed"><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
|
||||
|
@ -380,7 +380,7 @@ export default Vue.extend({
|
||||
|
||||
> .description {
|
||||
padding: 24px 24px 24px 154px;
|
||||
font-size: 15px;
|
||||
font-size: 0.95em;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
padding: 16px;
|
||||
@ -395,7 +395,7 @@ export default Vue.extend({
|
||||
|
||||
> .fields {
|
||||
padding: 24px;
|
||||
font-size: 14px;
|
||||
font-size: 0.9em;
|
||||
border-top: solid 1px var(--divider);
|
||||
|
||||
@media (max-width: 500px) {
|
||||
|
@ -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));
|
||||
|
||||
// Prepend the item
|
||||
this.items.unshift(item);
|
||||
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
|
||||
});
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { faUpload, faCloud, faLink } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faUpload, faCloud } from '@fortawesome/free-solid-svg-icons';
|
||||
import { selectDriveFile } from './select-drive-file';
|
||||
import { apiUrl } from '../config';
|
||||
|
||||
export function selectFile(component: any, src: any, label: string, multiple = false) {
|
||||
export function selectFile(component: any, src: any, label: string | null, multiple = false) {
|
||||
return new Promise((res, rej) => {
|
||||
const chooseFileFromPc = () => {
|
||||
const input = document.createElement('input');
|
||||
@ -56,10 +56,10 @@ export function selectFile(component: any, src: any, label: string, multiple = f
|
||||
};
|
||||
|
||||
component.$root.menu({
|
||||
items: [{
|
||||
items: [label ? {
|
||||
text: label,
|
||||
type: 'label'
|
||||
}, {
|
||||
} : undefined, {
|
||||
text: component.$t('upload'),
|
||||
icon: faUpload,
|
||||
action: chooseFileFromPc
|
||||
|
@ -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,13 +31,14 @@ const defaultDeviceSettings = {
|
||||
lang: null,
|
||||
loadRawImages: false,
|
||||
alwaysShowNsfw: false,
|
||||
useOsDefaultEmojis: false,
|
||||
useOsNativeEmojis: false,
|
||||
autoReload: false,
|
||||
accounts: [],
|
||||
recentEmojis: [],
|
||||
themes: [],
|
||||
theme: 'light',
|
||||
animation: true,
|
||||
animatedMfm: true,
|
||||
userData: {},
|
||||
};
|
||||
|
||||
|
@ -58,6 +58,18 @@ html {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.f-small {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&.f-large {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
&.f-veryLarge {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
html.changing-theme {
|
||||
@ -164,6 +176,19 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
._noSelect {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
._ghost {
|
||||
&, * {
|
||||
@extend ._noSelect;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
._button {
|
||||
appearance: none;
|
||||
padding: 0;
|
||||
@ -175,9 +200,7 @@ a {
|
||||
font-size: 1em;
|
||||
|
||||
&, * {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
@extend ._noSelect;
|
||||
}
|
||||
|
||||
* {
|
||||
|
@ -14,6 +14,7 @@
|
||||
focus: ':alpha<0.3<@accent',
|
||||
bg: '#000',
|
||||
fg: '#c7d1d8',
|
||||
fgHighlighted: ':lighten<3<@fg',
|
||||
panel: '#111213',
|
||||
shadow: 'rgba(0, 0, 0, 0.1)',
|
||||
header: 'rgba(20, 20, 20, 0.75)',
|
||||
@ -49,7 +50,6 @@
|
||||
pcncwizz: ':darken<2<@panel',
|
||||
vocsgcxy: 'rgba(0, 0, 0, 0.5)',
|
||||
yrnqrguo: 'rgba(255, 255, 255, 0.05)',
|
||||
mkykhqkw: ':lighten<3<@fg',
|
||||
nwjktjjq: 'rgba(255, 255, 255, 0.1)',
|
||||
geavgsxy: 'rgba(255, 255, 255, 0.05)',
|
||||
nhzhphzx: 'rgba(255, 255, 255, 0.15)',
|
||||
|
@ -14,6 +14,7 @@
|
||||
focus: ':alpha<0.3<@accent',
|
||||
bg: '#fafafa',
|
||||
fg: '#5c6a73',
|
||||
fgHighlighted: ':darken<3<@fg',
|
||||
panel: '#fff',
|
||||
shadow: 'rgba(0, 0, 0, 0.1)',
|
||||
header: 'rgba(255, 255, 255, 0.75)',
|
||||
@ -49,7 +50,6 @@
|
||||
pcncwizz: ':darken<2<@panel',
|
||||
vocsgcxy: 'rgba(255, 255, 255, 0.5)',
|
||||
yrnqrguo: 'rgba(0, 0, 0, 0.05)',
|
||||
mkykhqkw: ':darken<3<@fg',
|
||||
nwjktjjq: 'rgba(0, 0, 0, 0.1)',
|
||||
geavgsxy: 'rgba(0, 0, 0, 0.05)',
|
||||
nhzhphzx: 'rgba(0, 0, 0, 0.25)',
|
||||
|
@ -132,7 +132,7 @@ export default define({
|
||||
> p {
|
||||
margin: 0;
|
||||
line-height: 18px;
|
||||
font-size: 14px;
|
||||
font-size: 0.9em;
|
||||
|
||||
> span {
|
||||
margin: 0 4px;
|
||||
@ -142,7 +142,7 @@ export default define({
|
||||
> .day {
|
||||
margin: 10px 0;
|
||||
line-height: 32px;
|
||||
font-size: 28px;
|
||||
font-size: 1.75em;
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ export default define({
|
||||
|
||||
> p {
|
||||
margin: 0 0 2px 0;
|
||||
font-size: 12px;
|
||||
font-size: 0.75em;
|
||||
line-height: 18px;
|
||||
opacity: 0.8;
|
||||
|
||||
|
44
src/client/widgets/clock.vue
Normal file
44
src/client/widgets/clock.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<mk-container :naked="props.style % 2 === 0" :show-header="false">
|
||||
<div class="vubelbmv">
|
||||
<mk-analog-clock class="clock" :smooth="props.style < 2"/>
|
||||
</div>
|
||||
</mk-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import define from './define';
|
||||
import MkContainer from '../components/ui/container.vue';
|
||||
import MkAnalogClock from '../components/analog-clock.vue';
|
||||
|
||||
export default define({
|
||||
name: 'clock',
|
||||
props: () => ({
|
||||
style: 0
|
||||
})
|
||||
}).extend({
|
||||
components: {
|
||||
MkContainer,
|
||||
MkAnalogClock
|
||||
},
|
||||
methods: {
|
||||
func() {
|
||||
this.props.style = (this.props.style + 1) % 4;
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vubelbmv {
|
||||
padding: 8px;
|
||||
|
||||
> .clock {
|
||||
height: 150px;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -6,3 +6,4 @@ Vue.component('mkw-timeline', () => import('./timeline.vue').then(m => m.default
|
||||
Vue.component('mkw-calendar', () => import('./calendar.vue').then(m => m.default));
|
||||
Vue.component('mkw-rss', () => import('./rss.vue').then(m => m.default));
|
||||
Vue.component('mkw-trends', () => import('./trends.vue').then(m => m.default));
|
||||
Vue.component('mkw-clock', () => import('./clock.vue').then(m => m.default));
|
||||
|
@ -4,10 +4,11 @@
|
||||
<template #header><fa :icon="faHashtag"/>{{ $t('_widgets.trends') }}</template>
|
||||
|
||||
<div class="wbrkwala">
|
||||
<transition-group tag="div" name="chart">
|
||||
<mk-loading v-if="fetching"/>
|
||||
<transition-group tag="div" name="chart" class="tags" v-else>
|
||||
<div v-for="stat in stats" :key="stat.tag">
|
||||
<div class="tag">
|
||||
<router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
|
||||
<router-link class="a" :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
|
||||
<p>{{ $t('nUsersMentioned', { n: stat.usersCount }) }}</p>
|
||||
</div>
|
||||
<x-chart class="chart" :src="stat.chart"/>
|
||||
@ -66,20 +67,10 @@ export default define({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wbrkwala {
|
||||
> .fetching,
|
||||
> .empty {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--text);
|
||||
opacity: 0.7;
|
||||
height: (62px + 1px) + (62px + 1px) + (62px + 1px) + (62px + 1px) + 62px;
|
||||
overflow: hidden;
|
||||
|
||||
> [data-icon] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
> .tags {
|
||||
.chart-move {
|
||||
transition: transform 1s ease;
|
||||
}
|
||||
@ -96,22 +87,23 @@ export default define({
|
||||
> .tag {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
font-size: 0.9em;
|
||||
color: var(--fg);
|
||||
|
||||
> a {
|
||||
> .a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: inherit;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
> p {
|
||||
margin: 0;
|
||||
font-size: 75%;
|
||||
opacity: 0.7;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
5
src/docs/custom-emoji.ja-JP.md
Normal file
5
src/docs/custom-emoji.ja-JP.md
Normal file
@ -0,0 +1,5 @@
|
||||
# カスタム絵文字
|
||||
カスタム絵文字は、インスタンスで用意された画像を絵文字のように使える機能です。
|
||||
ノート、リアクション、チャット、自己紹介、名前などの場所で使うことができます。
|
||||
カスタム絵文字をそれらの場所で使うには、絵文字ピッカーボタン(ある場合)を押すか、`:`を入力して絵文字サジェストを表示します。
|
||||
テキスト内に`:foo:`のような形式の文字列が見つかると、`foo`の部分がカスタム絵文字名と解釈され、表示時には対応したカスタム絵文字に置き換わります。
|
15
src/docs/reaction.ja-JP.md
Normal file
15
src/docs/reaction.ja-JP.md
Normal file
@ -0,0 +1,15 @@
|
||||
# リアクション
|
||||
他の人のノートに、絵文字を付けて簡単にあなたの反応を伝えられる機能です。
|
||||
リアクションするには、ノートの + アイコンをクリックしてピッカーを表示し、絵文字を選択します。
|
||||
リアクションには[カスタム絵文字](./custom-emoji)も使用できます。
|
||||
|
||||
## リアクションピッカーのカスタマイズ
|
||||
ピッカーに表示される絵文字を自分好みにカスタマイズすることができます。
|
||||
設定の「リアクション」で設定します。
|
||||
|
||||
## リモート投稿へのリアクションについて
|
||||
リアクションはMisskeyオリジナルの機能であるため、リモートインスタンスがMisskeyでない限りは、ほとんどの場合「Like」としてアクティビティが送信されます。一般的にはLikeは「お気に入り」として実装されているようです。
|
||||
また、相手がMisskeyであったとしても、カスタム絵文字リアクションは伝わらず、自動的に「👍」等にフォールバックされます。
|
||||
|
||||
## リモートからのリアクションについて
|
||||
リモートから「Like」アクティビティを受信したとき、Misskeyでは「👍」のリアクションとして解釈されます。
|
@ -1,9 +1,10 @@
|
||||
import { Antenna } from '../models/entities/antenna';
|
||||
import { Note } from '../models/entities/note';
|
||||
import { User } from '../models/entities/user';
|
||||
import { UserListJoinings } from '../models';
|
||||
import { UserListJoinings, UserGroupJoinings } from '../models';
|
||||
import parseAcct from './acct/parse';
|
||||
import { getFullApAccount } from './convert-host';
|
||||
import { ensure } from '../prelude/ensure';
|
||||
|
||||
export async function checkHitAntenna(antenna: Antenna, note: Note, noteUser: User, followers: User['id'][]): Promise<boolean> {
|
||||
if (note.visibility === 'specified') return false;
|
||||
@ -22,6 +23,14 @@ export async function checkHitAntenna(antenna: Antenna, note: Note, noteUser: Us
|
||||
})).map(x => x.userId);
|
||||
|
||||
if (!listUsers.includes(note.userId)) return false;
|
||||
} else if (antenna.src === 'group') {
|
||||
const joining = await UserGroupJoinings.findOne(antenna.userGroupJoiningId!).then(ensure);
|
||||
|
||||
const groupUsers = (await UserGroupJoinings.find({
|
||||
userGroupId: joining.userGroupId
|
||||
})).map(x => x.userId);
|
||||
|
||||
if (!groupUsers.includes(note.userId)) return false;
|
||||
} else if (antenna.src === 'users') {
|
||||
const accts = antenna.users.map(x => {
|
||||
const { username, host } = parseAcct(x);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
|
||||
import { User } from './user';
|
||||
import { id } from '../id';
|
||||
import { UserList } from './user-list';
|
||||
import { UserGroupJoining } from './user-group-joining';
|
||||
|
||||
@Entity()
|
||||
export class Antenna {
|
||||
@ -32,8 +33,8 @@ export class Antenna {
|
||||
})
|
||||
public name: string;
|
||||
|
||||
@Column('enum', { enum: ['home', 'all', 'users', 'list'] })
|
||||
public src: 'home' | 'all' | 'users' | 'list';
|
||||
@Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] })
|
||||
public src: 'home' | 'all' | 'users' | 'list' | 'group';
|
||||
|
||||
@Column({
|
||||
...id(),
|
||||
@ -47,6 +48,18 @@ export class Antenna {
|
||||
@JoinColumn()
|
||||
public userList: UserList | null;
|
||||
|
||||
@Column({
|
||||
...id(),
|
||||
nullable: true
|
||||
})
|
||||
public userGroupJoiningId: UserGroupJoining['id'] | null;
|
||||
|
||||
@ManyToOne(type => UserGroupJoining, {
|
||||
onDelete: 'CASCADE'
|
||||
})
|
||||
@JoinColumn()
|
||||
public userGroupJoining: UserGroupJoining | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024, array: true,
|
||||
default: '{}'
|
||||
|
@ -4,6 +4,7 @@ import { DriveFolder } from './drive-folder';
|
||||
import { id } from '../id';
|
||||
|
||||
@Entity()
|
||||
@Index(['userId', 'folderId', 'id'])
|
||||
export class DriveFile {
|
||||
@PrimaryColumn(id())
|
||||
public id: string;
|
||||
|
@ -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;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user