Merge branch 'notification-read-api' into swn

This commit is contained in:
tamaina 2021-12-18 00:31:35 +09:00
commit 252169f879
84 changed files with 1500 additions and 1232 deletions

View file

@ -2,7 +2,6 @@
"recommendations": [ "recommendations": [
"editorconfig.editorconfig", "editorconfig.editorconfig",
"eg2.vscode-npm-script", "eg2.vscode-npm-script",
"ms-vscode.typescript-javascript-grammar",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"johnsoncodehk.volar", "johnsoncodehk.volar",
"sysoev.language-stylus" "sysoev.language-stylus"

View file

@ -10,13 +10,36 @@
--> -->
## 12.x.x (unreleased) ## 12.100.1 (2021/12/17)
### Bugfixes
- クライアント: デザインの調整
## 12.100.0 (2021/12/17)
### Improvements
- クライアント: モバイルでの各種メニュー、リアクションピッカーの表示を改善
### Bugfixes
- クライアント: 一部のコンポーネントが裏に隠れるのを修正
## 12.99.3 (2021/12/14)
### Bugfixes
- クライアント: オートコンプリートがダイアログの裏に隠れる問題を修正
## 12.99.2 (2021/12/14)
## 12.99.1 (2021/12/14)
## 12.99.0 (2021/12/14)
### Improvements ### Improvements
- Added a user-level instance mute in user settings - Added a user-level instance mute in user settings
- フォローエクスポートでミュートしているユーザーを含めないオプションを追加 - フォローエクスポートでミュートしているユーザーを含めないオプションを追加
- フォローエクスポートで使われていないアカウントを含めないオプションを追加 - フォローエクスポートで使われていないアカウントを含めないオプションを追加
- カスタム絵文字エクスポート機能 - カスタム絵文字エクスポート機能
- チャートのパフォーマンスの改善
- グループから抜けられるように
### Bugfixes ### Bugfixes
- クライアント: タッチ機能付きディスプレイを使っていてマウス操作をしている場合に一部機能が動作しない問題を修正 - クライアント: タッチ機能付きディスプレイを使っていてマウス操作をしている場合に一部機能が動作しない問題を修正

View file

@ -752,6 +752,7 @@ muteThread: "اكتم النقاش"
unmuteThread: "ارفع الكتم عن النقاش" unmuteThread: "ارفع الكتم عن النقاش"
deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟" deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟"
incorrectPassword: "كلمة السر خاطئة." incorrectPassword: "كلمة السر خاطئة."
hide: "إخفاء"
_emailUnavailable: _emailUnavailable:
used: "هذا البريد الإلكتروني مستخدم" used: "هذا البريد الإلكتروني مستخدم"
format: "صيغة البريد الإلكتروني غير صالحة" format: "صيغة البريد الإلكتروني غير صالحة"

View file

@ -592,6 +592,7 @@ smtpSecure: "Für SMTP-Verbindungen implizit SSL/TLS verwenden"
smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest" smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest"
testEmail: "Email-Versand testen" testEmail: "Email-Versand testen"
wordMute: "Wort-Stummschaltung" wordMute: "Wort-Stummschaltung"
instanceMute: "Instanzstummschaltungen"
userSaysSomething: "{name} hat etwas gesagt" userSaysSomething: "{name} hat etwas gesagt"
makeActive: "Aktivieren" makeActive: "Aktivieren"
display: "Anzeigeart" display: "Anzeigeart"
@ -811,6 +812,7 @@ continueThread: "Weiteren Threadverlauf anzeigen"
deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?" deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?"
incorrectPassword: "Falsches Passwort." incorrectPassword: "Falsches Passwort."
voteConfirm: "Wirklich für \"{choice}\" abstimmen?" voteConfirm: "Wirklich für \"{choice}\" abstimmen?"
hide: "Inhalt verbergen"
_emailUnavailable: _emailUnavailable:
used: "Diese Email-Adresse wird bereits verwendet" used: "Diese Email-Adresse wird bereits verwendet"
format: "Das Format dieser Email-Adresse ist ungültig" format: "Das Format dieser Email-Adresse ist ungültig"
@ -1001,6 +1003,11 @@ _wordMute:
soft: "Leicht" soft: "Leicht"
hard: "Schwer" hard: "Schwer"
mutedNotes: "Stummgeschaltete Notizen" mutedNotes: "Stummgeschaltete Notizen"
_instanceMute:
instanceMuteDescription: "Schaltet alle Notizen/Renotes stumm, die von den gelisteten Instanzen stammen, inklusive Antworten von Benutzern an einen Benutzer einer stummgeschalteten Instanz."
instanceMuteDescription2: "Instanzen getrennt durch Zeilenumbrüchen angeben"
title: "Blendet Notizen von stummgeschalteten Instanzen aus."
heading: "Liste der stummzuschaltenden Instanzen"
_theme: _theme:
explore: "Themen erforschen" explore: "Themen erforschen"
install: "Thema installieren" install: "Thema installieren"
@ -1274,6 +1281,8 @@ _exportOrImport:
muteList: "Stummschaltungen" muteList: "Stummschaltungen"
blockingList: "Blockierungen" blockingList: "Blockierungen"
userLists: "Listen" userLists: "Listen"
excludeMutingUsers: "Stummgeschaltete Benutzer aussortieren"
excludeInactiveUsers: "Inaktive Benutzer aussortieren"
_charts: _charts:
federationInstancesIncDec: "Unterschied in der Anzahl von förderierenden Instanzen" federationInstancesIncDec: "Unterschied in der Anzahl von förderierenden Instanzen"
federationInstancesTotal: "Anzahl aller föderierenden Instanzen" federationInstancesTotal: "Anzahl aller föderierenden Instanzen"

View file

@ -137,7 +137,7 @@ addEmoji: "Add an emoji"
settingGuide: "Recommended settings" settingGuide: "Recommended settings"
cacheRemoteFiles: "Cache remote files" cacheRemoteFiles: "Cache remote files"
cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated." cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated."
flagAsBot: "Mark this account as as bot" flagAsBot: "Mark this account as a bot"
flagAsBotDescription: "Enable this option if this account is controlled by a program. If enabled, it will act as a flag for other developers to prevent endless interaction chains with other bots and adjust Misskey's internal systems to treat this account as a bot." flagAsBotDescription: "Enable this option if this account is controlled by a program. If enabled, it will act as a flag for other developers to prevent endless interaction chains with other bots and adjust Misskey's internal systems to treat this account as a bot."
flagAsCat: "Mark this account as a cat" flagAsCat: "Mark this account as a cat"
flagAsCatDescription: "Enable this option to mark this account as a cat." flagAsCatDescription: "Enable this option to mark this account as a cat."
@ -592,6 +592,7 @@ smtpSecure: "Use implicit SSL/TLS for SMTP connections"
smtpSecureInfo: "Turn this off when using STARTTLS" smtpSecureInfo: "Turn this off when using STARTTLS"
testEmail: "Test email delivery" testEmail: "Test email delivery"
wordMute: "Word mute" wordMute: "Word mute"
instanceMute: "Instance mutes"
userSaysSomething: "{name} said something" userSaysSomething: "{name} said something"
makeActive: "Activate" makeActive: "Activate"
display: "Display" display: "Display"
@ -810,6 +811,7 @@ continueThread: "View thread continuation"
deleteAccountConfirm: "This will irreversibly delete your account. Proceed?" deleteAccountConfirm: "This will irreversibly delete your account. Proceed?"
incorrectPassword: "Incorrect password." incorrectPassword: "Incorrect password."
voteConfirm: "Confirm your vote for \"{choice}\"?" voteConfirm: "Confirm your vote for \"{choice}\"?"
hide: "Hide"
_emailUnavailable: _emailUnavailable:
used: "This email address is already being used" used: "This email address is already being used"
format: "The format of this email address is invalid" format: "The format of this email address is invalid"
@ -1000,6 +1002,11 @@ _wordMute:
soft: "Soft" soft: "Soft"
hard: "Hard" hard: "Hard"
mutedNotes: "Muted notes" mutedNotes: "Muted notes"
_instanceMute:
instanceMuteDescription: "This will mute any notes/renotes from the listed instances, including those of users replying to a user from a muted instance."
instanceMuteDescription2: "Separate with newlines"
title: "Hides notes from listed instances."
heading: "List of instances to be muted"
_theme: _theme:
explore: "Explore Themes" explore: "Explore Themes"
install: "Install a theme" install: "Install a theme"
@ -1273,6 +1280,8 @@ _exportOrImport:
muteList: "Muted users" muteList: "Muted users"
blockingList: "Blocked users" blockingList: "Blocked users"
userLists: "User lists" userLists: "User lists"
excludeMutingUsers: "Exclude muted users"
excludeInactiveUsers: "Exclude inactive users"
_charts: _charts:
federationInstancesIncDec: "Difference in # of federating instances" federationInstancesIncDec: "Difference in # of federating instances"
federationInstancesTotal: "Total # of federating instances" federationInstancesTotal: "Total # of federating instances"

View file

@ -1,8 +1,8 @@
--- ---
_lang_: "Esperanto" _lang_: "Esperanto"
headlineMisskey: "Jen la reto konektata de notoj" headlineMisskey: "Reto konektita per notoj"
introMisskey: "Bonvenon! Misskey estas malfermitkoda malcentraliza etbloga servo.\nKreu \"noto\"n por paroli vian penson al iuj ĉirkaŭ vi. 📡\nLa funkcion \"reago\" ebligas esprimi rapide vian senton pri ies noto en Fediverso. 👍\nBonvole esploru novan mondon. 🚀" introMisskey: "Bonvenon! Misskey estas malfermitkoda malcentraliza etbloga servo.\nKreu \"noto\"n por diskonigi nunan aferon, aŭ por paroli vian penson al ĉiuj ĉirkaŭ vi. 📡\nLa funkcion \"reago\" ebligas esprimi rapide vian senton pri la noto de la alia en la Fediverso. 👍\nBonvole esploru novan mondon. 🚀"
monthAndDay: "la {day}a de la {month}a" monthAndDay: "la {day}a de la {month}a monato"
search: "Serĉi" search: "Serĉi"
notifications: "Sciigoj" notifications: "Sciigoj"
username: "Uzantnomo" username: "Uzantnomo"
@ -20,13 +20,13 @@ instance: "Nodo"
settings: "Agordoj" settings: "Agordoj"
basicSettings: "Ĝeneralaj agordoj" basicSettings: "Ĝeneralaj agordoj"
otherSettings: "Aliaj agordoj" otherSettings: "Aliaj agordoj"
openInWindow: "Malfermi en fenestro" openInWindow: "Malfermi en nova fenestro"
profile: "Profilo" profile: "Profilo"
timeline: "Templinio" timeline: "Templinio"
noAccountDescription: "Neniu sinprezento" noAccountDescription: "Neniu priskribo"
login: "Ensaluti" login: "Saluti"
loggingIn: "Ensalutado…" loggingIn: "Salutado…"
logout: "Elsaluti" logout: "Adiaŭi"
signup: "Registriĝi" signup: "Registriĝi"
uploading: "Alŝutado…" uploading: "Alŝutado…"
save: "Konservi" save: "Konservi"
@ -58,13 +58,13 @@ followRequestAccepted: "La peto de sekvado akceptita"
mention: "Mencioj" mention: "Mencioj"
mentions: "Mencioj" mentions: "Mencioj"
directNotes: "Rekte senditaj" directNotes: "Rekte senditaj"
importAndExport: "Importi/eksporti" importAndExport: "Enporti kaj elporti"
import: "Importi" import: "Enporti"
export: "Eksporti" export: "Elporti"
files: "Dosieroj" files: "Dosieroj"
download: "Elŝuti" download: "Elŝuti"
driveFileDeleteConfirm: "Ĉu vi certas, ke vi volas forviŝi la dosieron \"{name}\"? Pro tio forviŝiĝos ankaŭ la notoj kiuj enhavas ĝin." driveFileDeleteConfirm: "Ĉu vi certas, ke vi volas forviŝi la dosieron \"{name}\"? Tio ankaŭ forviŝos la notojn kiuj citas ĝin."
unfollowConfirm: "Ĉu vi certas, ke vi volas ĉesi sekvi {name}'(o)n?" unfollowConfirm: "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?"
lists: "Listoj" lists: "Listoj"
noLists: "Neniu listo" noLists: "Neniu listo"
note: "Sendi" note: "Sendi"
@ -101,7 +101,7 @@ clickToShow: "Klaku por malkaŝu"
sensitive: "Enhavo ne estas deca por laborejo (NSFW)" sensitive: "Enhavo ne estas deca por laborejo (NSFW)"
add: "Aldoni" add: "Aldoni"
reaction: "Reagoj" reaction: "Reagoj"
reactionSettingDescription: "Agordi la reagojn kiujn vi volas prefere montrigi ĉe la elektilo de reagoj" reactionSettingDescription: "Agordi la reagojn kiujn vi volas montrigi prefere ĉe la elektilo de reagoj"
rememberNoteVisibility: "Rememori la agordon de videbleco de la laste sendita" rememberNoteVisibility: "Rememori la agordon de videbleco de la laste sendita"
attachCancel: "Deigi aldonaĵon" attachCancel: "Deigi aldonaĵon"
markAsSensitive: "Troviĝi NSFW" markAsSensitive: "Troviĝi NSFW"
@ -129,8 +129,9 @@ emojiUrl: "URL de la emoĵio"
addEmoji: "Aldoni emoĵion" addEmoji: "Aldoni emoĵion"
settingGuide: "Agordaj rekomendoj" settingGuide: "Agordaj rekomendoj"
cacheRemoteFiles: "Stapli forajn dosierojn" cacheRemoteFiles: "Stapli forajn dosierojn"
flagAsBot: "Fari la flagon por robota uzanto" flagAsBot: "Agordi por robota uzanto"
flagAsCat: "Fari la flagon por kat-iĝi" flagAsCat: "Agordi por kata uzanto"
flagAsCatDescription: "Se vi estas kato, ebligu la agordon."
autoAcceptFollowed: "Aŭtomate akcepti la peton de sekvado far uzantoj kiujn vi sekvas" autoAcceptFollowed: "Aŭtomate akcepti la peton de sekvado far uzantoj kiujn vi sekvas"
addAccount: "Aldoni konton" addAccount: "Aldoni konton"
showOnRemote: "Vidi ĉe la surloka nodo" showOnRemote: "Vidi ĉe la surloka nodo"
@ -140,15 +141,16 @@ setWallpaper: "Apliki ekranfonon"
removeWallpaper: "Forviŝi ekranfonon. " removeWallpaper: "Forviŝi ekranfonon. "
searchWith: "Serĉi: {q}" searchWith: "Serĉi: {q}"
youHaveNoLists: "Vi ne havas listojn." youHaveNoLists: "Vi ne havas listojn."
followConfirm: "Ĉu vi certas ke vi volas sekvi {name}'(o)n?" followConfirm: "Ĉu vi certas ke vi volas sekvi {name}?"
proxyAccount: "Retperanta konto"
host: "Nodo" host: "Nodo"
selectUser: "Elekti uzanton" selectUser: "Elekti uzanton"
recipient: "Ricevonto" recipient: "Ricevonton"
annotation: "Komentarioj" annotation: "Komentarioj"
federation: "Federaĵo" federation: "Federaĵo"
instances: "Nodoj" instances: "Nodoj"
latestRequestSentAt: "Lastatempa sendo" latestRequestSentAt: "La laste sendita peto"
latestRequestReceivedAt: "Lastatempa ricevo" latestRequestReceivedAt: "La laste ricevita peto "
latestStatus: "Laŭstato" latestStatus: "Laŭstato"
charts: "Diagramoj" charts: "Diagramoj"
perHour: "por horo" perHour: "por horo"
@ -157,7 +159,7 @@ blockThisInstance: "Bloki la nodon"
operations: "Agoj" operations: "Agoj"
software: "Programaro" software: "Programaro"
version: "Versio" version: "Versio"
metadata: "Metadatumoj" metadata: "Pridatumoj"
withNFiles: "{n} dosiero(j)" withNFiles: "{n} dosiero(j)"
monitor: "Monitoro" monitor: "Monitoro"
network: "Reto" network: "Reto"
@ -223,11 +225,11 @@ messageRead: "Legita"
noMoreHistory: "Ne plu de la historio" noMoreHistory: "Ne plu de la historio"
startMessaging: "Komenci babiladon" startMessaging: "Komenci babiladon"
nUsersRead: "Legita de {n} homoj" nUsersRead: "Legita de {n} homoj"
agreeTo: "Mi akceptas {0}'(o)n" agreeTo: "Mi akceptas {0}"
tos: "Kondiĉoj de uzado" tos: "Kondiĉoj de uzado"
start: "Komenciĝi" start: "Komenciĝi"
home: "Hejma" home: "Hejma"
remoteUserCaution: "Ĉi tiuj infomoj de la uzanto el fora nodo, ne estas tute ekzaktaj." remoteUserCaution: "Ĉi tiuj infomoj ne estas kompletaj, ĉar ili estas pri uzanto el la fora."
activity: "Aktiveco" activity: "Aktiveco"
images: "Bildoj" images: "Bildoj"
birthday: "Naskiĝdato" birthday: "Naskiĝdato"
@ -235,12 +237,13 @@ yearsOld: "{age} jaroj aĝa"
registeredDate: "Dato de registriĝo" registeredDate: "Dato de registriĝo"
location: "Kie" location: "Kie"
theme: "Koloraro" theme: "Koloraro"
themeForLightMode: "Luma kolararo en la luma modo" themeForLightMode: "Koloraro uzita en la luma modo"
themeForDarkMode: "Malluma kolararo en la malluma modo" themeForDarkMode: "Koloraro uzita en la malluma modo"
light: "Luma" light: "Luma"
dark: "Malluma" dark: "Malluma"
lightThemes: "Luma koloraro" lightThemes: "Luma koloraro"
darkThemes: "Malluma koloraro" darkThemes: "Malluma koloraro"
syncDeviceDarkMode: "Speguli la luman modon de via aparato"
drive: "Disko" drive: "Disko"
fileName: "Dosiernomo" fileName: "Dosiernomo"
selectFile: "Elekti dosieron" selectFile: "Elekti dosieron"
@ -262,7 +265,7 @@ inputNewFolderName: "Entajpu novan nomon de la dosierujo"
hasChildFilesOrFolders: "La dosierujo ne estas forviŝebla, ĉar ĝi ne malplenas." hasChildFilesOrFolders: "La dosierujo ne estas forviŝebla, ĉar ĝi ne malplenas."
copyUrl: "Kopii URL" copyUrl: "Kopii URL"
rename: "Alinomi" rename: "Alinomi"
avatar: "Ikono" avatar: "Bildsimbolo"
banner: "Standardo" banner: "Standardo"
nsfw: "Enhavo ne estas deca por laborejo (NSFW)" nsfw: "Enhavo ne estas deca por laborejo (NSFW)"
disconnectedFromServer: "Malkonektita de servilo" disconnectedFromServer: "Malkonektita de servilo"
@ -277,7 +280,7 @@ normal: "Normala"
instanceName: "Nomo de la nodo" instanceName: "Nomo de la nodo"
instanceDescription: "Priskribo de la nodo " instanceDescription: "Priskribo de la nodo "
maintainerName: "Nomo de la administranto" maintainerName: "Nomo de la administranto"
maintainerEmail: "Retpoŝto de la administranto" maintainerEmail: "Retpoŝtadreso de la administranto"
tosUrl: "URL de kondiĉoj de uzado" tosUrl: "URL de kondiĉoj de uzado"
thisYear: "Ĉi-jare" thisYear: "Ĉi-jare"
thisMonth: "Ĉi-monate" thisMonth: "Ĉi-monate"
@ -296,9 +299,9 @@ enableRegistration: "Ebligi novan uzanton registriĝon"
invite: "Inviti" invite: "Inviti"
driveCapacityPerLocalAccount: "Volumo de disko po unu loka uzanto" driveCapacityPerLocalAccount: "Volumo de disko po unu loka uzanto"
driveCapacityPerRemoteAccount: "Volumo de disko po unu fora uzanto" driveCapacityPerRemoteAccount: "Volumo de disko po unu fora uzanto"
iconUrl: "URL de la ikono (retpaĝsimbolo, ktp)" iconUrl: "URL de la bildsimbolo (retpaĝsimbolo, ktp.)"
bannerUrl: "URL de standardo" bannerUrl: "URL de standardo"
backgroundImageUrl: "URL de fona bildo" backgroundImageUrl: "URL de la fona bildo"
basicInfo: "Baza informo" basicInfo: "Baza informo"
pinnedUsers: "Alpinglita uzanto" pinnedUsers: "Alpinglita uzanto"
pinnedUsersDescription: "Listigu uzantnomojn apartige en ĉiu linio por alpingli al la paĝoj ekz \"Esplori\"." pinnedUsersDescription: "Listigu uzantnomojn apartige en ĉiu linio por alpingli al la paĝoj ekz \"Esplori\"."
@ -338,7 +341,7 @@ userList: "Listoj"
about: "Informoj" about: "Informoj"
aboutMisskey: "Pri Misskey" aboutMisskey: "Pri Misskey"
administrator: "Administranto" administrator: "Administranto"
token: "Ĵetono" token: "Peco"
twoStepAuthentication: "Dua-faktora aŭtentiko" twoStepAuthentication: "Dua-faktora aŭtentiko"
moderator: "Kontrolisto" moderator: "Kontrolisto"
nUsersMentioned: "{n} uzanto(j) menciis" nUsersMentioned: "{n} uzanto(j) menciis"
@ -347,12 +350,13 @@ securityKeyName: "Nomo de la ŝlosilo"
registerSecurityKey: "Registri ŝlosilon de sekureco" registerSecurityKey: "Registri ŝlosilon de sekureco"
lastUsed: "Plej malnove uzita" lastUsed: "Plej malnove uzita"
unregister: "Malregistriĝi" unregister: "Malregistriĝi"
passwordLessLogin: "Ensaluti sen pasvorto" passwordLessLogin: "Saluti sen pasvorto"
resetPassword: "Restarigi pasvorton" resetPassword: "Restarigi pasvorton"
newPasswordIs: "La nova pasvorto estas {password}." newPasswordIs: "La nova pasvorto estas {password}."
reduceUiAnimation: "Redukti la animacioj de la fasado" reduceUiAnimation: "Redukti la animaciojn de la fasado"
share: "Kundividi" share: "Kundividi"
notFound: "Ne trovita" notFound: "Ne trovita"
uploadFolder: "Dosierujo implicita por alŝuto"
cacheClear: "Malplenigi staplon" cacheClear: "Malplenigi staplon"
markAsReadAllNotifications: "Marki ĉiujn sciigojn kiel legita" markAsReadAllNotifications: "Marki ĉiujn sciigojn kiel legita"
help: "Manlibro de uzado" help: "Manlibro de uzado"
@ -362,7 +366,7 @@ group: "Grupo"
groups: "Grupoj" groups: "Grupoj"
createGroup: "Krei grupon" createGroup: "Krei grupon"
ownedGroups: "Administrataj grupoj" ownedGroups: "Administrataj grupoj"
joinedGroups: "Al grupoj kiuj vi aliĝis" joinedGroups: "Grupoj al kiuj vi aliĝis"
invites: "Inviti" invites: "Inviti"
groupName: "Grupa nomo" groupName: "Grupa nomo"
members: "Membroj" members: "Membroj"
@ -381,9 +385,9 @@ quoteQuestion: "Ĉu vi aldonas citaĵon?"
noMessagesYet: "Ankoraŭ neniu mesaĝo" noMessagesYet: "Ankoraŭ neniu mesaĝo"
newMessageExists: "Vi ricevis novan mesaĝon." newMessageExists: "Vi ricevis novan mesaĝon."
onlyOneFileCanBeAttached: "Oni povas aldoni nur unu dosieron po mesaĝo." onlyOneFileCanBeAttached: "Oni povas aldoni nur unu dosieron po mesaĝo."
signinRequired: "Bonvolu ensaluti" signinRequired: "Bonvolu saluti"
invitations: "Inviti" invitations: "Inviti"
invitationCode: "Invita kodo" invitationCode: "Kodo de invito"
available: "Disposabla" available: "Disposabla"
unavailable: "Ne disponebla" unavailable: "Ne disponebla"
usernameInvalidFormat: "La uzantnomo povas enhavi minusklajn kaj majusklajn literojn, numerojn, nur kaj '_'." usernameInvalidFormat: "La uzantnomo povas enhavi minusklajn kaj majusklajn literojn, numerojn, nur kaj '_'."
@ -394,10 +398,11 @@ normalPassword: "Normala pasvorto"
strongPassword: "Forta pasvorto" strongPassword: "Forta pasvorto"
passwordMatched: "Konforma" passwordMatched: "Konforma"
passwordNotMatched: "Nekonforma" passwordNotMatched: "Nekonforma"
signinWith: "Ensaluti kun {x}" signinWith: "Saluti kun {x}"
or: "Aŭ" or: "Aŭ"
language: "Lingvo" language: "Lingvo"
uiLanguage: "Lingvo de fasado" uiLanguage: "Lingvo de fasado"
groupInvited: "Invitita al grupo"
aboutX: "Pri {x}" aboutX: "Pri {x}"
useOsNativeEmojis: "Uzi la emoĵiojn implicitan de la operaciumo" useOsNativeEmojis: "Uzi la emoĵiojn implicitan de la operaciumo"
youHaveNoGroups: "Neniuj grupoj" youHaveNoGroups: "Neniuj grupoj"
@ -408,7 +413,7 @@ category: "Kategorio"
tags: "Etikedoj" tags: "Etikedoj"
docSource: "Fonto de la dokumento" docSource: "Fonto de la dokumento"
createAccount: "Krei konton" createAccount: "Krei konton"
existingAccount: "Ekzista konto" existingAccount: "Ekzistan konton"
regenerate: "Regeneri" regenerate: "Regeneri"
fontSize: "Tipara grando" fontSize: "Tipara grando"
noFollowRequests: "Vi ne havas peto de sekvado" noFollowRequests: "Vi ne havas peto de sekvado"
@ -426,9 +431,10 @@ objectStorageBaseUrl: "Baza URL"
objectStoragePrefix: "Prefix" objectStoragePrefix: "Prefix"
objectStorageRegion: "Regiono" objectStorageRegion: "Regiono"
objectStorageUseSSL: "Oni uzas SSL" objectStorageUseSSL: "Oni uzas SSL"
objectStorageUseProxy: "Uzi retperilon"
serverLogs: "Servila protokolo" serverLogs: "Servila protokolo"
deleteAll: "Forviŝi ĉiujn" deleteAll: "Forviŝi ĉiujn"
newNoteRecived: "Jen estas novaj notoj" newNoteRecived: "Jen novaj notoj"
sounds: "Sonoj" sounds: "Sonoj"
listen: "Aŭdi" listen: "Aŭdi"
none: "Neniu" none: "Neniu"
@ -467,8 +473,8 @@ enableInfiniteScroll: "Ebligi infinitan rulumon"
visibility: "Videbleco" visibility: "Videbleco"
poll: "Balotujo" poll: "Balotujo"
useCw: "Kaŝi enhavo" useCw: "Kaŝi enhavo"
enablePlayer: "Vidi videon" enablePlayer: "Vidigi la filmeton"
disablePlayer: "Fermi videon" disablePlayer: "Malfermi la filmeton"
expandTweet: "Disvolvi pepon" expandTweet: "Disvolvi pepon"
themeEditor: "Redaktilo de koloraroj" themeEditor: "Redaktilo de koloraroj"
description: "Priskribo" description: "Priskribo"
@ -484,15 +490,16 @@ height: "Alteco"
large: "Granda" large: "Granda"
medium: "Meza" medium: "Meza"
small: "Malgranda" small: "Malgranda"
generateAccessToken: "Generi ĵetonon de aliro" generateAccessToken: "Generi aŭtentikigan pecon"
permission: "Permesoj" permission: "Permesoj"
enableAll: "Ebligi ĉiujn" enableAll: "Ebligi ĉiujn"
disableAll: "Malebligi ĉiujn" disableAll: "Malebligi ĉiujn"
notificationType: "Tipo de sciigoj" notificationType: "Tipo de sciigoj"
edit: "Redakti" edit: "Redakti"
emailServer: "Retpoŝta servilo" emailServer: "Retpoŝta servilo"
enableEmail: "Ebligi dissendon el retpoŝto"
email: "Retpoŝto" email: "Retpoŝto"
emailAddress: "Retpoŝta adreso" emailAddress: "Retpoŝtadreso"
smtpConfig: "Agordoj de SMTP servilo" smtpConfig: "Agordoj de SMTP servilo"
smtpHost: "Transa servilo" smtpHost: "Transa servilo"
smtpPort: "Pordo" smtpPort: "Pordo"
@ -513,14 +520,14 @@ create: "Krei"
notificationSetting: "Agordoj de sciigoj" notificationSetting: "Agordoj de sciigoj"
useGlobalSetting: "Oni uzas malloka agordo" useGlobalSetting: "Oni uzas malloka agordo"
other: "Aliaj" other: "Aliaj"
regenerateLoginToken: "Regeneri la ĵetonon de aliro" regenerateLoginToken: "Regeneri la aŭtentikigan pecon"
fileIdOrUrl: "Dosiera identigilo aŭ URL" fileIdOrUrl: "Dosiera identigilo aŭ URL"
chatOpenBehavior: "Konduto por malfermi la fenestron de babilejo" chatOpenBehavior: "Konduto por malfermi la fenestron de babilejo"
behavior: "Konduto" behavior: "Konduto"
sample: "Ekzemplo" sample: "Ekzemplo"
abuseReports: "Signaloj" abuseReports: "Signaloj"
reportAbuse: "Signalo" reportAbuse: "Signalo"
reportAbuseOf: "Signali kontraŭ {name}'(o)" reportAbuseOf: "Signali kontraŭ {name}"
send: "Sendi" send: "Sendi"
openInNewTab: "Malfermi en nova langeto" openInNewTab: "Malfermi en nova langeto"
editTheseSettingsMayBreakAccount: "Redakti ĉi tiujn agordojn povas damaĝi vian konton." editTheseSettingsMayBreakAccount: "Redakti ĉi tiujn agordojn povas damaĝi vian konton."
@ -528,6 +535,7 @@ instanceTicker: "Nomo de la nodo sendinta notojn"
waitingFor: "Atendado pro {x}" waitingFor: "Atendado pro {x}"
random: "Hazarde" random: "Hazarde"
system: "Sistemo" system: "Sistemo"
switchUi: "Modifi la aspektigon"
desktop: "Labortablo" desktop: "Labortablo"
createNew: "Krei novan" createNew: "Krei novan"
optional: "Opciaj" optional: "Opciaj"
@ -563,7 +571,7 @@ wide: "Vasta"
narrow: "Malvasta" narrow: "Malvasta"
showTitlebar: "Videbligi titolan stangon" showTitlebar: "Videbligi titolan stangon"
clearCache: "Malplenigi staplon" clearCache: "Malplenigi staplon"
onlineUsersCount: "{n} uzanto(j) estas surlinea" onlineUsersCount: "{n} uzantoj estas surlineaj"
nUsers: "{n} uzanto(j)" nUsers: "{n} uzanto(j)"
nNotes: "{n} notoj" nNotes: "{n} notoj"
myTheme: "Miaj koloraroj" myTheme: "Miaj koloraroj"
@ -583,6 +591,7 @@ youAreRunningUpToDateClient: "Vi uzas la plej novan version de via kliento."
newVersionOfClientAvailable: "Nova versio de via kliento estas disponebla." newVersionOfClientAvailable: "Nova versio de via kliento estas disponebla."
inUse: "Uzata" inUse: "Uzata"
editCode: "Redakti kodon" editCode: "Redakti kodon"
receiveAnnouncementFromInstance: "Ricevi informojn sciigintajn de la nodo"
emailNotification: "Sciigoj per retpoŝto" emailNotification: "Sciigoj per retpoŝto"
inChannelSearch: "Serĉi en kanalo" inChannelSearch: "Serĉi en kanalo"
useReactionPickerForContextMenu: "Dekstre-klaki por malfermi la elektilon de reagoj" useReactionPickerForContextMenu: "Dekstre-klaki por malfermi la elektilon de reagoj"
@ -617,6 +626,7 @@ troubleshooting: "Problemsolvi"
learnMore: "Lernu pli" learnMore: "Lernu pli"
translate: "Traduki" translate: "Traduki"
translatedFrom: "Tradukita el {x}" translatedFrom: "Tradukita el {x}"
breakFollow: "Ĉesigi la sekvadon al vi"
itsOn: "Ŝaltita" itsOn: "Ŝaltita"
unread: "Nelegita" unread: "Nelegita"
controlPanel: "Ŝaltpodio" controlPanel: "Ŝaltpodio"
@ -647,7 +657,7 @@ _gallery:
like: "Ŝati" like: "Ŝati"
_email: _email:
_follow: _follow:
title: "Vi estas eksekvita" title: "Eksekvis vin"
_receiveFollowRequest: _receiveFollowRequest:
title: "Vi ricevis peton de sekvado" title: "Vi ricevis peton de sekvado"
_plugin: _plugin:
@ -735,12 +745,12 @@ _sfx:
notification: "Sciigoj" notification: "Sciigoj"
chat: "Retbabili" chat: "Retbabili"
chatBg: "Retbabili (BG)" chatBg: "Retbabili (BG)"
antenna: "Ricevo de anteno" antenna: "Ricevo de la anteno"
channel: "Sciigoj de kanalo" channel: "Sciigoj de kanalo"
_ago: _ago:
future: "Futuro" future: "Futuro"
justNow: "Ĵus" justNow: "Ĵus"
secondsAgo: "Antaŭ {n} sekundo(j)" secondsAgo: "Antaŭ {n} sekundoj"
minutesAgo: "Antaŭ {n} minutoj" minutesAgo: "Antaŭ {n} minutoj"
hoursAgo: "Antaŭ {n} horo(j)" hoursAgo: "Antaŭ {n} horo(j)"
daysAgo: "Antaŭ {n} tago(j)" daysAgo: "Antaŭ {n} tago(j)"
@ -780,6 +790,7 @@ _permissions:
"read:reactions": "Vidi reagojn" "read:reactions": "Vidi reagojn"
"write:reactions": "Redakti viajn reagojn" "write:reactions": "Redakti viajn reagojn"
"read:page-likes": "Vidi ŝatojn de paĝo" "read:page-likes": "Vidi ŝatojn de paĝo"
"read:user-groups": "Vidi viajn grupojn de uzantoj"
"read:channels": "Vidi kanalojn" "read:channels": "Vidi kanalojn"
_antennaSources: _antennaSources:
all: "Ĉiuj notoj" all: "Ĉiuj notoj"
@ -827,23 +838,23 @@ _visibility:
_postForm: _postForm:
replyPlaceholder: "Respondi la noton…" replyPlaceholder: "Respondi la noton…"
quotePlaceholder: "Citi la noton…" quotePlaceholder: "Citi la noton…"
channelPlaceholder: "Mencii en kanalo…" channelPlaceholder: "Mencii en la kanalo…"
_profile: _profile:
name: "Nomo" name: "Nomo"
username: "Uzantnomo" username: "Uzantnomo"
description: "Sinprezento" description: "Sinprezento"
metadata: "Kromaj informoj" metadata: "Kromaj informoj"
metadataEdit: "Redakti kromaj informoj" metadataEdit: "Redakti kromajn informojn"
changeAvatar: "Ŝanĝi profilbildon" changeAvatar: "Ŝanĝi profilbildon"
changeBanner: "Ŝanĝi standardon" changeBanner: "Ŝanĝi standardon"
_exportOrImport: _exportOrImport:
allNotes: "Ĉiuj notoj" allNotes: "Ĉiuj notoj"
followingList: "Sekvataj uzantoj" followingList: "Sekvatoj"
muteList: "Silentigoj" muteList: "Silentigoj"
blockingList: "Blokitoj" blockingList: "Blokitoj"
userLists: "Listoj" userLists: "Listoj"
_charts: _charts:
federationInstancesTotal: "La totala nombro de nodoj kunfederantaj" federationInstancesTotal: "La totala nombro de nodoj federantaj"
usersTotal: "La totala nombro de la uzantoj" usersTotal: "La totala nombro de la uzantoj"
activeUsers: "La nombro de la uzantoj aktivaj" activeUsers: "La nombro de la uzantoj aktivaj"
notesTotal: "La totala nombro de notoj" notesTotal: "La totala nombro de notoj"
@ -914,6 +925,7 @@ _pages:
default: "Implicitaĵa valoro" default: "Implicitaĵa valoro"
_canvas: _canvas:
id: "Kanvasa identigilo" id: "Kanvasa identigilo"
width: "Larĝeco"
_note: _note:
id: "Identigilo de noto" id: "Identigilo de noto"
_switch: _switch:
@ -924,8 +936,11 @@ _pages:
_button: _button:
text: "Titolo" text: "Titolo"
_action: _action:
_dialog:
content: "Enhavo"
_pushEvent: _pushEvent:
event: "Nomo de la evento" event: "Nomo de la evento"
no-variable: "Neniu"
_radioButton: _radioButton:
title: "Titolo" title: "Titolo"
default: "Implicitaĵa valoro" default: "Implicitaĵa valoro"
@ -991,6 +1006,7 @@ _notification:
youWereFollowed: "eksekvis vin" youWereFollowed: "eksekvis vin"
youReceivedFollowRequest: "Vi ricevis peton de sekvado" youReceivedFollowRequest: "Vi ricevis peton de sekvado"
yourFollowRequestAccepted: "Via peto de sekvado estis akceptita." yourFollowRequestAccepted: "Via peto de sekvado estis akceptita."
youWereInvitedToGroup: "Invitita al grupo"
_types: _types:
all: "Ĉio" all: "Ĉio"
follow: "Novaj sekvatoj" follow: "Novaj sekvatoj"
@ -1001,6 +1017,7 @@ _notification:
reaction: "Reagoj" reaction: "Reagoj"
receiveFollowRequest: "Ricevi peton de sekvado" receiveFollowRequest: "Ricevi peton de sekvado"
followRequestAccepted: "Akceptita peto por sekvado" followRequestAccepted: "Akceptita peto por sekvado"
groupInvited: "Invitita al grupo"
_deck: _deck:
profile: "Agordaro" profile: "Agordaro"
_columns: _columns:

View file

@ -738,6 +738,7 @@ lastCommunication: "Última comunicación"
resolved: "Resuelto" resolved: "Resuelto"
unresolved: "Sin resolver" unresolved: "Sin resolver"
controlPanel: "Panel de control" controlPanel: "Panel de control"
hide: "Ocultar"
_accountDelete: _accountDelete:
accountDelete: "Eliminar Cuenta" accountDelete: "Eliminar Cuenta"
_ad: _ad:

View file

@ -798,6 +798,7 @@ filter: "Filtre"
controlPanel: "Panneau de contrôle" controlPanel: "Panneau de contrôle"
manageAccounts: "Gérer les comptes" manageAccounts: "Gérer les comptes"
classic: "Classique" classic: "Classique"
hide: "Masquer"
_emailUnavailable: _emailUnavailable:
format: "Le format de cette adresse de courriel est invalide" format: "Le format de cette adresse de courriel est invalide"
mx: "Ce serveur de courriels est invalide" mx: "Ce serveur de courriels est invalide"

View file

@ -810,6 +810,7 @@ continueThread: "Lihat lanjutan thread"
deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?"
incorrectPassword: "Kata sandi salah." incorrectPassword: "Kata sandi salah."
voteConfirm: "Konfirmasi suara kamu untuk ({choice})" voteConfirm: "Konfirmasi suara kamu untuk ({choice})"
hide: "Sembunyikan"
_emailUnavailable: _emailUnavailable:
used: "Alamat surel ini telah digunakan" used: "Alamat surel ini telah digunakan"
format: "Format tidak valid." format: "Format tidak valid."

View file

@ -46,7 +46,10 @@ const primaries = {
'zh': 'CN', 'zh': 'CN',
}; };
const locales = languages.reduce((a, c) => (a[c] = yaml.load(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8')) || {}, a), {}); // 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く
const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {});
module.exports = Object.entries(locales) module.exports = Object.entries(locales)
.reduce((a, [k ,v]) => (a[k] = (() => { .reduce((a, [k ,v]) => (a[k] = (() => {

View file

@ -745,6 +745,7 @@ global: "Federata"
sent: "Inviare" sent: "Inviare"
hashtags: "Hashtag" hashtags: "Hashtag"
troubleshooting: "Risoluzione problemi" troubleshooting: "Risoluzione problemi"
hide: "Nascondere"
_ffVisibility: _ffVisibility:
public: "Pubblico" public: "Pubblico"
_ad: _ad:

View file

@ -106,7 +106,7 @@ clickToShow: "クリックして表示"
sensitive: "閲覧注意" sensitive: "閲覧注意"
add: "追加" add: "追加"
reaction: "リアクション" reaction: "リアクション"
reactionSettingDescription: "リアクションピッカーに表示するリアクションを設定します。" reactionSetting: "ピッカーに表示するリアクション"
reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。" reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。"
rememberNoteVisibility: "公開範囲を記憶する" rememberNoteVisibility: "公開範囲を記憶する"
attachCancel: "添付取り消し" attachCancel: "添付取り消し"
@ -813,6 +813,9 @@ deleteAccountConfirm: "アカウントが削除されます。よろしいです
incorrectPassword: "パスワードが間違っています。" incorrectPassword: "パスワードが間違っています。"
voteConfirm: "「{choice}」に投票しますか?" voteConfirm: "「{choice}」に投票しますか?"
hide: "隠す" hide: "隠す"
leaveGroup: "グループから抜ける"
leaveGroupConfirm: "「{name}」から抜けますか?"
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
_emailUnavailable: _emailUnavailable:
used: "既に使用されています" used: "既に使用されています"

View file

@ -653,6 +653,7 @@ low: "低い"
global: "グローバル" global: "グローバル"
sent: "送信" sent: "送信"
hashtags: "ハッシュタグ" hashtags: "ハッシュタグ"
hide: "隠す"
_ad: _ad:
back: "戻る" back: "戻る"
_gallery: _gallery:

View file

@ -81,6 +81,8 @@ somethingHappened: "오류가 발생했습니다"
retry: "다시 시도" retry: "다시 시도"
pageLoadError: "페이지를 불러오지 못했습니다." pageLoadError: "페이지를 불러오지 못했습니다."
pageLoadErrorDescription: "네트워크 연결 또는 브라우저 캐시로 인해 발생했을 가능성이 높습니다. 캐시를 삭제하거나, 잠시 후 다시 시도해 주세요." pageLoadErrorDescription: "네트워크 연결 또는 브라우저 캐시로 인해 발생했을 가능성이 높습니다. 캐시를 삭제하거나, 잠시 후 다시 시도해 주세요."
serverIsDead: "서버로부터 응답이 없습니다. 잠시 후 다시 시도해주세요."
youShouldUpgradeClient: "이 페이지를 표시하려면 새로고침하여 새로운 버전의 클라이언트를 이용해 주십시오."
enterListName: "리스트 이름을 입력" enterListName: "리스트 이름을 입력"
privacy: "프라이버시" privacy: "프라이버시"
makeFollowManuallyApprove: "팔로우를 수동으로 승인" makeFollowManuallyApprove: "팔로우를 수동으로 승인"
@ -590,6 +592,7 @@ smtpSecure: "SMTP 연결에 Implicit SSL/TTS 사용"
smtpSecureInfo: "STARTTLS 사용 시에는 해제합니다." smtpSecureInfo: "STARTTLS 사용 시에는 해제합니다."
testEmail: "이메일 전송 테스트" testEmail: "이메일 전송 테스트"
wordMute: "단어 뮤트" wordMute: "단어 뮤트"
instanceMute: "인스턴스 뮤트"
userSaysSomething: "{name}님이 무언가를 말했습니다" userSaysSomething: "{name}님이 무언가를 말했습니다"
makeActive: "활성화" makeActive: "활성화"
display: "표시" display: "표시"
@ -618,6 +621,8 @@ reportAbuse: "신고"
reportAbuseOf: "{name}을 신고하기" reportAbuseOf: "{name}을 신고하기"
fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요." fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요."
abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다." abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다."
reporteeOrigin: "피신고자"
reporterOrigin: "신고자"
send: "전송" send: "전송"
abuseMarkAsResolved: "해결됨으로 표시" abuseMarkAsResolved: "해결됨으로 표시"
openInNewTab: "새 탭에서 열기" openInNewTab: "새 탭에서 열기"
@ -764,6 +769,7 @@ middle: "보통"
low: "낮음" low: "낮음"
emailNotConfiguredWarning: "메일 주소가 설정되어 있지 않습니다." emailNotConfiguredWarning: "메일 주소가 설정되어 있지 않습니다."
ratio: "비율" ratio: "비율"
previewNoteText: "본문 미리보기"
customCss: "CSS 사용자화" customCss: "CSS 사용자화"
customCssWarn: "이 설정은 기능을 알고 있는 경우에만 사용해야 합니다. 잘못된 값을 입력하면 클라이언트가 정상적으로 작동하지 않을 수 있습니다." customCssWarn: "이 설정은 기능을 알고 있는 경우에만 사용해야 합니다. 잘못된 값을 입력하면 클라이언트가 정상적으로 작동하지 않을 수 있습니다."
global: "글로벌" global: "글로벌"
@ -787,9 +793,40 @@ pubSub: "Pub/Sub 계정"
lastCommunication: "마지막 통신" lastCommunication: "마지막 통신"
resolved: "해결됨" resolved: "해결됨"
unresolved: "해결되지 않음" unresolved: "해결되지 않음"
breakFollow: "팔로워 해제"
itsOn: "켜짐"
itsOff: "꺼짐"
emailRequiredForSignup: "가입할 때 이메일 주소 입력을 필수로 하기"
unread: "읽지 않음"
filter: "필터"
controlPanel: "제어판" controlPanel: "제어판"
manageAccounts: "계정 관리"
makeReactionsPublic: "리액션 목록을 공개하기"
makeReactionsPublicDescription: "나의 리액션을 누구나 볼 수 있게 합니다."
classic: "클래식"
muteThread: "이 글타래를 뮤트"
unmuteThread: "글타래 뮤트 해제"
ffVisibility: "내 인맥의 공개 범위"
ffVisibilityDescription: "나의 팔로우와 팔로워 정보에 대한 공개 범위를 설정할 수 있습니다."
continueThread: "이 글타래 이어서 보기"
deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 됩니다. 계속하시겠습니까? "
incorrectPassword: "비밀번호가 올바르지 않습니다."
voteConfirm: "\"{choice}\"에 투표하시겠습니까?"
hide: "숨기기"
_emailUnavailable:
used: "이 메일 주소는 사용중입니다"
format: "형식이 올바르지 않습니다"
disposable: "임시 이메일 주소는 사용할 수 없습니다"
mx: "메일 서버가 올바르지 않습니다"
smtp: "메일 서버가 응답하지 않습니다"
_ffVisibility: _ffVisibility:
public: "게시" public: "공개"
followers: "팔로워에게만 공개"
private: "비공개"
_signup:
almostThere: "거의 다 끝났습니다"
emailAddressInfo: "당신이 사용하고 있는 이메일 주소를 입력해 주세요. 이메일 주소는 다른 유저에게 공개되지 않습니다."
emailSent: "입력하신 메일 주소({email})로 확인 메일을 보내드렸습니다. 가입을 완료하시려면 보내드린 메일에 있는 링크로 접속해 주세요."
_accountDelete: _accountDelete:
accountDelete: "계정 삭제" accountDelete: "계정 삭제"
mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다." mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다."
@ -900,6 +937,7 @@ _mfm:
sparkle: "반짝반짝" sparkle: "반짝반짝"
sparkleDescription: "반짝이는 파티클 효과를 추가합니다." sparkleDescription: "반짝이는 파티클 효과를 추가합니다."
rotate: "회전" rotate: "회전"
rotateDescription: "지정한 각도로 회전시킵니다."
_reversi: _reversi:
reversi: "리버시" reversi: "리버시"
gameSettings: "대국 설정" gameSettings: "대국 설정"
@ -965,6 +1003,11 @@ _wordMute:
soft: "보통" soft: "보통"
hard: "보다 높은 수준" hard: "보다 높은 수준"
mutedNotes: "뮤트된 노트" mutedNotes: "뮤트된 노트"
_instanceMute:
instanceMuteDescription: "뮤트한 인스턴스에서 오는 답글을 포함한 모든 노트와 Renote를 뮤트합니다."
instanceMuteDescription2: "한 줄에 하나씩 입력해 주세요"
title: "지정한 인스턴스의 노트를 숨깁니다."
heading: "뮤트할 인스턴스"
_theme: _theme:
explore: "테마 찾아보기" explore: "테마 찾아보기"
install: "테마 설치" install: "테마 설치"
@ -1238,6 +1281,8 @@ _exportOrImport:
muteList: "뮤트" muteList: "뮤트"
blockingList: "차단" blockingList: "차단"
userLists: "리스트" userLists: "리스트"
excludeMutingUsers: "뮤트한 유저 제외하기"
excludeInactiveUsers: "휴면 중인 계정 제외하기"
_charts: _charts:
federationInstancesIncDec: "연합 인스턴스 수 증감" federationInstancesIncDec: "연합 인스턴스 수 증감"
federationInstancesTotal: "연합 인스턴스 수 합계" federationInstancesTotal: "연합 인스턴스 수 합계"

View file

@ -120,16 +120,144 @@ unblock: "Deblokkeren"
suspend: "Opschorten" suspend: "Opschorten"
unsuspend: "Heractiveren" unsuspend: "Heractiveren"
blockConfirm: "Weet je zeker dat je dit account wil blokkeren?" blockConfirm: "Weet je zeker dat je dit account wil blokkeren?"
searchWith: "Zoeken: {q}"
youHaveNoLists: "Je hebt geen lijsten"
followConfirm: "Weet je zeker dat je {name} wilt volgen?"
proxyAccount: "Proxy account"
proxyAccountDescription: "Een proxy-account is een account dat onder bepaalde voorwaarden fungeert als externe volger voor gebruikers. Als een gebruiker bijvoorbeeld een externe gebruiker aan de lijst toevoegt, wordt de activiteit van de externe gebruiker niet aan de server geleverd als geen lokale gebruiker die gebruiker volgt, dus het proxy-account volgt in plaats daarvan."
host: "Server"
selectUser: "Kies een gebruiker"
recipient: "Ontvanger"
annotation: "Reacties"
federation: "Federatie"
instances: "Server" instances: "Server"
registeredAt: "Geregistreerd op"
latestRequestSentAt: "Laatste aanvraag verstuurd"
latestRequestReceivedAt: "Laatste aanvraag ontvangen"
latestStatus: "Laatste status"
storageUsage: "Gebruikte opslagruimte"
charts: "Grafieken"
perHour: "Per uur"
perDay: "Per dag"
stopActivityDelivery: "Stop met versturen activiteiten"
blockThisInstance: "Blokkeer deze server"
operations: "Verwerkingen"
software: "Software"
version: "Versie"
metadata: "Metadata"
withNFiles: "{n} bestand(en)"
monitor: "Monitor"
jobQueue: "Job Queue"
cpuAndMemory: "CPU en geheugen"
network: "Netwerk"
disk: "Schijfruimte"
instanceInfo: "Serverinformatie"
statistics: "Statistieken"
clearQueue: "Wachtrij wissen"
clearQueueConfirmTitle: "Weet je zeker dat je de wachtrji leeg wil maken?"
clearQueueConfirmText: "Niet-bezorgde biljetten die nog in de wachtrij staan, worden niet gefedereerd. Meestal is deze operatie niet nodig."
clearCachedFiles: "Cache opschonen"
clearCachedFilesConfirm: "Weet je zeker dat je alle externe bestanden in de cache wilt verwijderen?"
blockedInstances: "Geblokkeerde servers"
blockedInstancesDescription: "Maak een lijst van de servers die moeten worden geblokkeerd, gescheiden door regeleinden. Geblokkeerde servers kunnen niet meer communiceren met deze server."
muteAndBlock: "Gedempt en geblokkeerd"
mutedUsers: "Gedempte gebruikers"
blockedUsers: "Geblokkeerde gebruikers"
noUsers: "Er zijn geen gebruikers."
editProfile: "Bewerk Profiel"
noteDeleteConfirm: "Ben je zeker dat je dit bericht wil verwijderen?"
pinLimitExceeded: "Je kunt geen berichten meer vastprikken"
intro: "Installatie van Misskey geëindigd! Maak nu een beheerder aan."
done: "Klaar"
processing: "Bezig met verwerken"
preview: "Voorbeeld"
default: "Standaard"
noCustomEmojis: "Er zijn geen emojis"
noJobs: "Er zijn geen taken"
federating: "Federeren"
blocked: "Geblokkeerd"
suspended: "Opgeschort"
all: "Alle"
subscribing: "Abonneren"
publishing: "Publiceren"
notResponding: "Reageert niet"
instanceFollowing: "Volgend op server"
instanceFollowers: "Volgers op server"
instanceUsers: "Gebruikers van deze server"
changePassword: "Wachtwoord wijzigen"
security: "Beveiliging"
retypedNotMatch: "Invoer komt niet overeen"
currentPassword: "Huidig wachtwoord"
newPassword: "Nieuwe wachtwoord"
newPasswordRetype: "Nieuw wachtwoord (herhalen)"
attachFile: "Bestanden toevoegen"
more: "Meer!"
featured: "Uitgelicht"
usernameOrUserId: "Gebruikersnaam of id"
noSuchUser: "Gebruiker niet gevonden"
lookup: "Opzoeken"
announcements: "Aankondigingen"
imageUrl: "AfbeeldingsURL"
remove: "Verwijderen" remove: "Verwijderen"
removed: "Succesvol verwijderd"
removeAreYouSure: "Weet je zeker dat je \"{x}\" wil verwijderen?"
deleteAreYouSure: "Weet je zeker dat je \"{x}\" wil verwijderen?"
resetAreYouSure: "Resetten?"
saved: "Opgeslagen"
messaging: "Chat"
upload: "Uploaden"
fromDrive: "Van schijf"
fromUrl: "Van URL"
uploadFromUrl: "Uploaden vanaf een URL"
uploadFromUrlDescription: "URL van het bestand dat je wil uploaden"
uploadFromUrlRequested: "Uploadverzoek"
uploadFromUrlMayTakeTime: "Het kan even duren voordat het uploaden voltooid is."
explore: "Verkennen"
games: "Misskey spellen"
messageRead: "Lezen"
noMoreHistory: "Er is geen verdere geschiedenis"
startMessaging: "Start een gesprek"
nUsersRead: "gelezen door {n}"
agreeTo: "Ik stem in met {0}"
tos: "Gebruiksvoorwaarden"
start: "Aan de slag"
home: "Startpagina"
remoteUserCaution: "Aangezien deze gebruiker van een externe server afkomstig is, kan de weergegeven informatie onvolledig zijn."
activity: "Activiteit"
images: "Afbeeldingen"
birthday: "Geboortedatum"
yearsOld: "{age} jaar"
registeredDate: "Inschrijvingsdatum"
location: "Locatie"
theme: "Thema's"
themeForLightMode: "Thema voor gebruik in de lichte modus"
themeForDarkMode: "Thema voor gebruik in de donkere modus"
light: "Licht"
dark: "Donker"
lightThemes: "Licht thema's"
darkThemes: "Donkere thema's"
syncDeviceDarkMode: "Synchroniseer donkere modus met je apparaatinstellingen"
drive: "Schijf"
fileName: "Bestandsnaam"
selectFile: "Kies een bestand"
selectFiles: "Selecteer bestanden"
selectFolder: "Kies een map"
selectFolders: "Kies mappen"
renameFile: "Wijzig bestandsnaam"
folderName: "Mapnaam"
createFolder: "Map aanmaken"
renameFolder: "Map hernoemen"
nsfw: "NSFW" nsfw: "NSFW"
pinnedNotes: "Vastgemaakte notitie" pinnedNotes: "Vastgemaakte notitie"
userList: "Lijsten" userList: "Lijsten"
smtpHost: "Server"
smtpUser: "Gebruikersnaam" smtpUser: "Gebruikersnaam"
smtpPass: "Wachtwoord" smtpPass: "Wachtwoord"
clearCache: "Cache opschonen"
user: "Gebruikers" user: "Gebruikers"
muteThread: "Discussies dempen " muteThread: "Discussies dempen "
unmuteThread: "Dempen van discussie ongedaan maken" unmuteThread: "Dempen van discussie ongedaan maken"
hide: "Verbergen"
_email: _email:
_follow: _follow:
title: "volgde jou" title: "volgde jou"
@ -144,12 +272,17 @@ _theme:
_sfx: _sfx:
note: "Notities" note: "Notities"
notification: "Meldingen" notification: "Meldingen"
chat: "Chat"
_widgets: _widgets:
notifications: "Meldingen" notifications: "Meldingen"
timeline: "Tijdlijn" timeline: "Tijdlijn"
activity: "Activiteit"
federation: "Federatie"
jobQueue: "Job Queue"
_cw: _cw:
show: "Laad meer" show: "Laad meer"
_visibility: _visibility:
home: "Startpagina"
followers: "Volgers" followers: "Volgers"
_profile: _profile:
username: "Gebruikersnaam" username: "Gebruikersnaam"
@ -158,7 +291,18 @@ _exportOrImport:
muteList: "Dempen" muteList: "Dempen"
blockingList: "Blokkeren" blockingList: "Blokkeren"
userLists: "Lijsten" userLists: "Lijsten"
excludeMutingUsers: "Negeer gedempte gebruikers"
excludeInactiveUsers: "Negeer inactieve gebruikers"
_timelines:
home: "Startpagina"
_rooms:
_roomType:
default: "Standaard"
_furnitures:
monitor: "Monitor"
_pages: _pages:
blocks:
image: "Afbeeldingen"
script: script:
categories: categories:
list: "Lijsten" list: "Lijsten"

View file

@ -738,6 +738,7 @@ ratio: "Stosunek"
global: "Globalna" global: "Globalna"
sent: "Wyślij" sent: "Wyślij"
hashtags: "Hashtag" hashtags: "Hashtag"
hide: "Ukryj"
_ffVisibility: _ffVisibility:
public: "Publikuj" public: "Publikuj"
_ad: _ad:

View file

@ -802,6 +802,7 @@ makeReactionsPublicDescription: "Список сделанных вами реа
classic: "Классика" classic: "Классика"
unmuteThread: "Отключить звук" unmuteThread: "Отключить звук"
ffVisibilityDescription: "Вы можете установить объем вашей следующей/последней информации." ffVisibilityDescription: "Вы можете установить объем вашей следующей/последней информации."
hide: "Спрятать"
_emailUnavailable: _emailUnavailable:
used: "Уже используется" used: "Уже используется"
format: "Неправильный формат" format: "Неправильный формат"
@ -923,6 +924,7 @@ _mfm:
sparkle: "Блеск" sparkle: "Блеск"
sparkleDescription: "Добавьте эффект искрящихся частиц." sparkleDescription: "Добавьте эффект искрящихся частиц."
rotate: "Повернуть" rotate: "Повернуть"
rotateDescription: "Повернуть на указанный угол."
_reversi: _reversi:
reversi: "Реверси" reversi: "Реверси"
gameSettings: "Настройки игры" gameSettings: "Настройки игры"

View file

@ -692,6 +692,7 @@ middle: "Середній"
global: "Глобальна" global: "Глобальна"
sent: "Відправити" sent: "Відправити"
hashtags: "Хештеґ" hashtags: "Хештеґ"
hide: "Сховати"
_ad: _ad:
back: "Назад" back: "Назад"
_gallery: _gallery:

View file

@ -592,6 +592,7 @@ smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS"
smtpSecureInfo: "使用STARTTLS时关闭。" smtpSecureInfo: "使用STARTTLS时关闭。"
testEmail: "邮件发送测试" testEmail: "邮件发送测试"
wordMute: "文字屏蔽" wordMute: "文字屏蔽"
instanceMute: "实例的屏蔽"
userSaysSomething: "{name}说了什么" userSaysSomething: "{name}说了什么"
makeActive: "启用" makeActive: "启用"
display: "显示" display: "显示"
@ -811,6 +812,7 @@ continueThread: "查看更多帖子"
deleteAccountConfirm: "将要删除账户。是否确认?" deleteAccountConfirm: "将要删除账户。是否确认?"
incorrectPassword: "密码错误" incorrectPassword: "密码错误"
voteConfirm: "确定投给“{choice}” " voteConfirm: "确定投给“{choice}” "
hide: "隐藏"
_emailUnavailable: _emailUnavailable:
used: "已经被使用过" used: "已经被使用过"
format: "无效的格式" format: "无效的格式"
@ -1001,6 +1003,11 @@ _wordMute:
soft: "软屏蔽" soft: "软屏蔽"
hard: "硬屏蔽" hard: "硬屏蔽"
mutedNotes: "被屏蔽的帖子" mutedNotes: "被屏蔽的帖子"
_instanceMute:
instanceMuteDescription: "屏蔽配置实例中的所有帖子和转帖,包括实例的用户回复。"
instanceMuteDescription2: "设置时用换行符来分隔"
title: "隐藏实例已设置的帖子。"
heading: "屏蔽实例"
_theme: _theme:
explore: "寻找主题" explore: "寻找主题"
install: "安装主题" install: "安装主题"
@ -1274,6 +1281,8 @@ _exportOrImport:
muteList: "屏蔽" muteList: "屏蔽"
blockingList: "拉黑" blockingList: "拉黑"
userLists: "列表" userLists: "列表"
excludeMutingUsers: "排除屏蔽用户"
excludeInactiveUsers: "排除不活跃用户"
_charts: _charts:
federationInstancesIncDec: "联合:增加/减少" federationInstancesIncDec: "联合:增加/减少"
federationInstancesTotal: "联合总数" federationInstancesTotal: "联合总数"

View file

@ -754,6 +754,7 @@ ratio: "%"
global: "公開" global: "公開"
sent: "發送" sent: "發送"
hashtags: "#tag" hashtags: "#tag"
hide: "隱藏"
_ffVisibility: _ffVisibility:
public: "發佈" public: "發佈"
_ad: _ad:

View file

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "12.98.0", "version": "12.100.1",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -0,0 +1,189 @@
const { MigrationInterface, QueryRunner } = require("typeorm");
module.exports = class chartV31639325650583 {
name = 'chartV31639325650583'
async up(queryRunner) {
await queryRunner.query(`DELETE FROM "__chart__per_user_drive" WHERE "group" IS NULL`);
await queryRunner.query(`DROP INDEX "public"."IDX_dd907becf76104e4b656659e6b"`);
await queryRunner.query(`DROP INDEX "public"."IDX_eddfed8fb40305a04c6f941050"`);
await queryRunner.query(`DROP INDEX "public"."IDX_f09d543e3acb16c5976bdb31fa"`);
await queryRunner.query(`DROP INDEX "public"."IDX_e60c358aaced5aab8900a4af31"`);
await queryRunner.query(`DROP INDEX "public"."IDX_337e9599f278bd7537fe30876f"`);
await queryRunner.query(`DROP INDEX "public"."IDX_66feba81e1795d176d06c0b1e6"`);
await queryRunner.query(`DROP INDEX "public"."IDX_0a905b992fecd2b5c3fb98759e"`);
await queryRunner.query(`DROP INDEX "public"."IDX_2082327b2699ce924fa654afc5"`);
await queryRunner.query(`DROP INDEX "public"."IDX_9a3ed15a30ab7e3a37702e6e08"`);
await queryRunner.query(`DROP INDEX "public"."IDX_60c5c6e7e538c09aa274ecd1cf"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8111b817b9818c04d7eb8475b1"`);
await queryRunner.query(`DROP INDEX "public"."IDX_583a157ed0cf0ed1b5ec2a833f"`);
await queryRunner.query(`DROP INDEX "public"."IDX_3313d7288855ec105b5bbf6c21"`);
await queryRunner.query(`DROP INDEX "public"."IDX_ceab80a6729f8e2e6f5b8a1a3d"`);
await queryRunner.query(`DROP INDEX "public"."IDX_3b7697a96f522d0478972e6d6f"`);
await queryRunner.query(`DROP INDEX "public"."IDX_53a3604b939e2b479eb2cfaac8"`);
await queryRunner.query(`DROP INDEX "public"."IDX_dabbb38a51ab86ee3cab291326"`);
await queryRunner.query(`DROP INDEX "public"."IDX_a9a806d466b314f253a1a611c4"`);
await queryRunner.query(`CREATE TABLE "__chart_day__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "UQ_617a8fe225a6e701d89e02d2c74" UNIQUE ("date"), CONSTRAINT "PK_7ca721c769f31698e0e1331e8e6" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_617a8fe225a6e701d89e02d2c7" ON "__chart_day__federation" ("date") `);
await queryRunner.query(`CREATE TABLE "__chart_day__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_1a527b423ad0858a1af5a056d43" UNIQUE ("date"), CONSTRAINT "PK_1fa4139e1f338272b758d05e090" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_1a527b423ad0858a1af5a056d4" ON "__chart_day__notes" ("date") `);
await queryRunner.query(`CREATE TABLE "__chart_day__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "UQ_cad6e07c20037f31cdba8a350c3" UNIQUE ("date"), CONSTRAINT "PK_d7f7185abb9851f70c4726c54bd" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_cad6e07c20037f31cdba8a350c" ON "__chart_day__users" ("date") `);
await queryRunner.query(`CREATE TABLE "__chart_day__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "UQ_8bfa548c2b31f9e07db113773ee" UNIQUE ("date"), CONSTRAINT "PK_cac499d6f471042dfed1e7e0132" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8bfa548c2b31f9e07db113773e" ON "__chart_day__network" ("date") `);
await queryRunner.query(`CREATE TABLE "__chart_day__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_d5954f3df5e5e3bdfc3c03f3906" UNIQUE ("date"), CONSTRAINT "PK_b1790489b14f005ae8f404f5795" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d5954f3df5e5e3bdfc3c03f390" ON "__chart_day__active_users" ("date") `);
await queryRunner.query(`CREATE TABLE "__chart_day__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "UQ_fea7c0278325a1a2492f2d6acbf" UNIQUE ("date", "group"), CONSTRAINT "PK_479a8ff9d959274981087043023" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_fea7c0278325a1a2492f2d6acb" ON "__chart_day__instance" ("date", "group") `);
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_c5545d4b31cdc684034e33b81c3" UNIQUE ("date", "group"), CONSTRAINT "PK_58bab6b6d3ad9310cbc7460fd28" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c5545d4b31cdc684034e33b81c" ON "__chart_day__per_user_notes" ("date", "group") `);
await queryRunner.query(`CREATE TABLE "__chart_day__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "UQ_0b60ebb3aa0065f10b0616c1171" UNIQUE ("date"), CONSTRAINT "PK_e7ec0de057c77c40fc8d8b62151" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0b60ebb3aa0065f10b0616c117" ON "__chart_day__drive" ("date") `);
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "UQ_d54b653660d808b118e36c184c0" UNIQUE ("date", "group"), CONSTRAINT "PK_8af24e2d51ff781a354fe595eda" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d54b653660d808b118e36c184c" ON "__chart_day__per_user_reaction" ("date", "group") `);
await queryRunner.query(`CREATE TABLE "__chart_day__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_8f589cf056ff51f09d6096f6450" UNIQUE ("date", "group"), CONSTRAINT "PK_13d5a3b089344e5557f8e0980b4" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8f589cf056ff51f09d6096f645" ON "__chart_day__hashtag" ("date", "group") `);
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "UQ_e4849a3231f38281280ea4c0eee" UNIQUE ("date", "group"), CONSTRAINT "PK_68ce6b67da57166da66fc8fb27e" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e4849a3231f38281280ea4c0ee" ON "__chart_day__per_user_following" ("date", "group") `);
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "UQ_62aa5047b5aec92524f24c701d7" UNIQUE ("date", "group"), CONSTRAINT "PK_1ae135254c137011645da7f4045" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_62aa5047b5aec92524f24c701d" ON "__chart_day__per_user_drive" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "group"`);
await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "group"`);
await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "group"`);
await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "group"`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "group"`);
await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "group"`);
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97" UNIQUE ("date")`);
await queryRunner.query(`ALTER TABLE "__chart__notes" ADD CONSTRAINT "UQ_42eb716a37d381cdf566192b2be" UNIQUE ("date")`);
await queryRunner.query(`ALTER TABLE "__chart__users" ADD CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265" UNIQUE ("date")`);
await queryRunner.query(`ALTER TABLE "__chart__network" ADD CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6" UNIQUE ("date")`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca" UNIQUE ("date")`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" DROP DEFAULT`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" DROP DEFAULT`);
await queryRunner.query(`DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`);
await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "group" SET NOT NULL`);
await queryRunner.query(`DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "__chart__drive" ADD CONSTRAINT "UQ_13565815f618a1ff53886c5b28a" UNIQUE ("date")`);
await queryRunner.query(`DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" SET NOT NULL`);
await queryRunner.query(`DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" DROP DEFAULT`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" DROP DEFAULT`);
await queryRunner.query(`DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" SET NOT NULL`);
await queryRunner.query(`DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" SET NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__instance" ADD CONSTRAINT "UQ_39ee857ab2f23493037c6b66311" UNIQUE ("date", "group")`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34" UNIQUE ("date", "group")`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD CONSTRAINT "UQ_229a41ad465f9205f1f57032910" UNIQUE ("date", "group")`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8" UNIQUE ("date", "group")`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7" UNIQUE ("date", "group")`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD CONSTRAINT "UQ_30bf67687f483ace115c5ca6429" UNIQUE ("date", "group")`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP CONSTRAINT "UQ_30bf67687f483ace115c5ca6429"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7"`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP CONSTRAINT "UQ_229a41ad465f9205f1f57032910"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34"`);
await queryRunner.query(`ALTER TABLE "__chart__instance" DROP CONSTRAINT "UQ_39ee857ab2f23493037c6b66311"`);
await queryRunner.query(`DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`);
await queryRunner.query(`DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`);
await queryRunner.query(`DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`);
await queryRunner.query(`DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`);
await queryRunner.query(`DROP INDEX "public"."IDX_13565815f618a1ff53886c5b28"`);
await queryRunner.query(`DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`);
await queryRunner.query(`DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`);
await queryRunner.query(`DROP INDEX "public"."IDX_0ad37b7ef50f4ddc84363d7ccc"`);
await queryRunner.query(`DROP INDEX "public"."IDX_a1efd3e0048a5f2793a47360dc"`);
await queryRunner.query(`DROP INDEX "public"."IDX_845254b3eaf708ae8a6cac3026"`);
await queryRunner.query(`DROP INDEX "public"."IDX_42eb716a37d381cdf566192b2b"`);
await queryRunner.query(`DROP INDEX "public"."IDX_36cb699c49580d4e6c2e6159f9"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" DROP NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" DROP NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" SET DEFAULT '{}'`);
await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" DROP NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" DROP NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__drive" DROP CONSTRAINT "UQ_13565815f618a1ff53886c5b28a"`);
await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" DROP NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "group" DROP NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `);
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" SET DEFAULT '{}'`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca"`);
await queryRunner.query(`ALTER TABLE "__chart__network" DROP CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6"`);
await queryRunner.query(`ALTER TABLE "__chart__users" DROP CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265"`);
await queryRunner.query(`ALTER TABLE "__chart__notes" DROP CONSTRAINT "UQ_42eb716a37d381cdf566192b2be"`);
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97"`);
await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "group" character varying(128)`);
await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "group" character varying(128)`);
await queryRunner.query(`ALTER TABLE "__chart__network" ADD "group" character varying(128)`);
await queryRunner.query(`ALTER TABLE "__chart__users" ADD "group" character varying(128)`);
await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "group" character varying(128)`);
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "group" character varying(128)`);
await queryRunner.query(`DROP INDEX "public"."IDX_62aa5047b5aec92524f24c701d"`);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_drive"`);
await queryRunner.query(`DROP INDEX "public"."IDX_e4849a3231f38281280ea4c0ee"`);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_following"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8f589cf056ff51f09d6096f645"`);
await queryRunner.query(`DROP TABLE "__chart_day__hashtag"`);
await queryRunner.query(`DROP INDEX "public"."IDX_d54b653660d808b118e36c184c"`);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_reaction"`);
await queryRunner.query(`DROP INDEX "public"."IDX_0b60ebb3aa0065f10b0616c117"`);
await queryRunner.query(`DROP TABLE "__chart_day__drive"`);
await queryRunner.query(`DROP INDEX "public"."IDX_c5545d4b31cdc684034e33b81c"`);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_notes"`);
await queryRunner.query(`DROP INDEX "public"."IDX_fea7c0278325a1a2492f2d6acb"`);
await queryRunner.query(`DROP TABLE "__chart_day__instance"`);
await queryRunner.query(`DROP INDEX "public"."IDX_d5954f3df5e5e3bdfc3c03f390"`);
await queryRunner.query(`DROP TABLE "__chart_day__active_users"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8bfa548c2b31f9e07db113773e"`);
await queryRunner.query(`DROP TABLE "__chart_day__network"`);
await queryRunner.query(`DROP INDEX "public"."IDX_cad6e07c20037f31cdba8a350c"`);
await queryRunner.query(`DROP TABLE "__chart_day__users"`);
await queryRunner.query(`DROP INDEX "public"."IDX_1a527b423ad0858a1af5a056d4"`);
await queryRunner.query(`DROP TABLE "__chart_day__notes"`);
await queryRunner.query(`DROP INDEX "public"."IDX_617a8fe225a6e701d89e02d2c7"`);
await queryRunner.query(`DROP TABLE "__chart_day__federation"`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a9a806d466b314f253a1a611c4" ON "__chart__per_user_drive" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dabbb38a51ab86ee3cab291326" ON "__chart__per_user_following" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_53a3604b939e2b479eb2cfaac8" ON "__chart__hashtag" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3b7697a96f522d0478972e6d6f" ON "__chart__per_user_reaction" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d" ON "__chart__drive" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_583a157ed0cf0ed1b5ec2a833f" ON "__chart__per_user_notes" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8111b817b9818c04d7eb8475b1" ON "__chart__instance" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_60c5c6e7e538c09aa274ecd1cf" ON "__chart__active_users" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2082327b2699ce924fa654afc5" ON "__chart__network" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_66feba81e1795d176d06c0b1e6" ON "__chart__users" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e60c358aaced5aab8900a4af31" ON "__chart__notes" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_eddfed8fb40305a04c6f941050" ON "__chart__federation" ("date") WHERE ("group" IS NULL)`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `);
}
}

View file

@ -170,7 +170,7 @@
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"style-loader": "3.3.1", "style-loader": "3.3.1",
"summaly": "2.4.1", "summaly": "2.5.0",
"syslog-pro": "1.0.0", "syslog-pro": "1.0.0",
"systeminformation": "5.9.9", "systeminformation": "5.9.9",
"throttle-debounce": "3.0.1", "throttle-debounce": "3.0.1",

View file

@ -3,10 +3,10 @@ const types = require('pg').types;
types.setTypeParser(20, Number); types.setTypeParser(20, Number);
import { createConnection, Logger, getConnection } from 'typeorm'; import { createConnection, Logger, getConnection } from 'typeorm';
import config from '@/config/index';
import { entities as charts } from '@/services/chart/entities';
import { dbLogger } from './logger';
import * as highlight from 'cli-highlight'; import * as highlight from 'cli-highlight';
import config from '@/config/index';
import { dbLogger } from './logger';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { DriveFile } from '@/models/entities/drive-file'; import { DriveFile } from '@/models/entities/drive-file';
@ -74,6 +74,8 @@ import { Ad } from '@/models/entities/ad';
import { PasswordResetRequest } from '@/models/entities/password-reset-request'; import { PasswordResetRequest } from '@/models/entities/password-reset-request';
import { UserPending } from '@/models/entities/user-pending'; import { UserPending } from '@/models/entities/user-pending';
import { entities as charts } from '@/services/chart/entities';
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
class MyCustomLogger implements Logger { class MyCustomLogger implements Logger {
@ -175,7 +177,7 @@ export const entities = [
Ad, Ad,
PasswordResetRequest, PasswordResetRequest,
UserPending, UserPending,
...charts as any, ...charts,
]; ];
export function initDb(justBorrow = false, sync = false, forceRecreate = false) { export function initDb(justBorrow = false, sync = false, forceRecreate = false) {

View file

@ -39,16 +39,20 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
const metaStream = fs.createWriteStream(metaPath, { flags: 'a' }); const metaStream = fs.createWriteStream(metaPath, { flags: 'a' });
await new Promise<void>((res, rej) => { const writeMeta = (text: string): Promise<void> => {
metaStream.write('[', err => { return new Promise<void>((res, rej) => {
if (err) { metaStream.write(text, err => {
logger.error(err); if (err) {
rej(err); logger.error(err);
} else { rej(err);
res(); } else {
} res();
}
});
}); });
}); };
await writeMeta(`{"metaVersion":1,"emojis":[`);
const customEmojis = await Emojis.find({ const customEmojis = await Emojis.find({
where: { where: {
@ -72,34 +76,17 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
logger.error(e); logger.error(e);
} }
await new Promise<void>((res, rej) => { const content = JSON.stringify({
const content = JSON.stringify({ id: exportId,
id: exportId, downloaded: downloaded,
downloaded: downloaded, emoji: emoji,
emoji: emoji,
});
const isFirst = customEmojis.indexOf(emoji) === 0;
metaStream.write(isFirst ? content : ',\n' + content, err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
}); });
const isFirst = customEmojis.indexOf(emoji) === 0;
await writeMeta(isFirst ? content : ',\n' + content);
} }
await new Promise<void>((res, rej) => { await writeMeta(']}');
metaStream.write(']', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
metaStream.end(); metaStream.end();

View file

@ -34,16 +34,20 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
const stream = fs.createWriteStream(path, { flags: 'a' }); const stream = fs.createWriteStream(path, { flags: 'a' });
await new Promise<void>((res, rej) => { const write = (text: string): Promise<void> => {
stream.write('[', err => { return new Promise<void>((res, rej) => {
if (err) { stream.write(text, err => {
logger.error(err); if (err) {
rej(err); logger.error(err);
} else { rej(err);
res(); } else {
} res();
}
});
}); });
}); };
await write('[');
let exportedNotesCount = 0; let exportedNotesCount = 0;
let cursor: Note['id'] | null = null; let cursor: Note['id'] | null = null;
@ -73,17 +77,8 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
poll = await Polls.findOneOrFail({ noteId: note.id }); poll = await Polls.findOneOrFail({ noteId: note.id });
} }
const content = JSON.stringify(serialize(note, poll)); const content = JSON.stringify(serialize(note, poll));
await new Promise<void>((res, rej) => { const isFirst = exportedNotesCount === 0;
const isFirst = exportedNotesCount === 0; await write(isFirst ? content : ',\n' + content);
stream.write(isFirst ? content : ',\n' + content, err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
exportedNotesCount++; exportedNotesCount++;
} }
@ -94,16 +89,7 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
job.progress(exportedNotesCount / total); job.progress(exportedNotesCount / total);
} }
await new Promise<void>((res, rej) => { await write(']');
stream.write(']', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
stream.end(); stream.end();
logger.succ(`Exported to: ${path}`); logger.succ(`Exported to: ${path}`);

View file

@ -1,12 +1,16 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Users } from '@/models/index'; import { Users } from '@/models/index';
import { name, schema } from '../schemas/active-users'; import { name, schema } from './entities/active-users';
type ActiveUsersLog = SchemaType<typeof schema>; type ActiveUsersLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class ActiveUsersChart extends Chart<ActiveUsersLog> { export default class ActiveUsersChart extends Chart<ActiveUsersLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -35,7 +39,7 @@ export default class ActiveUsersChart extends Chart<ActiveUsersLog> {
} }
@autobind @autobind
public async update(user: { id: User['id'], host: User['host'] }) { public async update(user: { id: User['id'], host: User['host'] }): Promise<void> {
const update: Obj = { const update: Obj = {
users: [user.id], users: [user.id],
}; };

View file

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { DriveFiles } from '@/models/index'; import { DriveFiles } from '@/models/index';
import { Not, IsNull } from 'typeorm'; import { Not, IsNull } from 'typeorm';
import { DriveFile } from '@/models/entities/drive-file'; import { DriveFile } from '@/models/entities/drive-file';
import { name, schema } from '../schemas/drive'; import { name, schema } from './entities/drive';
type DriveLog = SchemaType<typeof schema>; type DriveLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class DriveChart extends Chart<DriveLog> { export default class DriveChart extends Chart<DriveLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -71,7 +75,7 @@ export default class DriveChart extends Chart<DriveLog> {
} }
@autobind @autobind
public async update(file: DriveFile, isAdditional: boolean) { public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.totalCount = isAdditional ? 1 : -1; update.totalCount = isAdditional ? 1 : -1;

View file

@ -1,4 +1,8 @@
export const logSchema = { import Chart from '../../core';
export const name = 'activeUsers';
const logSchema = {
/** /**
* *
*/ */
@ -12,9 +16,6 @@ export const logSchema = {
}, },
}; };
/**
*
*/
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -32,4 +33,4 @@ export const schema = {
}, },
}; };
export const name = 'activeUsers'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'drive';
const logSchema = { const logSchema = {
/** /**
* *
@ -65,4 +69,4 @@ export const schema = {
}, },
}; };
export const name = 'drive'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,6 +1,7 @@
/** import Chart from '../../core';
*
*/ export const name = 'federation';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -26,4 +27,4 @@ export const schema = {
}, },
}; };
export const name = 'federation'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,4 +1,8 @@
export const logSchema = { import Chart from '../../core';
export const name = 'hashtag';
const logSchema = {
/** /**
* 稿 * 稿
*/ */
@ -12,9 +16,6 @@ export const logSchema = {
}, },
}; };
/**
*
*/
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -32,4 +33,4 @@ export const schema = {
}, },
}; };
export const name = 'hashtag'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,6 +1,7 @@
/** import Chart from '../../core';
*
*/ export const name = 'instance';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -154,4 +155,4 @@ export const schema = {
}, },
}; };
export const name = 'instance'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,6 +1,7 @@
/** import Chart from '../../core';
*
*/ export const name = 'network';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -28,4 +29,4 @@ export const schema = {
}, },
}; };
export const name = 'network'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'notes';
const logSchema = { const logSchema = {
total: { total: {
type: 'number' as const, type: 'number' as const,
@ -53,4 +57,4 @@ export const schema = {
}, },
}; };
export const name = 'notes'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'perUserDrive';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -52,4 +56,4 @@ export const schema = {
}, },
}; };
export const name = 'perUserDrive'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,4 +1,8 @@
export const logSchema = { import Chart from '../../core';
export const name = 'perUserFollowing';
const logSchema = {
/** /**
* *
*/ */
@ -83,4 +87,4 @@ export const schema = {
}, },
}; };
export const name = 'perUserFollowing'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'perUserNotes';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -40,4 +44,4 @@ export const schema = {
}, },
}; };
export const name = 'perUserNotes'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,6 +1,10 @@
export const logSchema = { import Chart from '../../core';
export const name = 'perUserReaction';
const logSchema = {
/** /**
* *
*/ */
count: { count: {
type: 'number' as const, type: 'number' as const,
@ -8,9 +12,6 @@ export const logSchema = {
}, },
}; };
/**
*
*/
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -28,4 +29,4 @@ export const schema = {
}, },
}; };
export const name = 'perUserReaction'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'testGrouped';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -25,4 +29,4 @@ export const schema = {
}, },
}; };
export const name = 'testGrouped'; export const entity = Chart.schemaToEntity(name, schema, true);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'testUnique';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -13,4 +17,4 @@ export const schema = {
}, },
}; };
export const name = 'testUnique'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'test';
export const schema = { export const schema = {
type: 'object' as const, type: 'object' as const,
optional: false as const, nullable: false as const, optional: false as const, nullable: false as const,
@ -25,4 +29,4 @@ export const schema = {
}, },
}; };
export const name = 'test'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,3 +1,7 @@
import Chart from '../../core';
export const name = 'users';
const logSchema = { const logSchema = {
/** /**
* *
@ -41,4 +45,4 @@ export const schema = {
}, },
}; };
export const name = 'users'; export const entity = Chart.schemaToEntity(name, schema);

View file

@ -1,11 +1,15 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Instances } from '@/models/index'; import { Instances } from '@/models/index';
import { name, schema } from '../schemas/federation'; import { name, schema } from './entities/federation';
type FederationLog = SchemaType<typeof schema>; type FederationLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class FederationChart extends Chart<FederationLog> { export default class FederationChart extends Chart<FederationLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -45,7 +49,7 @@ export default class FederationChart extends Chart<FederationLog> {
} }
@autobind @autobind
public async update(isAdditional: boolean) { public async update(isAdditional: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.total = isAdditional ? 1 : -1; update.total = isAdditional ? 1 : -1;

View file

@ -1,12 +1,16 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Users } from '@/models/index'; import { Users } from '@/models/index';
import { name, schema } from '../schemas/hashtag'; import { name, schema } from './entities/hashtag';
type HashtagLog = SchemaType<typeof schema>; type HashtagLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class HashtagChart extends Chart<HashtagLog> { export default class HashtagChart extends Chart<HashtagLog> {
constructor() { constructor() {
super(name, schema, true); super(name, schema, true);
@ -35,7 +39,7 @@ export default class HashtagChart extends Chart<HashtagLog> {
} }
@autobind @autobind
public async update(hashtag: string, user: { id: User['id'], host: User['host'] }) { public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise<void> {
const update: Obj = { const update: Obj = {
users: [user.id], users: [user.id],
}; };

View file

@ -1,17 +1,21 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { DriveFiles, Followings, Users, Notes } from '@/models/index'; import { DriveFiles, Followings, Users, Notes } from '@/models/index';
import { DriveFile } from '@/models/entities/drive-file'; import { DriveFile } from '@/models/entities/drive-file';
import { name, schema } from '../schemas/instance';
import { Note } from '@/models/entities/note'; import { Note } from '@/models/entities/note';
import { toPuny } from '@/misc/convert-host'; import { toPuny } from '@/misc/convert-host';
import { name, schema } from './entities/instance';
type InstanceLog = SchemaType<typeof schema>; type InstanceLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class InstanceChart extends Chart<InstanceLog> { export default class InstanceChart extends Chart<InstanceLog> {
constructor() { constructor() {
super(name, schema); super(name, schema, true);
} }
@autobind @autobind
@ -119,7 +123,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async requestReceived(host: string) { public async requestReceived(host: string): Promise<void> {
await this.inc({ await this.inc({
requests: { requests: {
received: 1, received: 1,
@ -128,7 +132,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async requestSent(host: string, isSucceeded: boolean) { public async requestSent(host: string, isSucceeded: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
if (isSucceeded) { if (isSucceeded) {
@ -143,7 +147,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async newUser(host: string) { public async newUser(host: string): Promise<void> {
await this.inc({ await this.inc({
users: { users: {
total: 1, total: 1,
@ -153,8 +157,8 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async updateNote(host: string, note: Note, isAdditional: boolean) { public async updateNote(host: string, note: Note, isAdditional: boolean): Promise<void> {
const diffs = {} as any; const diffs = {} as Record<string, unknown>;
if (note.replyId != null) { if (note.replyId != null) {
diffs.reply = isAdditional ? 1 : -1; diffs.reply = isAdditional ? 1 : -1;
@ -175,7 +179,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async updateFollowing(host: string, isAdditional: boolean) { public async updateFollowing(host: string, isAdditional: boolean): Promise<void> {
await this.inc({ await this.inc({
following: { following: {
total: isAdditional ? 1 : -1, total: isAdditional ? 1 : -1,
@ -186,7 +190,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async updateFollowers(host: string, isAdditional: boolean) { public async updateFollowers(host: string, isAdditional: boolean): Promise<void> {
await this.inc({ await this.inc({
followers: { followers: {
total: isAdditional ? 1 : -1, total: isAdditional ? 1 : -1,
@ -197,7 +201,7 @@ export default class InstanceChart extends Chart<InstanceLog> {
} }
@autobind @autobind
public async updateDrive(file: DriveFile, isAdditional: boolean) { public async updateDrive(file: DriveFile, isAdditional: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.totalFiles = isAdditional ? 1 : -1; update.totalFiles = isAdditional ? 1 : -1;

View file

@ -1,10 +1,14 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { DeepPartial } from '../../core'; import Chart, { DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { name, schema } from '../schemas/network'; import { name, schema } from './entities/network';
type NetworkLog = SchemaType<typeof schema>; type NetworkLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class NetworkChart extends Chart<NetworkLog> { export default class NetworkChart extends Chart<NetworkLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -32,7 +36,7 @@ export default class NetworkChart extends Chart<NetworkLog> {
} }
@autobind @autobind
public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number) { public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number): Promise<void> {
const inc: DeepPartial<NetworkLog> = { const inc: DeepPartial<NetworkLog> = {
incomingRequests: incomingRequests, incomingRequests: incomingRequests,
totalTime: time, totalTime: time,

View file

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Notes } from '@/models/index'; import { Notes } from '@/models/index';
import { Not, IsNull } from 'typeorm'; import { Not, IsNull } from 'typeorm';
import { Note } from '@/models/entities/note'; import { Note } from '@/models/entities/note';
import { name, schema } from '../schemas/notes'; import { name, schema } from './entities/notes';
type NotesLog = SchemaType<typeof schema>; type NotesLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class NotesChart extends Chart<NotesLog> { export default class NotesChart extends Chart<NotesLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -69,7 +73,7 @@ export default class NotesChart extends Chart<NotesLog> {
} }
@autobind @autobind
public async update(note: Note, isAdditional: boolean) { public async update(note: Note, isAdditional: boolean): Promise<void> {
const update: Obj = { const update: Obj = {
diffs: {}, diffs: {},
}; };

View file

@ -1,12 +1,16 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { DriveFiles } from '@/models/index'; import { DriveFiles } from '@/models/index';
import { DriveFile } from '@/models/entities/drive-file'; import { DriveFile } from '@/models/entities/drive-file';
import { name, schema } from '../schemas/per-user-drive'; import { name, schema } from './entities/per-user-drive';
type PerUserDriveLog = SchemaType<typeof schema>; type PerUserDriveLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserDriveChart extends Chart<PerUserDriveLog> { export default class PerUserDriveChart extends Chart<PerUserDriveLog> {
constructor() { constructor() {
super(name, schema, true); super(name, schema, true);
@ -46,7 +50,7 @@ export default class PerUserDriveChart extends Chart<PerUserDriveLog> {
} }
@autobind @autobind
public async update(file: DriveFile, isAdditional: boolean) { public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.totalCount = isAdditional ? 1 : -1; update.totalCount = isAdditional ? 1 : -1;

View file

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Followings, Users } from '@/models/index'; import { Followings, Users } from '@/models/index';
import { Not, IsNull } from 'typeorm'; import { Not, IsNull } from 'typeorm';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { name, schema } from '../schemas/per-user-following'; import { name, schema } from './entities/per-user-following';
type PerUserFollowingLog = SchemaType<typeof schema>; type PerUserFollowingLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> { export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
constructor() { constructor() {
super(name, schema, true); super(name, schema, true);
@ -100,7 +104,7 @@ export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
} }
@autobind @autobind
public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean) { public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.total = isFollow ? 1 : -1; update.total = isFollow ? 1 : -1;

View file

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Notes } from '@/models/index'; import { Notes } from '@/models/index';
import { Note } from '@/models/entities/note'; import { Note } from '@/models/entities/note';
import { name, schema } from '../schemas/per-user-notes'; import { name, schema } from './entities/per-user-notes';
type PerUserNotesLog = SchemaType<typeof schema>; type PerUserNotesLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserNotesChart extends Chart<PerUserNotesLog> { export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
constructor() { constructor() {
super(name, schema, true); super(name, schema, true);
@ -46,7 +50,7 @@ export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
} }
@autobind @autobind
public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean) { public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise<void> {
const update: Obj = { const update: Obj = {
diffs: {}, diffs: {},
}; };

View file

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { DeepPartial } from '../../core'; import Chart, { DeepPartial } from '../core';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { Note } from '@/models/entities/note'; import { Note } from '@/models/entities/note';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Users } from '@/models/index'; import { Users } from '@/models/index';
import { name, schema } from '../schemas/per-user-reactions'; import { name, schema } from './entities/per-user-reactions';
type PerUserReactionsLog = SchemaType<typeof schema>; type PerUserReactionsLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> { export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
constructor() { constructor() {
super(name, schema, true); super(name, schema, true);
@ -36,7 +40,7 @@ export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
} }
@autobind @autobind
public async update(user: { id: User['id'], host: User['host'] }, note: Note) { public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise<void> {
this.inc({ this.inc({
[Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 }, [Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 },
}, note.userId); }, note.userId);

View file

@ -1,10 +1,14 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { name, schema } from '../schemas/test-grouped'; import { name, schema } from './entities/test-grouped';
type TestGroupedLog = SchemaType<typeof schema>; type TestGroupedLog = SchemaType<typeof schema>;
/**
* For testing
*/
// eslint-disable-next-line import/no-default-export
export default class TestGroupedChart extends Chart<TestGroupedLog> { export default class TestGroupedChart extends Chart<TestGroupedLog> {
private total = {} as Record<string, number>; private total = {} as Record<string, number>;
@ -42,7 +46,7 @@ export default class TestGroupedChart extends Chart<TestGroupedLog> {
} }
@autobind @autobind
public async increment(group: string) { public async increment(group: string): Promise<void> {
if (this.total[group] == null) this.total[group] = 0; if (this.total[group] == null) this.total[group] = 0;
const update: Obj = {}; const update: Obj = {};

View file

@ -1,10 +1,14 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { DeepPartial } from '../../core'; import Chart, { DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { name, schema } from '../schemas/test-unique'; import { name, schema } from './entities/test-unique';
type TestUniqueLog = SchemaType<typeof schema>; type TestUniqueLog = SchemaType<typeof schema>;
/**
* For testing
*/
// eslint-disable-next-line import/no-default-export
export default class TestUniqueChart extends Chart<TestUniqueLog> { export default class TestUniqueChart extends Chart<TestUniqueLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -28,7 +32,7 @@ export default class TestUniqueChart extends Chart<TestUniqueLog> {
} }
@autobind @autobind
public async uniqueIncrement(key: string) { public async uniqueIncrement(key: string): Promise<void> {
await this.inc({ await this.inc({
foo: [key], foo: [key],
}); });

View file

@ -1,10 +1,14 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { name, schema } from '../schemas/test'; import { name, schema } from './entities/test';
type TestLog = SchemaType<typeof schema>; type TestLog = SchemaType<typeof schema>;
/**
* For testing
*/
// eslint-disable-next-line import/no-default-export
export default class TestChart extends Chart<TestLog> { export default class TestChart extends Chart<TestLog> {
public total = 0; // publicにするのはテストのため public total = 0; // publicにするのはテストのため
@ -42,7 +46,7 @@ export default class TestChart extends Chart<TestLog> {
} }
@autobind @autobind
public async increment() { public async increment(): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.total = 1; update.total = 1;
@ -55,7 +59,7 @@ export default class TestChart extends Chart<TestLog> {
} }
@autobind @autobind
public async decrement() { public async decrement(): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.total = -1; update.total = -1;

View file

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core'; import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { Users } from '@/models/index'; import { Users } from '@/models/index';
import { Not, IsNull } from 'typeorm'; import { Not, IsNull } from 'typeorm';
import { User } from '@/models/entities/user'; import { User } from '@/models/entities/user';
import { name, schema } from '../schemas/users'; import { name, schema } from './entities/users';
type UsersLog = SchemaType<typeof schema>; type UsersLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class UsersChart extends Chart<UsersLog> { export default class UsersChart extends Chart<UsersLog> {
constructor() { constructor() {
super(name, schema); super(name, schema);
@ -59,7 +63,7 @@ export default class UsersChart extends Chart<UsersLog> {
} }
@autobind @autobind
public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean) { public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> {
const update: Obj = {}; const update: Obj = {};
update.total = isAdditional ? 1 : -1; update.total = isAdditional ? 1 : -1;

View file

@ -30,7 +30,7 @@ type Log = {
/** /**
* *
*/ */
group: string | null; group?: string | null;
/** /**
* Unixタイムスタンプ() * Unixタイムスタンプ()
@ -38,7 +38,7 @@ type Log = {
date: number; date: number;
}; };
const camelToSnake = (str: string) => { const camelToSnake = (str: string): string => {
return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase()); return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase());
}; };
@ -47,6 +47,7 @@ const removeDuplicates = (array: any[]) => Array.from(new Set(array));
/** /**
* *
*/ */
// eslint-disable-next-line import/no-default-export
export default abstract class Chart<T extends Record<string, any>> { export default abstract class Chart<T extends Record<string, any>> {
private static readonly columnPrefix = '___'; private static readonly columnPrefix = '___';
private static readonly columnDot = '_'; private static readonly columnDot = '_';
@ -57,7 +58,8 @@ export default abstract class Chart<T extends Record<string, any>> {
group: string | null; group: string | null;
}[] = []; }[] = [];
public schema: SimpleSchema; public schema: SimpleSchema;
protected repository: Repository<Log>; protected repositoryForHour: Repository<Log>;
protected repositoryForDay: Repository<Log>;
protected abstract genNewLog(latest: T): DeepPartial<T>; protected abstract genNewLog(latest: T): DeepPartial<T>;
@ -181,9 +183,15 @@ export default abstract class Chart<T extends Record<string, any>> {
} }
@autobind @autobind
public static schemaToEntity(name: string, schema: SimpleSchema): EntitySchema { public static schemaToEntity(name: string, schema: SimpleSchema, grouped = false): {
return new EntitySchema({ hour: EntitySchema,
name: `__chart__${camelToSnake(name)}`, day: EntitySchema,
} {
const createEntity = (span: 'hour' | 'day'): EntitySchema => new EntitySchema({
name:
span === 'hour' ? `__chart__${camelToSnake(name)}` :
span === 'day' ? `__chart_day__${camelToSnake(name)}` :
new Error('not happen') as never,
columns: { columns: {
id: { id: {
type: 'integer', type: 'integer',
@ -193,37 +201,45 @@ export default abstract class Chart<T extends Record<string, any>> {
date: { date: {
type: 'integer', type: 'integer',
}, },
group: { ...(grouped ? {
type: 'varchar', group: {
length: 128, type: 'varchar',
nullable: true, length: 128,
}, },
} : {}),
...Chart.convertSchemaToFlatColumnDefinitions(schema), ...Chart.convertSchemaToFlatColumnDefinitions(schema),
}, },
indices: [{ indices: [{
columns: ['date', 'group'], columns: grouped ? ['date', 'group'] : ['date'],
unique: true, unique: true,
}, { // groupにnullが含まれると↑のuniqueは機能しないので↓の部分インデックスでカバー
columns: ['date'],
unique: true,
where: '"group" IS NULL',
}], }],
uniques: [{
columns: grouped ? ['date', 'group'] : ['date'],
}],
relations: {
/* TODO
group: {
target: () => Foo,
type: 'many-to-one',
onDelete: 'CASCADE',
},
*/
},
}); });
return {
hour: createEntity('hour'),
day: createEntity('day'),
};
} }
constructor(name: string, schema: SimpleSchema, grouped = false) { constructor(name: string, schema: SimpleSchema, grouped = false) {
this.name = name; this.name = name;
this.schema = schema; this.schema = schema;
const entity = Chart.schemaToEntity(name, schema);
const keys = ['date']; const { hour, day } = Chart.schemaToEntity(name, schema, grouped);
if (grouped) keys.push('group'); this.repositoryForHour = getRepository<Log>(hour);
this.repositoryForDay = getRepository<Log>(day);
entity.options.uniques = [{
columns: keys,
}];
this.repository = getRepository<Log>(entity);
} }
@autobind @autobind
@ -247,24 +263,40 @@ export default abstract class Chart<T extends Record<string, any>> {
} }
@autobind @autobind
private getLatestLog(group: string | null = null): Promise<Log | null> { private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise<Log | null> {
return this.repository.findOne({ const repository =
span === 'hour' ? this.repositoryForHour :
span === 'day' ? this.repositoryForDay :
new Error('not happen') as never;
return repository.findOne(group ? {
group: group, group: group,
}, { } : {}, {
order: { order: {
date: -1, date: -1,
}, },
}).then(x => x || null); }).then(x => x || null);
} }
/**
* (=Hour or Day)
*/
@autobind @autobind
private async getCurrentLog(group: string | null = null): Promise<Log> { private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise<Log> {
const [y, m, d, h] = Chart.getCurrentDate(); const [y, m, d, h] = Chart.getCurrentDate();
const current = dateUTC([y, m, d, h]); const current = dateUTC(
span === 'hour' ? [y, m, d, h] :
span === 'day' ? [y, m, d] :
new Error('not happen') as never);
// 現在(=今のHour)のログ const repository =
const currentLog = await this.repository.findOne({ span === 'hour' ? this.repositoryForHour :
span === 'day' ? this.repositoryForDay :
new Error('not happen') as never;
// 現在(=今のHour or Day)のログ
const currentLog = await repository.findOne({
date: Chart.dateToTimestamp(current), date: Chart.dateToTimestamp(current),
...(group ? { group: group } : {}), ...(group ? { group: group } : {}),
}); });
@ -283,7 +315,7 @@ export default abstract class Chart<T extends Record<string, any>> {
// * 昨日何もチャートを更新するような出来事がなかった場合は、 // * 昨日何もチャートを更新するような出来事がなかった場合は、
// * ログがそもそも作られずドキュメントが存在しないということがあり得るため、 // * ログがそもそも作られずドキュメントが存在しないということがあり得るため、
// * 「昨日の」と決め打ちせずに「もっとも最近の」とします // * 「昨日の」と決め打ちせずに「もっとも最近の」とします
const latest = await this.getLatestLog(group); const latest = await this.getLatestLog(group, span);
if (latest != null) { if (latest != null) {
const obj = Chart.convertFlattenColumnsToObject(latest) as T; const obj = Chart.convertFlattenColumnsToObject(latest) as T;
@ -297,16 +329,16 @@ export default abstract class Chart<T extends Record<string, any>> {
// 初期ログデータを作成 // 初期ログデータを作成
data = this.getNewLog(null); data = this.getNewLog(null);
logger.info(`${this.name + (group ? `:${group}` : '')}: Initial commit created`); logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): Initial commit created`);
} }
const date = Chart.dateToTimestamp(current); const date = Chart.dateToTimestamp(current);
const lockKey = `${this.name}:${date}:${group}`; const lockKey = group ? `${this.name}:${date}:${span}:${group}` : `${this.name}:${date}:${span}`;
const unlock = await getChartInsertLock(lockKey); const unlock = await getChartInsertLock(lockKey);
try { try {
// ロック内でもう1回チェックする // ロック内でもう1回チェックする
const currentLog = await this.repository.findOne({ const currentLog = await repository.findOne({
date: date, date: date,
...(group ? { group: group } : {}), ...(group ? { group: group } : {}),
}); });
@ -315,13 +347,13 @@ export default abstract class Chart<T extends Record<string, any>> {
if (currentLog != null) return currentLog; if (currentLog != null) return currentLog;
// 新規ログ挿入 // 新規ログ挿入
log = await this.repository.insert({ log = await repository.insert({
group: group,
date: date, date: date,
...(group ? { group: group } : {}),
...Chart.convertObjectToFlattenColumns(data), ...Chart.convertObjectToFlattenColumns(data),
}).then(x => this.repository.findOneOrFail(x.identifiers[0])); }).then(x => repository.findOneOrFail(x.identifiers[0]));
logger.info(`${this.name + (group ? `:${group}` : '')}: New commit created`); logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`);
return log; return log;
} finally { } finally {
@ -349,10 +381,10 @@ export default abstract class Chart<T extends Record<string, any>> {
// そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。 // そのログは本来は 01:00~ のログとしてDBに保存されて欲しいのに、02:00~ のログ扱いになってしまう。
// これを回避するための実装は複雑になりそうなため、一旦保留。 // これを回避するための実装は複雑になりそうなため、一旦保留。
const update = async (log: Log) => { const update = async (logHour: Log, logDay: Log): Promise<void> => {
const finalDiffs = {} as Record<string, number | unknown[]>; const finalDiffs = {} as Record<string, number | unknown[]>;
for (const diff of this.buffer.filter(q => q.group === log.group).map(q => q.diff)) { for (const diff of this.buffer.filter(q => q.group == null || (q.group === logHour.group)).map(q => q.diff)) {
const columns = Chart.convertObjectToFlattenColumns(diff); const columns = Chart.convertObjectToFlattenColumns(diff);
for (const [k, v] of Object.entries(columns)) { for (const [k, v] of Object.entries(columns)) {
@ -371,36 +403,60 @@ export default abstract class Chart<T extends Record<string, any>> {
const query = Chart.convertQuery(finalDiffs); const query = Chart.convertQuery(finalDiffs);
// ログ更新 // ログ更新
await this.repository.createQueryBuilder() await Promise.all([
.update() this.repositoryForHour.createQueryBuilder()
.set(query) .update()
.where('id = :id', { id: log.id }) .set(query)
.execute(); .where('id = :id', { id: logHour.id })
.execute(),
this.repositoryForDay.createQueryBuilder()
.update()
.set(query)
.where('id = :id', { id: logDay.id })
.execute(),
]);
logger.info(`${this.name + (log.group ? `:${log.group}` : '')}: Updated`); logger.info(`${this.name + (logHour.group ? `:${logHour.group}` : '')}: Updated`);
// TODO: この一連の処理が始まった後に新たにbufferに入ったものは消さないようにする // TODO: この一連の処理が始まった後に新たにbufferに入ったものは消さないようにする
this.buffer = this.buffer.filter(q => q.group !== log.group); this.buffer = this.buffer.filter(q => q.group != null && (q.group !== logHour.group));
}; };
const groups = removeDuplicates(this.buffer.map(log => log.group)); const groups = removeDuplicates(this.buffer.map(log => log.group));
await Promise.all(groups.map(group => this.getCurrentLog(group).then(log => update(log)))); await Promise.all(
groups.map(group =>
Promise.all([
this.claimCurrentLog(group, 'hour'),
this.claimCurrentLog(group, 'day'),
]).then(([logHour, logDay]) =>
update(logHour, logDay))));
} }
@autobind @autobind
public async resync(group: string | null = null): Promise<any> { public async resync(group: string | null = null): Promise<void> {
const data = await this.fetchActual(group); const data = await this.fetchActual(group);
const update = async (log: Log) => { const update = async (logHour: Log, logDay: Log): Promise<void> => {
await this.repository.createQueryBuilder() await Promise.all([
.update() this.repositoryForHour.createQueryBuilder()
.set(Chart.convertObjectToFlattenColumns(data)) .update()
.where('id = :id', { id: log.id }) .set(Chart.convertObjectToFlattenColumns(data))
.execute(); .where('id = :id', { id: logHour.id })
.execute(),
this.repositoryForDay.createQueryBuilder()
.update()
.set(Chart.convertObjectToFlattenColumns(data))
.where('id = :id', { id: logDay.id })
.execute(),
]);
}; };
return this.getCurrentLog(group).then(log => update(log)); return Promise.all([
this.claimCurrentLog(group, 'hour'),
this.claimCurrentLog(group, 'day'),
]).then(([logHour, logDay]) =>
update(logHour, logDay));
} }
@autobind @autobind
@ -418,13 +474,18 @@ export default abstract class Chart<T extends Record<string, any>> {
const gt = const gt =
span === 'day' ? subtractTime(cursor ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') : span === 'day' ? subtractTime(cursor ? dateUTC([y2, m2, d2, 0]) : dateUTC([y, m, d, 0]), amount - 1, 'day') :
span === 'hour' ? subtractTime(cursor ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') : span === 'hour' ? subtractTime(cursor ? dateUTC([y2, m2, d2, h2]) : dateUTC([y, m, d, h]), amount - 1, 'hour') :
null as never; new Error('not happen') as never;
const repository =
span === 'hour' ? this.repositoryForHour :
span === 'day' ? this.repositoryForDay :
new Error('not happen') as never;
// ログ取得 // ログ取得
let logs = await this.repository.find({ let logs = await repository.find({
where: { where: {
group: group,
date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)), date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)),
...(group ? { group: group } : {}),
}, },
order: { order: {
date: -1, date: -1,
@ -435,9 +496,9 @@ export default abstract class Chart<T extends Record<string, any>> {
if (logs.length === 0) { if (logs.length === 0) {
// もっとも新しいログを持ってくる // もっとも新しいログを持ってくる
// (すくなくともひとつログが無いと隙間埋めできないため) // (すくなくともひとつログが無いと隙間埋めできないため)
const recentLog = await this.repository.findOne({ const recentLog = await repository.findOne(group ? {
group: group, group: group,
}, { } : {}, {
order: { order: {
date: -1, date: -1,
}, },
@ -451,9 +512,9 @@ export default abstract class Chart<T extends Record<string, any>> {
} else if (!isTimeSame(new Date(logs[logs.length - 1].date * 1000), gt)) { } else if (!isTimeSame(new Date(logs[logs.length - 1].date * 1000), gt)) {
// 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する // 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する
// (隙間埋めできないため) // (隙間埋めできないため)
const outdatedLog = await this.repository.findOne({ const outdatedLog = await repository.findOne({
group: group,
date: LessThan(Chart.dateToTimestamp(gt)), date: LessThan(Chart.dateToTimestamp(gt)),
...(group ? { group: group } : {}),
}, { }, {
order: { order: {
date: -1, date: -1,
@ -467,60 +528,26 @@ export default abstract class Chart<T extends Record<string, any>> {
const chart: T[] = []; const chart: T[] = [];
if (span === 'hour') { for (let i = (amount - 1); i >= 0; i--) {
for (let i = (amount - 1); i >= 0; i--) { const current =
const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour'); span === 'hour' ? subtractTime(dateUTC([y, m, d, h]), i, 'hour') :
span === 'day' ? subtractTime(dateUTC([y, m, d]), i, 'day') :
new Error('not happen') as never;
const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current));
if (log) { if (log) {
const data = Chart.convertFlattenColumnsToObject(log); const data = Chart.convertFlattenColumnsToObject(log);
chart.unshift(Chart.countUniqueFields(data) as T); chart.unshift(Chart.countUniqueFields(data) as T);
} else { } else {
// 隙間埋め // 隙間埋め
const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current));
const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null;
chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T); chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T);
}
}
} else if (span === 'day') {
const logsForEachDays: T[][] = [];
let currentDay = -1;
let currentDayIndex = -1;
for (let i = ((amount - 1) * 24) + h; i >= 0; i--) {
const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour');
const _currentDay = Chart.parseDate(current)[2];
if (currentDay != _currentDay) currentDayIndex++;
currentDay = _currentDay;
const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current));
if (log) {
if (logsForEachDays[currentDayIndex]) {
logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log) as T);
} else {
logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log) as T];
}
} else {
// 隙間埋め
const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current));
const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null;
const newLog = this.getNewLog(data);
if (logsForEachDays[currentDayIndex]) {
logsForEachDays[currentDayIndex].unshift(newLog);
} else {
logsForEachDays[currentDayIndex] = [newLog];
}
}
}
for (const logs of logsForEachDays) {
const log = this.aggregate(logs);
chart.unshift(Chart.countUniqueFields(log) as T);
} }
} }
const res: ArrayValue<T> = {} as any; const res = {} as Record<string, unknown>;
/** /**
* [{ foo: 1, bar: 5 }, { foo: 2, bar: 6 }, { foo: 3, bar: 7 }] * [{ foo: 1, bar: 5 }, { foo: 2, bar: 6 }, { foo: 3, bar: 7 }]
@ -528,7 +555,7 @@ export default abstract class Chart<T extends Record<string, any>> {
* { foo: [1, 2, 3], bar: [5, 6, 7] } * { foo: [1, 2, 3], bar: [5, 6, 7] }
* *
*/ */
const compact = (x: Obj, path?: string) => { const compact = (x: Obj, path?: string): void => {
for (const [k, v] of Object.entries(x)) { for (const [k, v] of Object.entries(x)) {
const p = path ? `${path}.${k}` : k; const p = path ? `${path}.${k}` : k;
if (typeof v === 'object' && !Array.isArray(v)) { if (typeof v === 'object' && !Array.isArray(v)) {
@ -542,7 +569,7 @@ export default abstract class Chart<T extends Record<string, any>> {
compact(chart[0]); compact(chart[0]);
return res; return res as ArrayValue<T>;
} }
} }

View file

@ -1,15 +1,27 @@
import { fileURLToPath } from 'url'; import { entity as FederationChart } from './charts/entities/federation';
import { dirname } from 'path'; import { entity as NotesChart } from './charts/entities/notes';
import Chart from './core'; import { entity as UsersChart } from './charts/entities/users';
import { entity as NetworkChart } from './charts/entities/network';
import { entity as ActiveUsersChart } from './charts/entities/active-users';
import { entity as InstanceChart } from './charts/entities/instance';
import { entity as PerUserNotesChart } from './charts/entities/per-user-notes';
import { entity as DriveChart } from './charts/entities/drive';
import { entity as PerUserReactionsChart } from './charts/entities/per-user-reactions';
import { entity as HashtagChart } from './charts/entities/hashtag';
import { entity as PerUserFollowingChart } from './charts/entities/per-user-following';
import { entity as PerUserDriveChart } from './charts/entities/per-user-drive';
//const _filename = fileURLToPath(import.meta.url); export const entities = [
const _filename = __filename; FederationChart.hour, FederationChart.day,
const _dirname = dirname(_filename); NotesChart.hour, NotesChart.day,
UsersChart.hour, UsersChart.day,
export const entities = Object.values(require('require-all')({ NetworkChart.hour, NetworkChart.day,
dirname: _dirname + '/charts/schemas', ActiveUsersChart.hour, ActiveUsersChart.day,
filter: /^.+\.[jt]s$/, InstanceChart.hour, InstanceChart.day,
resolve: (x: any) => { PerUserNotesChart.hour, PerUserNotesChart.day,
return Chart.schemaToEntity(x.name, x.schema); DriveChart.hour, DriveChart.day,
}, PerUserReactionsChart.hour, PerUserReactionsChart.day,
})); HashtagChart.hour, HashtagChart.day,
PerUserFollowingChart.hour, PerUserFollowingChart.day,
PerUserDriveChart.hour, PerUserDriveChart.day,
];

View file

@ -1,17 +1,18 @@
import FederationChart from './charts/classes/federation';
import NotesChart from './charts/classes/notes';
import UsersChart from './charts/classes/users';
import NetworkChart from './charts/classes/network';
import ActiveUsersChart from './charts/classes/active-users';
import InstanceChart from './charts/classes/instance';
import PerUserNotesChart from './charts/classes/per-user-notes';
import DriveChart from './charts/classes/drive';
import PerUserReactionsChart from './charts/classes/per-user-reactions';
import HashtagChart from './charts/classes/hashtag';
import PerUserFollowingChart from './charts/classes/per-user-following';
import PerUserDriveChart from './charts/classes/per-user-drive';
import { beforeShutdown } from '@/misc/before-shutdown'; import { beforeShutdown } from '@/misc/before-shutdown';
import FederationChart from './charts/federation';
import NotesChart from './charts/notes';
import UsersChart from './charts/users';
import NetworkChart from './charts/network';
import ActiveUsersChart from './charts/active-users';
import InstanceChart from './charts/instance';
import PerUserNotesChart from './charts/per-user-notes';
import DriveChart from './charts/drive';
import PerUserReactionsChart from './charts/per-user-reactions';
import HashtagChart from './charts/hashtag';
import PerUserFollowingChart from './charts/per-user-following';
import PerUserDriveChart from './charts/per-user-drive';
export const federationChart = new FederationChart(); export const federationChart = new FederationChart();
export const notesChart = new NotesChart(); export const notesChart = new NotesChart();
export const usersChart = new UsersChart(); export const usersChart = new UsersChart();

View file

@ -3,13 +3,12 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import * as lolex from '@sinonjs/fake-timers'; import * as lolex from '@sinonjs/fake-timers';
import { async, initTestDb } from './utils'; import { async, initTestDb } from './utils';
import TestChart from '../src/services/chart/charts/classes/test'; import TestChart from '../src/services/chart/charts/test';
import TestGroupedChart from '../src/services/chart/charts/classes/test-grouped'; import TestGroupedChart from '../src/services/chart/charts/test-grouped';
import TestUniqueChart from '../src/services/chart/charts/classes/test-unique'; import TestUniqueChart from '../src/services/chart/charts/test-unique';
import * as _TestChart from '../src/services/chart/charts/schemas/test'; import * as _TestChart from '../src/services/chart/charts/entities/test';
import * as _TestGroupedChart from '../src/services/chart/charts/schemas/test-grouped'; import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped';
import * as _TestUniqueChart from '../src/services/chart/charts/schemas/test-unique'; import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique';
import Chart from '../src/services/chart/core';
describe('Chart', () => { describe('Chart', () => {
let testChart: TestChart; let testChart: TestChart;
@ -19,9 +18,9 @@ describe('Chart', () => {
beforeEach(async(async () => { beforeEach(async(async () => {
await initTestDb(false, [ await initTestDb(false, [
Chart.schemaToEntity(_TestChart.name, _TestChart.schema), _TestChart.entity.hour, _TestChart.entity.day,
Chart.schemaToEntity(_TestGroupedChart.name, _TestGroupedChart.schema), _TestGroupedChart.entity.hour, _TestGroupedChart.entity.day,
Chart.schemaToEntity(_TestUniqueChart.name, _TestUniqueChart.schema) _TestUniqueChart.entity.hour, _TestUniqueChart.entity.day,
]); ]);
testChart = new TestChart(); testChart = new TestChart();
@ -132,6 +131,32 @@ describe('Chart', () => {
}); });
})); }));
it('複数回saveされてもデータの更新は一度だけ', async(async () => {
await testChart.increment();
await testChart.save();
await testChart.save();
await testChart.save();
const chartHours = await testChart.getChart('hour', 3, null);
const chartDays = await testChart.getChart('day', 3, null);
assert.deepStrictEqual(chartHours, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0]
},
});
assert.deepStrictEqual(chartDays, {
foo: {
dec: [0, 0, 0],
inc: [1, 0, 0],
total: [1, 0, 0]
},
});
}));
it('Can updates at different times', async(async () => { it('Can updates at different times', async(async () => {
await testChart.increment(); await testChart.increment();
await testChart.save(); await testChart.save();

View file

@ -188,6 +188,11 @@
node-fetch "^2.6.1" node-fetch "^2.6.1"
yaml-ast-parser "0.0.43" yaml-ast-parser "0.0.43"
"@sindresorhus/is@^3.0.0":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-3.1.2.tgz#548650de521b344e3781fbdb0ece4aa6f729afb8"
integrity sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==
"@sindresorhus/is@^4.0.0": "@sindresorhus/is@^4.0.0":
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4"
@ -314,13 +319,6 @@
dependencies: dependencies:
cbor "*" cbor "*"
"@types/cheerio@0.22.18":
version "0.22.18"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.18.tgz#19018dceae691509901e339d63edf1e935978fe6"
integrity sha512-Fq7R3fINAPSdUEhOyjG4iVxgHrOnqDJbY0/BUuiN0pvD/rfmZWekVZnv+vcs8TtpA2XF50uv50LaE4EnpEL/Hw==
dependencies:
"@types/node" "*"
"@types/color-name@^1.1.1": "@types/color-name@^1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@ -738,11 +736,6 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/rsvp@^4.0.4":
version "4.0.4"
resolved "https://registry.yarnpkg.com/@types/rsvp/-/rsvp-4.0.4.tgz#55e93e7054027f1ad4b4ebc1e60e59eb091e2d32"
integrity sha512-J3Ol++HCC7/hwZhanDvggFYU/GtxHxE/e7cGRWxR04BF7Tt3TqJZ84BkzQgDxmX0uu8IagiyfmfoUlBACh2Ilg==
"@types/sanitize-html@2.5.0": "@types/sanitize-html@2.5.0":
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.5.0.tgz#bfef58fbcf2674b20ffcc23c3506faa68c3a13e3" resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.5.0.tgz#bfef58fbcf2674b20ffcc23c3506faa68c3a13e3"
@ -1230,7 +1223,7 @@ ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.5" version "6.12.5"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
@ -1290,7 +1283,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.1" "@types/color-name" "^1.1.1"
color-convert "^2.0.1" color-convert "^2.0.1"
any-promise@^1.0.0, any-promise@^1.1.0: any-promise@^1.0.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
@ -1483,16 +1476,6 @@ aws-sdk@2.1013.0:
uuid "3.3.2" uuid "3.3.2"
xml2js "0.4.19" xml2js "0.4.19"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
axios@^0.19.2: axios@^0.19.2:
version "0.19.2" version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
@ -1882,11 +1865,6 @@ canonicalize@^1.0.1:
resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.1.tgz#657b4f3fa38a6ecb97a9e5b7b26d7a19cc6e0da9" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.1.tgz#657b4f3fa38a6ecb97a9e5b7b26d7a19cc6e0da9"
integrity sha512-N3cmB3QLhS5TJ5smKFf1w42rJXWe6C1qP01z4dxJiI5v269buii4fLHWETDyf7yEd0azGLNC63VxNMiPd2u0Cg== integrity sha512-N3cmB3QLhS5TJ5smKFf1w42rJXWe6C1qP01z4dxJiI5v269buii4fLHWETDyf7yEd0azGLNC63VxNMiPd2u0Cg==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
cbor@*: cbor@*:
version "7.0.5" version "7.0.5"
resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f" resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f"
@ -1964,31 +1942,7 @@ chartjs-plugin-zoom@1.1.1:
dependencies: dependencies:
hammerjs "^2.0.8" hammerjs "^2.0.8"
cheerio-httpcli@0.8.2: cheerio@0.22.0:
version "0.8.2"
resolved "https://registry.yarnpkg.com/cheerio-httpcli/-/cheerio-httpcli-0.8.2.tgz#0189bda71c8bd2852de78e154291e2288184fbf2"
integrity sha512-grIzTwQg/nE7Oy6VvL19pf0UlM6wiluy/AOpXfQLVFrSi21F8wnO3dLchtaH2hfMF6jz68ot0/ngyQQVrp2VTw==
dependencies:
"@types/cheerio" "0.22.18"
"@types/rsvp" "^4.0.4"
async "^3.2.0"
cheerio "^0.22.0"
colors "^1.4.0"
foreach "^2.0.5"
he "^1.2.0"
iconv-lite "^0.6.3"
import-fresh "^3.3.0"
jschardet "^3.0.0"
object-assign "^4.1.1"
os-locale "^5.0.0"
prettyjson "^1.2.1"
request "^2.88.2"
rsvp "^4.8.5"
tough-cookie "^2.5.0"
type-of "^2.0.1"
valid-url "^1.0.9"
cheerio@^0.22.0:
version "0.22.0" version "0.22.0"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=
@ -2169,12 +2123,7 @@ colorette@^1.2.0, colorette@^1.2.1, colorette@^1.2.2:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
colors@^1.1.2, colors@^1.4.0: combined-stream@^1.0.8:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8" version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@ -2329,7 +2278,7 @@ cross-env@7.0.3:
dependencies: dependencies:
cross-spawn "^7.0.1" cross-spawn "^7.0.1"
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@ -2535,14 +2484,14 @@ debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
dependencies: dependencies:
ms "2.1.2" ms "2.1.2"
debug@4.3.2, debug@^4.3.2: debug@4.3.3:
version "4.3.2" version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies: dependencies:
ms "2.1.2" ms "2.1.2"
debug@=3.1.0, debug@~3.1.0: debug@=3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
@ -2563,6 +2512,13 @@ debug@^3.2.7:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
debuglog@^1.0.0: debuglog@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@ -3296,21 +3252,6 @@ execa@6.0.0:
signal-exit "^3.0.5" signal-exit "^3.0.5"
strip-final-newline "^3.0.0" strip-final-newline "^3.0.0"
execa@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
dependencies:
cross-spawn "^7.0.0"
get-stream "^5.0.0"
human-signals "^1.1.1"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.0"
onetime "^5.1.0"
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
exit-on-epipe@~1.0.1: exit-on-epipe@~1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
@ -3342,11 +3283,6 @@ extend-shallow@^2.0.1:
dependencies: dependencies:
is-extendable "^0.1.0" is-extendable "^0.1.0"
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
extsprintf@1.3.0: extsprintf@1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
@ -3509,16 +3445,6 @@ follow-redirects@1.5.10:
dependencies: dependencies:
debug "=3.1.0" debug "=3.1.0"
foreach@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@^3.0.0: form-data@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682"
@ -3528,15 +3454,6 @@ form-data@^3.0.0:
combined-stream "^1.0.8" combined-stream "^1.0.8"
mime-types "^2.1.12" mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
fresh@~0.5.2: fresh@~0.5.2:
version "0.5.2" version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
@ -3623,7 +3540,7 @@ get-port@^5.1.1:
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
get-stream@^5.0.0, get-stream@^5.1.0: get-stream@^5.1.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
@ -3744,6 +3661,23 @@ globby@^11.0.4:
merge2 "^1.3.0" merge2 "^1.3.0"
slash "^3.0.0" slash "^3.0.0"
got@11.5.1:
version "11.5.1"
resolved "https://registry.yarnpkg.com/got/-/got-11.5.1.tgz#bf098a270fe80b3fb88ffd5a043a59ebb0a391db"
integrity sha512-reQEZcEBMTGnujmQ+Wm97mJs/OK6INtO6HmLI+xt3+9CvnRwWjXutUvb2mqr+Ao4Lu05Rx6+udx9sOQAmExMxA==
dependencies:
"@sindresorhus/is" "^3.0.0"
"@szmarczak/http-timer" "^4.0.5"
"@types/cacheable-request" "^6.0.1"
"@types/responselike" "^1.0.0"
cacheable-lookup "^5.0.3"
cacheable-request "^7.0.1"
decompress-response "^6.0.0"
http2-wrapper "^1.0.0-beta.5.0"
lowercase-keys "^2.0.0"
p-cancelable "^2.0.0"
responselike "^2.0.0"
got@11.8.2: got@11.8.2:
version "11.8.2" version "11.8.2"
resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599"
@ -3786,19 +3720,6 @@ hammerjs@^2.0.8:
resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
dependencies:
ajv "^6.5.5"
har-schema "^2.0.0"
has-bigints@^1.0.1: has-bigints@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
@ -3843,7 +3764,7 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
he@1.2.0, he@^1.2.0: he@1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@ -3963,14 +3884,13 @@ http-signature@1.3.5:
jsprim "^1.2.2" jsprim "^1.2.2"
sshpk "^1.14.1" sshpk "^1.14.1"
http-signature@~1.2.0: http2-wrapper@^1.0.0-beta.5.0:
version "1.2.0" version "1.0.3"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
dependencies: dependencies:
assert-plus "^1.0.0" quick-lru "^5.1.1"
jsprim "^1.2.2" resolve-alpn "^1.0.0"
sshpk "^1.7.0"
http2-wrapper@^1.0.0-beta.5.2: http2-wrapper@^1.0.0-beta.5.2:
version "1.0.0-beta.5.2" version "1.0.0-beta.5.2"
@ -3995,11 +3915,6 @@ https-proxy-agent@^5.0.0:
agent-base "6" agent-base "6"
debug "4" debug "4"
human-signals@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
human-signals@^3.0.1: human-signals@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5"
@ -4031,13 +3946,6 @@ iconv-lite@^0.6.2:
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3.0.0" safer-buffer ">= 2.1.2 < 3.0.0"
iconv-lite@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
icss-utils@^5.0.0, icss-utils@^5.1.0: icss-utils@^5.0.0, icss-utils@^5.1.0:
version "5.1.0" version "5.1.0"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
@ -4083,14 +3991,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1:
parent-module "^1.0.0" parent-module "^1.0.0"
resolve-from "^4.0.0" resolve-from "^4.0.0"
import-fresh@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
dependencies:
parent-module "^1.0.0"
resolve-from "^4.0.0"
imurmurhash@^0.1.4: imurmurhash@^0.1.4:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@ -4158,11 +4058,6 @@ internal-slot@^1.0.3:
has "^1.0.3" has "^1.0.3"
side-channel "^1.0.4" side-channel "^1.0.4"
invert-kv@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.1.tgz#a93c7a3d4386a1dc8325b97da9bb1620c0282523"
integrity sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==
ioredis@^4.27.0: ioredis@^4.27.0:
version "4.27.6" version "4.27.6"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.6.tgz#a53d427d3fe75fbd10ed7ad150ce00559df8dcf8" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.27.6.tgz#a53d427d3fe75fbd10ed7ad150ce00559df8dcf8"
@ -4418,11 +4313,6 @@ is-shared-array-buffer@^1.0.1:
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6"
integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
is-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
is-stream@^3.0.0: is-stream@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
@ -4456,7 +4346,7 @@ is-symbol@^1.0.3:
dependencies: dependencies:
has-symbols "^1.0.2" has-symbols "^1.0.2"
is-typedarray@^1.0.0, is-typedarray@~1.0.0: is-typedarray@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
@ -4498,11 +4388,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jest-worker@^26.6.2: jest-worker@^26.6.2:
version "26.6.2" version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
@ -4575,7 +4460,7 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jschardet@^3.0.0: jschardet@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882"
integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==
@ -4643,7 +4528,7 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: json-stringify-safe@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
@ -4773,26 +4658,11 @@ koa-bodyparser@4.3.0:
co-body "^6.0.0" co-body "^6.0.0"
copy-to "^2.0.1" copy-to "^2.0.1"
koa-compose@^3.0.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7"
integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=
dependencies:
any-promise "^1.1.0"
koa-compose@^4.1.0: koa-compose@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"
integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==
koa-convert@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0"
integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=
dependencies:
co "^4.6.0"
koa-compose "^3.0.0"
koa-convert@^2.0.0: koa-convert@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5"
@ -4877,35 +4747,6 @@ koa-views@7.0.2:
pretty "^2.0.0" pretty "^2.0.0"
resolve-path "^1.4.0" resolve-path "^1.4.0"
koa@2.13.1:
version "2.13.1"
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.1.tgz#6275172875b27bcfe1d454356a5b6b9f5a9b1051"
integrity sha512-Lb2Dloc72auj5vK4X4qqL7B5jyDPQaZucc9sR/71byg7ryoD1NCaCm63CShk9ID9quQvDEi1bGR/iGjCG7As3w==
dependencies:
accepts "^1.3.5"
cache-content-type "^1.0.0"
content-disposition "~0.5.2"
content-type "^1.0.4"
cookies "~0.8.0"
debug "~3.1.0"
delegates "^1.0.0"
depd "^2.0.0"
destroy "^1.0.4"
encodeurl "^1.0.2"
escape-html "^1.0.3"
fresh "~0.5.2"
http-assert "^1.3.0"
http-errors "^1.6.3"
is-generator-function "^1.0.7"
koa-compose "^4.1.0"
koa-convert "^1.2.0"
on-finished "^2.3.0"
only "~0.0.2"
parseurl "^1.3.2"
statuses "^1.5.0"
type-is "^1.6.16"
vary "^1.1.2"
koa@2.13.4: koa@2.13.4:
version "2.13.4" version "2.13.4"
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e" resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e"
@ -4960,13 +4801,6 @@ lazystream@^1.0.0:
dependencies: dependencies:
readable-stream "^2.0.5" readable-stream "^2.0.5"
lcid@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-3.1.1.tgz#9030ec479a058fc36b5e8243ebaac8b6ac582fd0"
integrity sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==
dependencies:
invert-kv "^3.0.0"
levn@^0.4.1: levn@^0.4.1:
version "0.4.1" version "0.4.1"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@ -5193,13 +5027,6 @@ make-fetch-happen@^8.0.14:
socks-proxy-agent "^5.0.0" socks-proxy-agent "^5.0.0"
ssri "^8.0.0" ssri "^8.0.0"
map-age-cleaner@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
dependencies:
p-defer "^1.0.0"
mdn-data@2.0.14: mdn-data@2.0.14:
version "2.0.14" version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
@ -5210,15 +5037,6 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
mem@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3"
integrity sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==
dependencies:
map-age-cleaner "^0.1.3"
mimic-fn "^2.1.0"
p-is-promise "^2.1.0"
merge-stream@^2.0.0: merge-stream@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@ -5284,18 +5102,13 @@ mime-types@2.1.34:
dependencies: dependencies:
mime-db "1.51.0" mime-db "1.51.0"
mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24:
version "2.1.27" version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies: dependencies:
mime-db "1.44.0" mime-db "1.44.0"
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-fn@^4.0.0: mimic-fn@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
@ -5690,13 +5503,6 @@ normalize-url@^6.0.1:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
npm-run-path@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
npm-run-path@^5.0.1: npm-run-path@^5.0.1:
version "5.0.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.0.1.tgz#748dd68ed7de377bb1f7132c7dafe657be5ab400" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.0.1.tgz#748dd68ed7de377bb1f7132c7dafe657be5ab400"
@ -5738,11 +5544,6 @@ nwsapi@^2.2.0:
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
oauth@0.9.15: oauth@0.9.15:
version "0.9.15" version "0.9.15"
resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1"
@ -5824,13 +5625,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies: dependencies:
wrappy "1" wrappy "1"
onetime@^5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
onetime@^6.0.0: onetime@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
@ -5877,15 +5671,6 @@ os-homedir@^1.0.0:
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
os-locale@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-5.0.0.tgz#6d26c1d95b6597c5d5317bf5fba37eccec3672e0"
integrity sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA==
dependencies:
execa "^4.0.0"
lcid "^3.0.0"
mem "^5.0.0"
os-tmpdir@^1.0.0: os-tmpdir@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@ -5909,21 +5694,11 @@ p-cancelable@^2.0.0:
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e"
integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==
p-defer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
p-finally@^1.0.0: p-finally@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
p-limit@^1.1.0: p-limit@^1.1.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@ -6066,7 +5841,7 @@ path-is-absolute@1.0.1, path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-key@^3.0.0, path-key@^3.1.0: path-key@^3.1.0:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
@ -6096,11 +5871,6 @@ peek-readable@^4.0.1:
resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.0.1.tgz#9a045f291db254111c3412c1ce4fec27ddd4d202" resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.0.1.tgz#9a045f291db254111c3412c1ce4fec27ddd4d202"
integrity sha512-7qmhptnR0WMSpxT5rMHG9bW/mYSR1uqaPFj2MHvT+y/aOUu6msJijpKt5SkTDKySwg65OWG2JwTMBlgcbwMHrQ== integrity sha512-7qmhptnR0WMSpxT5rMHG9bW/mYSR1uqaPFj2MHvT+y/aOUu6msJijpKt5SkTDKySwg65OWG2JwTMBlgcbwMHrQ==
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
pg-connection-string@^2.5.0: pg-connection-string@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
@ -6529,14 +6299,6 @@ pretty@^2.0.0:
extend-shallow "^2.0.1" extend-shallow "^2.0.1"
js-beautify "^1.6.12" js-beautify "^1.6.12"
prettyjson@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289"
integrity sha1-/P+rQdGcq0365eV15kJGYZsS0ok=
dependencies:
colors "^1.1.2"
minimist "^1.2.0"
printj@~1.1.0: printj@~1.1.0:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
@ -6620,7 +6382,7 @@ pseudomap@^1.0.2:
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.28, psl@^1.1.33: psl@^1.1.33:
version "1.8.0" version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
@ -6778,11 +6540,6 @@ qs@^6.4.0, qs@^6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
querystring@0.2.0: querystring@0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@ -6966,22 +6723,6 @@ rename@1.0.4:
dependencies: dependencies:
debug "^2.5.2" debug "^2.5.2"
request-promise-core@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
dependencies:
lodash "^4.17.19"
request-promise-native@1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
dependencies:
request-promise-core "1.1.4"
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
request-stats@3.0.0: request-stats@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/request-stats/-/request-stats-3.0.0.tgz#769155dc8974d78d4a1cb87bbf14eaab985afe25" resolved "https://registry.yarnpkg.com/request-stats/-/request-stats-3.0.0.tgz#769155dc8974d78d4a1cb87bbf14eaab985afe25"
@ -6990,32 +6731,6 @@ request-stats@3.0.0:
http-headers "^3.0.1" http-headers "^3.0.1"
once "^1.4.0" once "^1.4.0"
request@2.88.2, request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
require-all@3.0.0: require-all@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/require-all/-/require-all-3.0.0.tgz#473d49704be310115ce124f77383b1ebd8671312" resolved "https://registry.yarnpkg.com/require-all/-/require-all-3.0.0.tgz#473d49704be310115ce124f77383b1ebd8671312"
@ -7102,11 +6817,6 @@ rndstr@1.0.0:
rangestr "0.0.1" rangestr "0.0.1"
seedrandom "2.4.2" seedrandom "2.4.2"
rsvp@^4.8.5:
version "4.8.5"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
run-parallel@^1.1.9: run-parallel@^1.1.9:
version "1.1.9" version "1.1.9"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
@ -7132,7 +6842,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
@ -7294,7 +7004,7 @@ sigmund@^1.0.1:
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2: signal-exit@^3.0.0:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
@ -7404,7 +7114,7 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sshpk@^1.14.1, sshpk@^1.7.0: sshpk@^1.14.1:
version "1.16.1" version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
@ -7441,11 +7151,6 @@ standard-as-callback@^2.1.0:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
stealthy-require@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
stream-parser@~0.3.1: stream-parser@~0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773"
@ -7614,11 +7319,6 @@ strip-bom@^3.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strip-final-newline@^3.0.0: strip-final-newline@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
@ -7655,18 +7355,19 @@ stylehacks@^5.0.1:
browserslist "^4.16.0" browserslist "^4.16.0"
postcss-selector-parser "^6.0.4" postcss-selector-parser "^6.0.4"
summaly@2.4.1: summaly@2.5.0:
version "2.4.1" version "2.5.0"
resolved "https://registry.yarnpkg.com/summaly/-/summaly-2.4.1.tgz#d2a8fa6bad10c1651eb0b849aab3009e87216a3d" resolved "https://registry.yarnpkg.com/summaly/-/summaly-2.5.0.tgz#ec5af6e84857efcb6c844d896e83569e64a923ea"
integrity sha512-1gETEQXqK5RD7yIGgdGeTwGL1uh+uj14u99atzNLNmvsxwdtZbPvDHZBPXkAW0cqsd8teoBJln5Dh1QeAhvGIg== integrity sha512-IzvO2s7yj/PUyH42qWjVjSPpIiPlgTRWGh33t4cIZKOqPQJ2INo7e83hXhHFr4hXTb3JRcIdCuM1ELjlrujiUQ==
dependencies: dependencies:
cheerio-httpcli "0.8.2" cheerio "0.22.0"
debug "4.3.2" debug "4.3.3"
escape-regexp "0.0.1" escape-regexp "0.0.1"
got "11.5.1"
html-entities "2.3.2" html-entities "2.3.2"
koa "2.13.1" jschardet "3.0.0"
request "2.88.2" koa "2.13.4"
request-promise-native "1.0.9" private-ip "2.3.3"
require-all "3.0.0" require-all "3.0.0"
trace-redirect "1.0.6" trace-redirect "1.0.6"
@ -7889,14 +7590,6 @@ token-types@^4.1.1:
"@tokenizer/token" "^0.3.0" "@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1" ieee754 "^1.2.1"
tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
dependencies:
psl "^1.1.28"
punycode "^2.1.1"
tough-cookie@^4.0.0: tough-cookie@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
@ -8055,11 +7748,6 @@ type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.4:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.24" mime-types "~2.1.24"
type-of@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/type-of/-/type-of-2.0.1.tgz#e72a1741896568e9f628378d816d6912f7f23972"
integrity sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI=
type@^1.0.1: type@^1.0.1:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
@ -8223,21 +7911,11 @@ uuid@8.3.2, uuid@^8.3.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v8-compile-cache@^2.0.3: v8-compile-cache@^2.0.3:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==
valid-url@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200"
integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=
vary@^1.1.2: vary@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="swhvrteh _popup _shadow" @contextmenu.prevent="() => {}"> <div class="swhvrteh _popup _shadow" :style="{ zIndex }" @contextmenu.prevent="() => {}">
<ol v-if="type === 'user'" ref="suggests" class="users"> <ol v-if="type === 'user'" ref="suggests" class="users">
<li v-for="user in users" tabindex="-1" class="user" @click="complete(type, user)" @keydown="onKeydown"> <li v-for="user in users" tabindex="-1" class="user" @click="complete(type, user)" @keydown="onKeydown">
<img class="avatar" :src="user.avatarUrl"/> <img class="avatar" :src="user.avatarUrl"/>
@ -157,6 +157,7 @@ export default defineComponent({
items: [], items: [],
mfmTags: [], mfmTags: [],
select: -1, select: -1,
zIndex: os.claimZIndex(true),
} }
}, },
@ -403,7 +404,6 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.swhvrteh { .swhvrteh {
position: fixed; position: fixed;
z-index: 65535;
max-width: 100%; max-width: 100%;
margin-top: calc(1em + 8px); margin-top: calc(1em + 8px);
overflow: hidden; overflow: hidden;

View file

@ -1,5 +1,5 @@
<template> <template>
<MkModal ref="modal" @click="done(true)" @closed="$emit('closed')"> <MkModal ref="modal" :prefer-type="'dialog'" :front="true" @click="done(true)" @closed="$emit('closed')">
<div class="mk-dialog"> <div class="mk-dialog">
<div v-if="icon" class="icon"> <div v-if="icon" class="icon">
<i :class="icon"></i> <i :class="icon"></i>

View file

@ -1,17 +1,17 @@
<template> <template>
<MkPopup ref="popup" v-slot="{ point, close }" :manual-showing="manualShowing" :src="src" :front="true" @click="close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')"> <MkModal ref="modal" v-slot="{ type, maxHeight }" :prefer-type="asReactionPicker && $store.state.reactionPickerUseDrawerForMobile === false ? 'popup' : 'auto'" :transparent-bg="true" :manual-showing="manualShowing" :src="src" :front="true" @click="$refs.modal.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')">
<MkEmojiPicker ref="picker" class="ryghynhb _popup _shadow" :class="{ pointer: point === 'top' }" :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen"/> <MkEmojiPicker ref="picker" class="ryghynhb _popup _shadow" :class="{ drawer: type === 'drawer' }" :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" :as-drawer="type === 'drawer'" :max-height="maxHeight" @chosen="chosen"/>
</MkPopup> </MkModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, markRaw } from 'vue'; import { defineComponent, markRaw } from 'vue';
import MkPopup from '@/components/ui/popup.vue'; import MkModal from '@/components/ui/modal.vue';
import MkEmojiPicker from '@/components/emoji-picker.vue'; import MkEmojiPicker from '@/components/emoji-picker.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
MkPopup, MkModal,
MkEmojiPicker, MkEmojiPicker,
}, },
@ -44,7 +44,7 @@ export default defineComponent({
methods: { methods: {
chosen(emoji: any) { chosen(emoji: any) {
this.$emit('done', emoji); this.$emit('done', emoji);
this.$refs.popup.close(); this.$refs.modal.close();
}, },
opening() { opening() {
@ -57,20 +57,10 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.ryghynhb { .ryghynhb {
&.pointer { &.drawer {
&:before { border-radius: 24px;
--size: 8px; border-bottom-right-radius: 0;
content: ''; border-bottom-left-radius: 0;
display: block;
position: absolute;
top: calc(0px - (var(--size) * 2));
left: 0;
right: 0;
width: 0;
margin: auto;
border: solid var(--size) transparent;
border-bottom-color: var(--popup);
}
} }
} }
</style> </style>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="omfetrab" :class="['w' + width, 'h' + height, { big }]"> <div class="omfetrab" :class="['w' + width, 'h' + height, { big, asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : null }">
<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()"> <input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
<div ref="emojis" class="emojis"> <div ref="emojis" class="emojis">
<section class="result"> <section class="result">
@ -92,9 +92,17 @@ export default defineComponent({
props: { props: {
showPinned: { showPinned: {
required: false, required: false,
default: true default: true,
}, },
asReactionPicker: { asReactionPicker: {
required: false,
},
maxHeight: {
type: Number,
required: false,
},
asDrawer: {
type: Boolean,
required: false required: false
}, },
}, },
@ -353,26 +361,60 @@ export default defineComponent({
&.w1 { &.w1 {
width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); width: calc((var(--eachSize) * 5) + (#{$pad} * 2));
--columns: 1fr 1fr 1fr 1fr 1fr;
} }
&.w2 { &.w2 {
width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); width: calc((var(--eachSize) * 6) + (#{$pad} * 2));
--columns: 1fr 1fr 1fr 1fr 1fr 1fr;
} }
&.w3 { &.w3 {
width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); width: calc((var(--eachSize) * 7) + (#{$pad} * 2));
--columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
} }
&.h1 { &.h1 {
--height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); height: calc((var(--eachSize) * 4) + (#{$pad} * 2));
} }
&.h2 { &.h2 {
--height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); height: calc((var(--eachSize) * 6) + (#{$pad} * 2));
} }
&.h3 { &.h3 {
--height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); height: calc((var(--eachSize) * 8) + (#{$pad} * 2));
}
&.asDrawer {
width: 100% !important;
> .emojis {
::v-deep(section) {
> header {
height: 32px;
line-height: 32px;
padding: 0 12px;
font-size: 15px;
}
> div {
display: grid;
grid-template-columns: var(--columns);
> button {
aspect-ratio: 1 / 1;
width: auto;
height: auto;
min-width: 0;
> * {
font-size: 30px;
}
}
}
}
}
} }
> .search { > .search {
@ -409,7 +451,7 @@ export default defineComponent({
} }
> .emojis { > .emojis {
height: var(--height); height: 100%;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;

View file

@ -6,7 +6,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import * as katex from 'katex';import * as os from '@/os'; import katex from 'katex';
export default defineComponent({ export default defineComponent({
props: { props: {

View file

@ -1,5 +1,5 @@
<template> <template>
<MkModal ref="modal" @click="$refs.modal.close()" @closed="$emit('closed')"> <MkModal ref="modal" :prefer-type="'dialog'" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="szkkfdyq _popup"> <div class="szkkfdyq _popup">
<div class="main"> <div class="main">
<template v-for="item in items"> <template v-for="item in items">

View file

@ -1,5 +1,5 @@
<template> <template>
<MkModal ref="modal" :position="'top'" @click="$refs.modal.close()" @closed="$emit('closed')"> <MkModal ref="modal" :prefer-type="'dialog:top'" @click="$refs.modal.close()" @closed="$emit('closed')">
<MkPostForm v-bind="$attrs" @posted="$refs.modal.close()" @cancel="$refs.modal.close()" @esc="$refs.modal.close()"/> <MkPostForm v-bind="$attrs" @posted="$refs.modal.close()" @cancel="$refs.modal.close()" @esc="$refs.modal.close()"/>
</MkModal> </MkModal>
</template> </template>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="mk-toast"> <div class="mk-toast" :style="{ zIndex }">
<transition name="notification-slide" appear @after-leave="$emit('closed')"> <transition name="notification-slide" appear @after-leave="$emit('closed')">
<XNotification v-if="showing" :notification="notification" class="notification _acrylic"/> <XNotification v-if="showing" :notification="notification" class="notification _acrylic"/>
</transition> </transition>
@ -9,6 +9,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import XNotification from './notification.vue'; import XNotification from './notification.vue';
import * as os from '@/os';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -23,7 +24,8 @@ export default defineComponent({
emits: ['closed'], emits: ['closed'],
data() { data() {
return { return {
showing: true showing: true,
zIndex: os.claimZIndex(true),
}; };
}, },
mounted() { mounted() {
@ -45,7 +47,6 @@ export default defineComponent({
.mk-toast { .mk-toast {
position: fixed; position: fixed;
z-index: 10000;
left: 0; left: 0;
width: 250px; width: 250px;
top: 32px; top: 32px;

View file

@ -1,8 +1,8 @@
<template> <template>
<div ref="items" v-hotkey="keymap" <div ref="items" v-hotkey="keymap"
class="rrevdjwt" class="rrevdjwt"
:class="{ center: align === 'center' }" :class="{ center: align === 'center', asDrawer }"
:style="{ width: width ? width + 'px' : null, maxHeight: maxHeight ? maxHeight + 'px' : null }" :style="{ width: (width && !asDrawer) ? width + 'px' : null, maxHeight: maxHeight ? maxHeight + 'px' : null }"
@contextmenu.self="e => e.preventDefault()" @contextmenu.self="e => e.preventDefault()"
> >
<template v-for="(item, i) in items2"> <template v-for="(item, i) in items2">
@ -56,6 +56,10 @@ export default defineComponent({
type: Boolean, type: Boolean,
required: false required: false
}, },
asDrawer: {
type: Boolean,
required: false
},
align: { align: {
type: String, type: String,
requried: false requried: false
@ -279,5 +283,29 @@ export default defineComponent({
height: 1px; height: 1px;
background: var(--divider); background: var(--divider);
} }
&.asDrawer {
padding: 12px 0;
width: 100%;
> .item {
font-size: 1em;
padding: 12px 24px;
&:before {
width: calc(100% - 24px);
border-radius: 12px;
}
> i {
margin-right: 14px;
width: 24px;
}
}
> .divider {
margin: 12px 0;
}
}
} }
</style> </style>

View file

@ -1,5 +1,5 @@
<template> <template>
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')"> <MkModal ref="modal" :prefer-type="'dialog'" @click="$emit('click')" @closed="$emit('closed')">
<div class="ebkgoccj _window _narrow_" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown"> <div class="ebkgoccj _window _narrow_" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }" @keydown="onKeydown">
<div class="header"> <div class="header">
<button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="fas fa-times"></i></button> <button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="fas fa-times"></i></button>

View file

@ -1,16 +1,18 @@
<template> <template>
<transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? popup ? 500 : 300 : 0" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered"> <transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="$emit('closed')" @enter="$emit('opening')" @after-enter="childRendered">
<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ front }" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> <div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<div class="bg _modalBg" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> <div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
<div ref="content" class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick"> <div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick">
<slot></slot> <slot :max-height="maxHeight" :type="type"></slot>
</div> </div>
</div> </div>
</transition> </transition>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, nextTick, onMounted, computed, PropType, ref, watch } from 'vue';
import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch';
function getFixedContainer(el: Element | null): Element | null { function getFixedContainer(el: Element | null): Element | null {
if (el == null || el.tagName === 'BODY') return null; if (el == null || el.tagName === 'BODY') return null;
@ -26,6 +28,7 @@ export default defineComponent({
provide: { provide: {
modal: true modal: true
}, },
props: { props: {
manualShowing: { manualShowing: {
type: Boolean, type: Boolean,
@ -37,60 +40,86 @@ export default defineComponent({
required: false required: false
}, },
src: { src: {
type: Object as PropType<HTMLElement>,
required: false, required: false,
default: null,
}, },
position: { preferType: {
required: false required: false,
type: String,
default: 'auto',
}, },
front: { front: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false,
}
},
emits: ['opening', 'click', 'esc', 'close', 'closed'],
data() {
return {
showing: true,
fixed: false,
transformOrigin: 'center',
contentClicking: false,
};
},
computed: {
keymap(): any {
return {
'esc': () => this.$emit('esc'),
};
}, },
popup(): boolean { noOverlap: {
return this.src != null; type: Boolean,
} required: false,
default: true,
},
transparentBg: {
type: Boolean,
required: false,
default: false,
},
}, },
mounted() {
this.$watch('src', () => {
this.fixed = getFixedContainer(this.src) != null;
this.$nextTick(() => {
this.align();
});
}, { immediate: true });
this.$nextTick(() => { emits: ['opening', 'click', 'esc', 'close', 'closed'],
const popover = this.$refs.content as any;
new ResizeObserver((entries, observer) => { setup(props, context) {
this.align(); const maxHeight = ref<number>();
}).observe(popover); const fixed = ref(false);
const transformOrigin = ref('center');
const showing = ref(true);
const content = ref<HTMLElement>();
const zIndex = os.claimZIndex(props.front);
const type = computed(() => {
if (props.preferType === 'auto') {
if (isTouchUsing && window.innerWidth < 500 && window.innerHeight < 1000) {
return 'drawer';
} else {
return props.src != null ? 'popup' : 'dialog';
}
} else {
return props.preferType;
}
}); });
},
methods: {
align() {
if (!this.popup) return;
const popover = this.$refs.content as any; let contentClicking = false;
const close = () => {
// eslint-disable-next-line vue/no-mutating-props
if (props.src) props.src.style.pointerEvents = 'auto';
showing.value = false;
context.emit('close');
};
const onBgClick = () => {
if (contentClicking) return;
context.emit('click');
};
if (type.value === 'drawer') {
maxHeight.value = window.innerHeight / 2;
}
const keymap = {
'esc': () => context.emit('esc'),
};
const MARGIN = 16;
const align = () => {
if (props.src == null) return;
if (type.value === 'drawer') return;
const popover = content.value!;
if (popover == null) return; if (popover == null) return;
const rect = this.src.getBoundingClientRect(); const rect = props.src.getBoundingClientRect();
const width = popover.offsetWidth; const width = popover.offsetWidth;
const height = popover.offsetHeight; const height = popover.offsetHeight;
@ -98,102 +127,143 @@ export default defineComponent({
let left; let left;
let top; let top;
if (this.srcCenter) { if (props.srcCenter) {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2); const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.src.offsetHeight / 2); const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + (props.src.offsetHeight / 2);
left = (x - (width / 2)); left = (x - (width / 2));
top = (y - (height / 2)); top = (y - (height / 2));
} else { } else {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2); const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.src.offsetHeight; const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + props.src.offsetHeight;
left = (x - (width / 2)); left = (x - (width / 2));
top = y; top = y;
} }
if (this.fixed) { if (fixed.value) {
//
if (left + width > window.innerWidth) { if (left + width > window.innerWidth) {
left = window.innerWidth - width; left = window.innerWidth - width;
} }
if (top + height > window.innerHeight) { //
top = window.innerHeight - height; if (top + height > (window.innerHeight - MARGIN)) {
if (props.noOverlap) {
const underSpace = (window.innerHeight - MARGIN) - top;
const upperSpace = (rect.top - MARGIN);
if (underSpace >= (upperSpace / 3)) {
maxHeight.value = underSpace;
} else {
maxHeight.value = upperSpace;
top = (upperSpace + MARGIN) - height;
}
} else {
top = (window.innerHeight - MARGIN) - height;
}
} }
} else { } else {
//
if (left + width - window.pageXOffset > window.innerWidth) { if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset - 1; left = window.innerWidth - width + window.pageXOffset - 1;
} }
if (top + height - window.pageYOffset > window.innerHeight) { //
top = window.innerHeight - height + window.pageYOffset - 1; if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) {
if (props.noOverlap) {
const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset);
const upperSpace = (rect.top - MARGIN);
if (underSpace >= (upperSpace / 3)) {
maxHeight.value = underSpace;
} else {
maxHeight.value = upperSpace;
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
}
} else {
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1;
}
} }
} }
if (top < 0) { if (top < 0) {
top = 0; top = MARGIN;
} }
if (left < 0) { if (left < 0) {
left = 0; left = 0;
} }
if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) { if (top > rect.top + (fixed.value ? 0 : window.pageYOffset)) {
this.transformOrigin = 'center top'; transformOrigin.value = 'center top';
} else if ((top + height) <= rect.top + (fixed.value ? 0 : window.pageYOffset)) {
transformOrigin.value = 'center bottom';
} else { } else {
this.transformOrigin = 'center'; transformOrigin.value = 'center';
} }
popover.style.left = left + 'px'; popover.style.left = left + 'px';
popover.style.top = top + 'px'; popover.style.top = top + 'px';
}, };
childRendered() { const childRendered = () => {
// //
const content = this.$refs.content.children[0]; const el = content.value!.children[0];
content.addEventListener('mousedown', e => { el.addEventListener('mousedown', e => {
this.contentClicking = true; contentClicking = true;
window.addEventListener('mouseup', e => { window.addEventListener('mouseup', e => {
// click mouseup // click mouseup
setTimeout(() => { setTimeout(() => {
this.contentClicking = false; contentClicking = false;
}, 100); }, 100);
}, { passive: true, once: true }); }, { passive: true, once: true });
}, { passive: true }); }, { passive: true });
}, };
close() { onMounted(() => {
this.showing = false; watch(() => props.src, async () => {
this.$emit('close'); if (props.src) {
}, // eslint-disable-next-line vue/no-mutating-props
props.src.style.pointerEvents = 'none';
}
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
onBgClick() { await nextTick()
if (this.contentClicking) return;
this.$emit('click');
},
onClosed() { align();
this.$emit('closed'); }, { immediate: true, });
}
} nextTick(() => {
const popover = content.value;
new ResizeObserver((entries, observer) => {
align();
}).observe(popover!);
});
});
return {
showing,
type,
fixed,
content,
transformOrigin,
maxHeight,
close,
zIndex,
keymap,
onBgClick,
childRendered,
};
},
}); });
</script> </script>
<style lang="scss">
.modal-popup-enter-active, .modal-popup-leave-active,
.modal-enter-from, .modal-leave-to {
> .content {
transform-origin: var(--transformOrigin);
}
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.modal-enter-active, .modal-leave-active { .modal-enter-active, .modal-leave-active {
> .bg { > .bg {
transition: opacity 0.3s !important; transition: opacity 0.2s !important;
} }
> .content { > .content {
transition: opacity 0.3s, transform 0.3s !important; transform-origin: var(--transformOrigin);
transition: opacity 0.2s, transform 0.2s !important;
} }
} }
.modal-enter-from, .modal-leave-to { .modal-enter-from, .modal-leave-to {
@ -204,17 +274,19 @@ export default defineComponent({
> .content { > .content {
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
transform-origin: var(--transformOrigin);
transform: scale(0.9); transform: scale(0.9);
} }
} }
.modal-popup-enter-active, .modal-popup-leave-active { .modal-popup-enter-active, .modal-popup-leave-active {
> .bg { > .bg {
transition: opacity 0.3s !important; transition: opacity 0.2s !important;
} }
> .content { > .content {
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) !important; transform-origin: var(--transformOrigin);
transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1), transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
} }
} }
.modal-popup-enter-from, .modal-popup-leave-to { .modal-popup-enter-from, .modal-popup-leave-to {
@ -225,68 +297,112 @@ export default defineComponent({
> .content { > .content {
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
transform-origin: var(--transformOrigin);
transform: scale(0.9); transform: scale(0.9);
} }
} }
.modal-drawer-enter-active {
> .bg {
transition: opacity 0.2s !important;
}
> .content {
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
}
}
.modal-drawer-leave-active {
> .bg {
transition: opacity 0.2s !important;
}
> .content {
transition: transform 0.2s cubic-bezier(0,.5,0,1) !important;
}
}
.modal-drawer-enter-from, .modal-drawer-leave-to {
> .bg {
opacity: 0;
}
> .content {
pointer-events: none;
transform: translateY(100%);
}
}
.qzhlnise { .qzhlnise {
> .bg { > .bg {
z-index: 10000; &.transparent {
background: transparent;
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
} }
> .content:not(.popup) { &.dialog {
position: fixed; > .content {
z-index: 10000; position: fixed;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
margin: auto;
padding: 32px;
// TODO: mask-imageiOS
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
overflow: auto;
display: flex;
@media (max-width: 500px) {
padding: 16px;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
}
> ::v-deep(*) {
margin: auto; margin: auto;
} padding: 32px;
// TODO: mask-imageiOS
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 32px, rgba(0,0,0,1) calc(100% - 32px), rgba(0,0,0,0) 100%);
overflow: auto;
display: flex;
@media (max-width: 500px) {
padding: 16px;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 16px, rgba(0,0,0,1) calc(100% - 16px), rgba(0,0,0,0) 100%);
}
&.top {
> ::v-deep(*) { > ::v-deep(*) {
margin-top: 0; margin: auto;
}
&.top {
> ::v-deep(*) {
margin-top: 0;
}
} }
} }
} }
> .content.popup { &.popup {
position: absolute; > .content {
z-index: 10000; position: absolute;
&.fixed { &.fixed {
position: fixed;
}
}
}
&.drawer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: clip;
> .content {
position: fixed; position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: auto;
> ::v-deep(*) {
margin: auto;
}
} }
} }
&.front {
> .bg {
z-index: 20000;
}
> .content:not(.popup) {
z-index: 20000;
}
> .content.popup {
z-index: 20000;
}
}
} }
</style> </style>

View file

@ -1,17 +1,17 @@
<template> <template>
<MkPopup ref="popup" v-slot="{ maxHeight, close }" :src="src" @closed="$emit('closed')"> <MkModal ref="modal" v-slot="{ type, maxHeight }" :src="src" :transparent-bg="true" @click="$refs.modal.close()" @closed="$emit('closed')">
<MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" class="_popup _shadow" @close="close()"/> <MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" :as-drawer="type === 'drawer'" class="sfhdhdhq _popup _shadow" :class="{ drawer: type === 'drawer' }" @close="$refs.modal.close()"/>
</MkPopup> </MkModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import MkPopup from './popup.vue'; import MkModal from './modal.vue';
import MkMenu from './menu.vue'; import MkMenu from './menu.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
MkPopup, MkModal,
MkMenu, MkMenu,
}, },
@ -40,3 +40,13 @@ export default defineComponent({
emits: ['close', 'closed'], emits: ['close', 'closed'],
}); });
</script> </script>
<style lang="scss" scoped>
.sfhdhdhq {
&.drawer {
border-radius: 24px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
}
</style>

View file

@ -1,237 +0,0 @@
<template>
<transition :name="$store.state.animation ? 'popup-menu' : ''" appear @after-leave="$emit('closed')" @enter="$emit('opening')">
<div v-show="manualShowing != null ? manualShowing : showing" ref="content" class="ccczpooj" :class="{ fixed, top: position === 'top' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<slot :max-height="maxHeight" :close="close"></slot>
</div>
</transition>
</template>
<script lang="ts">
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, ref, watch } from 'vue';
import * as os from '@/os';
function getFixedContainer(el: Element | null | undefined): Element | null {
if (el == null || el.tagName === 'BODY') return null;
const position = window.getComputedStyle(el).getPropertyValue('position');
if (position === 'fixed') {
return el;
} else {
return getFixedContainer(el.parentElement);
}
}
export default defineComponent({
props: {
manualShowing: {
type: Boolean,
required: false,
default: null,
},
srcCenter: {
type: Boolean,
required: false
},
src: {
type: Object as PropType<HTMLElement>,
required: false,
},
position: {
required: false
},
front: {
type: Boolean,
required: false,
default: false,
},
noOverlap: {
type: Boolean,
required: false,
default: true,
},
},
emits: ['opening', 'click', 'esc', 'close', 'closed'],
setup(props, context) {
const maxHeight = ref<number>();
const fixed = ref(false);
const transformOrigin = ref('center');
const showing = ref(true);
const content = ref<HTMLElement>();
const zIndex = os.claimZIndex(props.front);
const close = () => {
// eslint-disable-next-line vue/no-mutating-props
if (props.src) props.src.style.pointerEvents = 'auto';
showing.value = false;
context.emit('close');
};
const MARGIN = 16;
const align = () => {
if (props.src == null) return;
const popover = content.value!;
if (popover == null) return;
const rect = props.src.getBoundingClientRect();
const width = popover.offsetWidth;
const height = popover.offsetHeight;
let left;
let top;
if (props.srcCenter) {
const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2);
const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + (props.src.offsetHeight / 2);
left = (x - (width / 2));
top = (y - (height / 2));
} else {
const x = rect.left + (fixed.value ? 0 : window.pageXOffset) + (props.src.offsetWidth / 2);
const y = rect.top + (fixed.value ? 0 : window.pageYOffset) + props.src.offsetHeight;
left = (x - (width / 2));
top = y;
}
if (fixed.value) {
//
if (left + width > window.innerWidth) {
left = window.innerWidth - width;
}
//
if (top + height > (window.innerHeight - MARGIN)) {
if (props.noOverlap) {
const underSpace = (window.innerHeight - MARGIN) - top;
const upperSpace = (rect.top - MARGIN);
if (underSpace >= (upperSpace / 3)) {
maxHeight.value = underSpace;
} else {
maxHeight.value = upperSpace;
top = (upperSpace + MARGIN) - height;
}
} else {
top = (window.innerHeight - MARGIN) - height;
}
}
} else {
//
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset - 1;
}
//
if (top + height - window.pageYOffset > (window.innerHeight - MARGIN)) {
if (props.noOverlap) {
const underSpace = (window.innerHeight - MARGIN) - (top - window.pageYOffset);
const upperSpace = (rect.top - MARGIN);
if (underSpace >= (upperSpace / 3)) {
maxHeight.value = underSpace;
} else {
maxHeight.value = upperSpace;
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
}
} else {
top = (window.innerHeight - MARGIN) - height + window.pageYOffset - 1;
}
}
}
if (top < 0) {
top = MARGIN;
}
if (left < 0) {
left = 0;
}
if (top > rect.top + (fixed.value ? 0 : window.pageYOffset)) {
transformOrigin.value = 'center top';
} else if ((top + height) <= rect.top + (fixed.value ? 0 : window.pageYOffset)) {
transformOrigin.value = 'center bottom';
} else {
transformOrigin.value = 'center';
}
popover.style.left = left + 'px';
popover.style.top = top + 'px';
};
const onDocumentClick = (ev: MouseEvent) => {
const flyoutElement = content.value;
let targetElement = ev.target;
do {
if (targetElement === flyoutElement) {
return;
}
targetElement = targetElement.parentNode;
} while (targetElement);
close();
};
onMounted(() => {
watch(() => props.src, async () => {
if (props.src) {
// eslint-disable-next-line vue/no-mutating-props
props.src.style.pointerEvents = 'none';
}
fixed.value = getFixedContainer(props.src) != null;
await nextTick()
align();
}, { immediate: true, });
nextTick(() => {
const popover = content.value;
new ResizeObserver((entries, observer) => {
align();
}).observe(popover!);
});
document.addEventListener('mousedown', onDocumentClick, { passive: true });
onUnmounted(() => {
document.removeEventListener('mousedown', onDocumentClick);
});
});
return {
showing,
fixed,
content,
transformOrigin,
maxHeight,
close,
zIndex,
};
},
});
</script>
<style lang="scss" scoped>
.popup-menu-enter-active {
transform-origin: var(--transformOrigin);
transition: opacity 0.2s cubic-bezier(0, 0, 0.2, 1), transform 0.2s cubic-bezier(0, 0, 0.2, 1) !important;
}
.popup-menu-leave-active {
transform-origin: var(--transformOrigin);
transition: opacity 0.2s cubic-bezier(0.4, 0, 1, 1), transform 0.2s cubic-bezier(0.4, 0, 1, 1) !important;
}
.popup-menu-enter-from, .popup-menu-leave-to {
pointer-events: none;
opacity: 0;
transform: scale(0.9);
}
.ccczpooj {
position: absolute;
&.fixed {
position: fixed;
}
}
</style>

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="fgmtyycl" :style="{ top: top + 'px', left: left + 'px' }"> <div class="fgmtyycl" :style="{ zIndex, top: top + 'px', left: left + 'px' }">
<transition name="zoom" @after-leave="$emit('closed')"> <transition name="zoom" @after-leave="$emit('closed')">
<MkUrlPreview v-if="showing" class="_popup _shadow" :url="url"/> <MkUrlPreview v-if="showing" class="_popup _shadow" :url="url"/>
</transition> </transition>
@ -35,6 +35,7 @@ export default defineComponent({
u: null, u: null,
top: 0, top: 0,
left: 0, left: 0,
zIndex: os.claimZIndex(),
}; };
}, },
@ -52,7 +53,6 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.fgmtyycl { .fgmtyycl {
position: absolute; position: absolute;
z-index: 11000;
width: 500px; width: 500px;
max-width: calc(90vw - 12px); max-width: calc(90vw - 12px);
pointer-events: none; pointer-events: none;

View file

@ -1,6 +1,6 @@
<template> <template>
<transition name="popup" appear @after-leave="$emit('closed')"> <transition name="popup" appear @after-leave="$emit('closed')">
<div v-if="showing" class="fxxzrfni _popup _shadow" :style="{ top: top + 'px', left: left + 'px' }" @mouseover="() => { $emit('mouseover'); }" @mouseleave="() => { $emit('mouseleave'); }"> <div v-if="showing" class="fxxzrfni _popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { $emit('mouseover'); }" @mouseleave="() => { $emit('mouseleave'); }">
<div v-if="fetched" class="info"> <div v-if="fetched" class="info">
<div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div> <div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
<MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/> <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/>
@ -65,6 +65,7 @@ export default defineComponent({
fetched: false, fetched: false,
top: 0, top: 0,
left: 0, left: 0,
zIndex: os.claimZIndex(),
}; };
}, },
@ -109,7 +110,6 @@ export default defineComponent({
.fxxzrfni { .fxxzrfni {
position: absolute; position: absolute;
z-index: 11000;
width: 300px; width: 300px;
overflow: hidden; overflow: hidden;
transform-origin: center top; transform-origin: center top;

View file

@ -1,5 +1,5 @@
<template> <template>
<MkModal ref="modal" @click="success ? done() : () => {}" @closed="$emit('closed')"> <MkModal ref="modal" :prefer-type="'dialog'" @click="success ? done() : () => {}" @closed="$emit('closed')">
<div class="iuyakobc" :class="{ iconOnly: (text == null) || success }"> <div class="iuyakobc" :class="{ iconOnly: (text == null) || success }">
<i v-if="success" class="fas fa-check icon success"></i> <i v-if="success" class="fas fa-check icon success"></i>
<i v-else class="fas fa-spinner fa-pulse icon waiting"></i> <i v-else class="fas fa-spinner fa-pulse icon waiting"></i>

View file

@ -2,7 +2,7 @@
<div class="mk-group-page"> <div class="mk-group-page">
<transition name="zoom" mode="out-in"> <transition name="zoom" mode="out-in">
<div v-if="group" class="_section"> <div v-if="group" class="_section">
<div class="_content"> <div class="_content" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
<MkButton inline @click="invite()">{{ $ts.invite }}</MkButton> <MkButton inline @click="invite()">{{ $ts.invite }}</MkButton>
<MkButton inline @click="renameGroup()">{{ $ts.rename }}</MkButton> <MkButton inline @click="renameGroup()">{{ $ts.rename }}</MkButton>
<MkButton inline @click="transfer()">{{ $ts.transfer }}</MkButton> <MkButton inline @click="transfer()">{{ $ts.transfer }}</MkButton>

View file

@ -1,52 +1,45 @@
<template> <template>
<div class=""> <MkSpacer :content-max="700">
<div class="_section" style="padding: 0;"> <div v-if="tab === 'owned'" class="_content">
<MkTab v-model="tab"> <MkButton primary style="margin: 0 auto var(--margin) auto;" @click="create"><i class="fas fa-plus"></i> {{ $ts.createGroup }}</MkButton>
<option value="owned">{{ $ts.ownedGroups }}</option>
<option value="joined">{{ $ts.joinedGroups }}</option> <MkPagination v-slot="{items}" ref="owned" :pagination="ownedPagination">
<option value="invites"><i class="fas fa-envelope-open-text"></i> {{ $ts.invites }}</option> <div v-for="group in items" :key="group.id" class="_card">
</MkTab> <div class="_title"><MkA :to="`/my/groups/${ group.id }`" class="_link">{{ group.name }}</MkA></div>
<div class="_content"><MkAvatars :user-ids="group.userIds"/></div>
</div>
</MkPagination>
</div> </div>
<div class="_section"> <div v-else-if="tab === 'joined'" class="_content">
<div v-if="tab === 'owned'" class="_content"> <MkPagination v-slot="{items}" ref="joined" :pagination="joinedPagination">
<MkButton primary style="margin: 0 auto var(--margin) auto;" @click="create"><i class="fas fa-plus"></i> {{ $ts.createGroup }}</MkButton> <div v-for="group in items" :key="group.id" class="_card">
<div class="_title">{{ group.name }}</div>
<MkPagination v-slot="{items}" ref="owned" :pagination="ownedPagination"> <div class="_content"><MkAvatars :user-ids="group.userIds"/></div>
<div v-for="group in items" :key="group.id" class="_card"> <div class="_footer">
<div class="_title"><MkA :to="`/my/groups/${ group.id }`" class="_link">{{ group.name }}</MkA></div> <MkButton danger @click="leave(group)">{{ $ts.leaveGroup }}</MkButton>
<div class="_content"><MkAvatars :user-ids="group.userIds"/></div>
</div> </div>
</MkPagination> </div>
</div> </MkPagination>
<div v-else-if="tab === 'joined'" class="_content">
<MkPagination v-slot="{items}" ref="joined" :pagination="joinedPagination">
<div v-for="group in items" :key="group.id" class="_card">
<div class="_title">{{ group.name }}</div>
<div class="_content"><MkAvatars :user-ids="group.userIds"/></div>
</div>
</MkPagination>
</div>
<div v-else-if="tab === 'invites'" class="_content">
<MkPagination v-slot="{items}" ref="invitations" :pagination="invitationPagination">
<div v-for="invitation in items" :key="invitation.id" class="_card">
<div class="_title">{{ invitation.group.name }}</div>
<div class="_content"><MkAvatars :user-ids="invitation.group.userIds"/></div>
<div class="_footer">
<MkButton primary inline @click="acceptInvite(invitation)"><i class="fas fa-check"></i> {{ $ts.accept }}</MkButton>
<MkButton primary inline @click="rejectInvite(invitation)"><i class="fas fa-ban"></i> {{ $ts.reject }}</MkButton>
</div>
</div>
</MkPagination>
</div>
</div> </div>
</div>
<div v-else-if="tab === 'invites'" class="_content">
<MkPagination v-slot="{items}" ref="invitations" :pagination="invitationPagination">
<div v-for="invitation in items" :key="invitation.id" class="_card">
<div class="_title">{{ invitation.group.name }}</div>
<div class="_content"><MkAvatars :user-ids="invitation.group.userIds"/></div>
<div class="_footer">
<MkButton primary inline @click="acceptInvite(invitation)"><i class="fas fa-check"></i> {{ $ts.accept }}</MkButton>
<MkButton primary inline @click="rejectInvite(invitation)"><i class="fas fa-ban"></i> {{ $ts.reject }}</MkButton>
</div>
</div>
</MkPagination>
</div>
</MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, computed } from 'vue';
import MkPagination from '@/components/ui/pagination.vue'; import MkPagination from '@/components/ui/pagination.vue';
import MkButton from '@/components/ui/button.vue'; import MkButton from '@/components/ui/button.vue';
import MkContainer from '@/components/ui/container.vue'; import MkContainer from '@/components/ui/container.vue';
@ -66,10 +59,32 @@ export default defineComponent({
data() { data() {
return { return {
[symbols.PAGE_INFO]: { [symbols.PAGE_INFO]: computed(() => ({
title: this.$ts.groups, title: this.$ts.groups,
icon: 'fas fa-users' icon: 'fas fa-users',
}, bg: 'var(--bg)',
actions: [{
icon: 'fas fa-plus',
text: this.$ts.createGroup,
handler: this.create,
}],
tabs: [{
active: this.tab === 'owned',
title: this.$ts.ownedGroups,
icon: 'fas fa-user-tie',
onClick: () => { this.tab = 'owned'; },
}, {
active: this.tab === 'joined',
title: this.$ts.joinedGroups,
icon: 'fas fa-id-badge',
onClick: () => { this.tab = 'joined'; },
}, {
active: this.tab === 'invites',
title: this.$ts.invites,
icon: 'fas fa-envelope-open-text',
onClick: () => { this.tab = 'invites'; },
},]
})),
tab: 'owned', tab: 'owned',
ownedPagination: { ownedPagination: {
endpoint: 'users/groups/owned', endpoint: 'users/groups/owned',
@ -111,6 +126,18 @@ export default defineComponent({
}).then(() => { }).then(() => {
this.$refs.invitations.reload(); this.$refs.invitations.reload();
}); });
},
async leave(group) {
const { canceled } = await os.confirm({
type: 'warning',
text: this.$t('leaveGroupConfirm', { name: group.name }),
});
if (canceled) return;
os.apiWithDialog('users/groups/leave', {
groupId: group.id,
}).then(() => {
this.$refs.joined.reload();
});
} }
} }
}); });

View file

@ -29,11 +29,14 @@
<option :value="2">{{ $ts.medium }}</option> <option :value="2">{{ $ts.medium }}</option>
<option :value="3">{{ $ts.large }}</option> <option :value="3">{{ $ts.large }}</option>
</FormRadios> </FormRadios>
<FormSwitch v-model="reactionPickerUseDrawerForMobile" class="_formBlock">{{ $ts.useDrawerReactionPickerForMobile }}</FormSwitch>
<FormSection> <FormSection>
<FormButton @click="preview"><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton> <div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
</FormSection> <FormButton inline @click="preview"><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton>
<FormSection> <FormButton inline danger @click="setDefault"><i class="fas fa-undo"></i> {{ $ts.default }}</FormButton>
<FormButton danger @click="setDefault"><i class="fas fa-undo"></i> {{ $ts.default }}</FormButton> </div>
</FormSection> </FormSection>
</div> </div>
</template> </template>
@ -46,6 +49,7 @@ import FormRadios from '@/components/form/radios.vue';
import FromSlot from '@/components/form/slot.vue'; import FromSlot from '@/components/form/slot.vue';
import FormButton from '@/components/ui/button.vue'; import FormButton from '@/components/ui/button.vue';
import FormSection from '@/components/form/section.vue'; import FormSection from '@/components/form/section.vue';
import FormSwitch from '@/components/form/switch.vue';
import * as os from '@/os'; import * as os from '@/os';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -57,6 +61,7 @@ export default defineComponent({
FromSlot, FromSlot,
FormRadios, FormRadios,
FormSection, FormSection,
FormSwitch,
XDraggable, XDraggable,
}, },
@ -80,6 +85,7 @@ export default defineComponent({
computed: { computed: {
reactionPickerWidth: defaultStore.makeGetterSetter('reactionPickerWidth'), reactionPickerWidth: defaultStore.makeGetterSetter('reactionPickerWidth'),
reactionPickerHeight: defaultStore.makeGetterSetter('reactionPickerHeight'), reactionPickerHeight: defaultStore.makeGetterSetter('reactionPickerHeight'),
reactionPickerUseDrawerForMobile: defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile'),
}, },
watch: { watch: {

View file

@ -178,6 +178,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device', where: 'device',
default: 1 default: 1
}, },
reactionPickerUseDrawerForMobile: {
where: 'device',
default: true,
},
recentlyUsedEmojis: { recentlyUsedEmojis: {
where: 'device', where: 'device',
default: [] as string[] default: [] as string[]

View file

@ -346,7 +346,7 @@ hr {
._popup { ._popup {
background: var(--popup); background: var(--popup);
border-radius: var(--radius); border-radius: var(--radius);
contain: contain; contain: content;
} }
// TODO: 廃止 // TODO: 廃止

View file

@ -66,7 +66,7 @@ export default defineComponent({
#wait { #wait {
display: block; display: block;
position: fixed; position: fixed;
z-index: 10000; z-index: 3000000;
top: 15px; top: 15px;
right: 15px; right: 15px;

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="mk-uploader _acrylic"> <div class="mk-uploader _acrylic" :style="{ zIndex }">
<ol v-if="uploads.length > 0"> <ol v-if="uploads.length > 0">
<li v-for="ctx in uploads" :key="ctx.id"> <li v-for="ctx in uploads" :key="ctx.id">
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div> <div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
@ -25,6 +25,7 @@ export default defineComponent({
data() { data() {
return { return {
uploads: os.uploads, uploads: os.uploads,
zIndex: os.claimZIndex(true),
}; };
}, },
}); });
@ -33,7 +34,6 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
.mk-uploader { .mk-uploader {
position: fixed; position: fixed;
z-index: 10000;
right: 16px; right: 16px;
width: 260px; width: 260px;
top: 32px; top: 32px;