Merge branch 'misskey-dev:develop' into sim-dev-ja-nya

This commit is contained in:
こけっち 2021-12-28 02:17:22 +09:00 committed by GitHub
commit 24896d4a36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
149 changed files with 2155 additions and 1791 deletions

View File

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

View File

@ -9,11 +9,49 @@
## 12.x.x (unreleased)
### Improvements
- クライアント: ノートプレビューの精度を改善
### Bugfixes
- クライアント: 一部のコンポーネントが裏に隠れるのを修正
## 12.100.2 (2021/12/18)
### Bugfixes
- クライアント: Deckカラムの増減がページをリロードするまで正しく反映されない問題を修正
- クライアント: 一部のコンポーネントが裏に隠れるのを修正
- クライアント: カスタム絵文字一覧ページの負荷が高いのを修正
## 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
- Added a user-level instance mute in user settings
- フォローエクスポートでミュートしているユーザーを含めないオプションを追加
- フォローエクスポートで使われていないアカウントを含めないオプションを追加
- カスタム絵文字エクスポート機能
- チャートのパフォーマンスの改善
- グループから抜けられるように
### Bugfixes
- クライアント: タッチ機能付きディスプレイを使っていてマウス操作をしている場合に一部機能が動作しない問題を修正

View File

@ -87,7 +87,7 @@ Configuration files are located in [`/.github/workflows`](/.github/workflows).
## Vue
Misskey uses Vue(v3) as its front-end framework.
**When creating a new component, please use the Composition API instead of the Options API.**
**When creating a new component, please use the Composition API (and [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html)) instead of the Options API.**
Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
## Adding MisskeyRoom items

View File

@ -106,7 +106,6 @@ clickToShow: "اضغط للعرض"
sensitive: "محتوى حساس"
add: "إضافة"
reaction: "التفاعلات"
reactionSettingDescription: "اختر التفاعلات المفضلة التي تريد تثبيتها في منتقي التفاعلات."
reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة."
rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات"
attachCancel: "أزل المرفق"
@ -752,6 +751,7 @@ muteThread: "اكتم النقاش"
unmuteThread: "ارفع الكتم عن النقاش"
deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟"
incorrectPassword: "كلمة السر خاطئة."
hide: "إخفاء"
_emailUnavailable:
used: "هذا البريد الإلكتروني مستخدم"
format: "صيغة البريد الإلكتروني غير صالحة"

View File

@ -88,7 +88,6 @@ clickToShow: "Klikněte pro zobrazení"
sensitive: "NSFW"
add: "Přidat"
reaction: "Reakce"
reactionSettingDescription: "Vyberte Vaší oblíbenou reakci, kterou chcete připnout ve výběru."
reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání"
rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky"
attachCancel: "Odstranit přílohu"

View File

@ -106,7 +106,7 @@ clickToShow: "Zum Anzeigen anklicken"
sensitive: "NSFW"
add: "Hinzufügen"
reaction: "Reaktionen"
reactionSettingDescription: "Wähle die Reaktionen aus, die in der Reaktionsauswahl angezeigt werden sollen."
reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen"
reactionSettingDescription2: "Ziehe zum Anordnen, klicke zum Löschen, drücke + zum Hinzufügen"
rememberNoteVisibility: "Notizsichtbarkeit merken"
attachCancel: "Anhang entfernen"
@ -592,6 +592,7 @@ smtpSecure: "Für SMTP-Verbindungen implizit SSL/TLS verwenden"
smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest"
testEmail: "Email-Versand testen"
wordMute: "Wort-Stummschaltung"
instanceMute: "Instanzstummschaltungen"
userSaysSomething: "{name} hat etwas gesagt"
makeActive: "Aktivieren"
display: "Anzeigeart"
@ -684,6 +685,7 @@ center: "Mitte"
wide: "Breit"
narrow: "Schmal"
reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?"
needReloadToApply: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft."
showTitlebar: "Titelleiste anzeigen"
clearCache: "Cache leeren"
onlineUsersCount: "{n} Benutzer sind online"
@ -811,6 +813,11 @@ continueThread: "Weiteren Threadverlauf anzeigen"
deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?"
incorrectPassword: "Falsches Passwort."
voteConfirm: "Wirklich für \"{choice}\" abstimmen?"
hide: "Inhalt verbergen"
leaveGroup: "Gruppe verlassen"
leaveGroupConfirm: "Möchtest du \"{name}\" wirklich verlassen?"
useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen"
welcomeBackWithName: "Willkommen zurück, {name}"
_emailUnavailable:
used: "Diese Email-Adresse wird bereits verwendet"
format: "Das Format dieser Email-Adresse ist ungültig"
@ -1001,6 +1008,11 @@ _wordMute:
soft: "Leicht"
hard: "Schwer"
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:
explore: "Themen erforschen"
install: "Thema installieren"
@ -1274,6 +1286,8 @@ _exportOrImport:
muteList: "Stummschaltungen"
blockingList: "Blockierungen"
userLists: "Listen"
excludeMutingUsers: "Stummgeschaltete Benutzer aussortieren"
excludeInactiveUsers: "Inaktive Benutzer aussortieren"
_charts:
federationInstancesIncDec: "Unterschied in der Anzahl von förderierenden Instanzen"
federationInstancesTotal: "Anzahl aller föderierenden Instanzen"

View File

@ -106,7 +106,7 @@ clickToShow: "Click to show"
sensitive: "NSFW"
add: "Add"
reaction: "Reactions"
reactionSettingDescription: "Configure which reactions you want to display in the reaction picker."
reactionSetting: "Reactions to show in the reaction picker"
reactionSettingDescription2: "Drag to reorder, Click to delete, Press \"+\" to add"
rememberNoteVisibility: "Remember note visibility settings"
attachCancel: "Remove attachment"
@ -137,7 +137,7 @@ addEmoji: "Add an emoji"
settingGuide: "Recommended settings"
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."
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."
flagAsCat: "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"
testEmail: "Test email delivery"
wordMute: "Word mute"
instanceMute: "Instance mutes"
userSaysSomething: "{name} said something"
makeActive: "Activate"
display: "Display"
@ -684,6 +685,7 @@ center: "Center"
wide: "Wide"
narrow: "Narrow"
reloadToApplySetting: "This setting will only apply after a page reload. Reload now?"
needReloadToApply: "This setting will only apply after a page reload."
showTitlebar: "Show title bar"
clearCache: "Clear cache"
onlineUsersCount: "{n} users are online"
@ -810,6 +812,11 @@ continueThread: "View thread continuation"
deleteAccountConfirm: "This will irreversibly delete your account. Proceed?"
incorrectPassword: "Incorrect password."
voteConfirm: "Confirm your vote for \"{choice}\"?"
hide: "Hide"
leaveGroup: "Leave Group"
leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?"
useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile"
welcomeBackWithName: "Welcome back, {name}"
_emailUnavailable:
used: "This email address is already being used"
format: "The format of this email address is invalid"
@ -1000,6 +1007,11 @@ _wordMute:
soft: "Soft"
hard: "Hard"
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:
explore: "Explore Themes"
install: "Install a theme"
@ -1273,6 +1285,8 @@ _exportOrImport:
muteList: "Muted users"
blockingList: "Blocked users"
userLists: "User lists"
excludeMutingUsers: "Exclude muted users"
excludeInactiveUsers: "Exclude inactive users"
_charts:
federationInstancesIncDec: "Difference in # of federating instances"
federationInstancesTotal: "Total # of federating instances"

View File

@ -1,8 +1,8 @@
---
_lang_: "Esperanto"
headlineMisskey: "Jen la reto konektata de 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. 🚀"
monthAndDay: "la {day}a de la {month}a"
headlineMisskey: "Reto konektita per notoj"
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 monato"
search: "Serĉi"
notifications: "Sciigoj"
username: "Uzantnomo"
@ -20,13 +20,13 @@ instance: "Nodo"
settings: "Agordoj"
basicSettings: "Ĝeneralaj agordoj"
otherSettings: "Aliaj agordoj"
openInWindow: "Malfermi en fenestro"
openInWindow: "Malfermi en nova fenestro"
profile: "Profilo"
timeline: "Templinio"
noAccountDescription: "Neniu sinprezento"
login: "Ensaluti"
loggingIn: "Ensalutado…"
logout: "Elsaluti"
noAccountDescription: "Neniu priskribo"
login: "Saluti"
loggingIn: "Salutado…"
logout: "Adiaŭi"
signup: "Registriĝi"
uploading: "Alŝutado…"
save: "Konservi"
@ -58,22 +58,22 @@ followRequestAccepted: "La peto de sekvado akceptita"
mention: "Mencioj"
mentions: "Mencioj"
directNotes: "Rekte senditaj"
importAndExport: "Importi/eksporti"
import: "Importi"
export: "Eksporti"
importAndExport: "Enporti kaj elporti"
import: "Enporti"
export: "Elporti"
files: "Dosieroj"
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."
unfollowConfirm: "Ĉu vi certas, ke vi volas ĉesi sekvi {name}'(o)n?"
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}?"
lists: "Listoj"
noLists: "Neniu listo"
note: "Sendi"
note: "Noti"
notes: "Notoj"
following: "Sekvatoj"
followers: "Sekvantoj"
followsYou: "Sekvas vin"
createList: "Krei liston"
manageLists: "Administri liston"
manageLists: "Bonteni liston"
error: "Eraro"
somethingHappened: "Problemo okazis"
retry: "Provi denove"
@ -101,7 +101,6 @@ clickToShow: "Klaku por malkaŝu"
sensitive: "Enhavo ne estas deca por laborejo (NSFW)"
add: "Aldoni"
reaction: "Reagoj"
reactionSettingDescription: "Agordi la reagojn kiujn vi volas prefere montrigi ĉe la elektilo de reagoj"
rememberNoteVisibility: "Rememori la agordon de videbleco de la laste sendita"
attachCancel: "Deigi aldonaĵon"
markAsSensitive: "Troviĝi NSFW"
@ -129,8 +128,9 @@ emojiUrl: "URL de la emoĵio"
addEmoji: "Aldoni emoĵion"
settingGuide: "Agordaj rekomendoj"
cacheRemoteFiles: "Stapli forajn dosierojn"
flagAsBot: "Fari la flagon por robota uzanto"
flagAsCat: "Fari la flagon por kat-iĝi"
flagAsBot: "Marki kiel uzata de roboto"
flagAsCat: "Agordi kiel kat-iĝa"
flagAsCatDescription: "Se vi estas kato, ebligu la agordon."
autoAcceptFollowed: "Aŭtomate akcepti la peton de sekvado far uzantoj kiujn vi sekvas"
addAccount: "Aldoni konton"
showOnRemote: "Vidi ĉe la surloka nodo"
@ -140,15 +140,16 @@ setWallpaper: "Apliki ekranfonon"
removeWallpaper: "Forviŝi ekranfonon. "
searchWith: "Serĉi: {q}"
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"
selectUser: "Elekti uzanton"
recipient: "Ricevonto"
recipient: "Ricevonton"
annotation: "Komentarioj"
federation: "Federaĵo"
instances: "Nodoj"
latestRequestSentAt: "Lastatempa sendo"
latestRequestReceivedAt: "Lastatempa ricevo"
latestRequestSentAt: "La laste sendita peto"
latestRequestReceivedAt: "La laste ricevita peto "
latestStatus: "Laŭstato"
charts: "Diagramoj"
perHour: "por horo"
@ -157,7 +158,7 @@ blockThisInstance: "Bloki la nodon"
operations: "Agoj"
software: "Programaro"
version: "Versio"
metadata: "Metadatumoj"
metadata: "Pridatumoj"
withNFiles: "{n} dosiero(j)"
monitor: "Monitoro"
network: "Reto"
@ -223,11 +224,11 @@ messageRead: "Legita"
noMoreHistory: "Ne plu de la historio"
startMessaging: "Komenci babiladon"
nUsersRead: "Legita de {n} homoj"
agreeTo: "Mi akceptas {0}'(o)n"
agreeTo: "Mi akceptas {0}"
tos: "Kondiĉoj de uzado"
start: "Komenciĝi"
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"
images: "Bildoj"
birthday: "Naskiĝdato"
@ -235,12 +236,13 @@ yearsOld: "{age} jaroj aĝa"
registeredDate: "Dato de registriĝo"
location: "Kie"
theme: "Koloraro"
themeForLightMode: "Luma kolararo en la luma modo"
themeForDarkMode: "Malluma kolararo en la malluma modo"
themeForLightMode: "Koloraro uzita en la luma modo"
themeForDarkMode: "Koloraro uzita en la malluma modo"
light: "Luma"
dark: "Malluma"
lightThemes: "Luma koloraro"
darkThemes: "Malluma koloraro"
syncDeviceDarkMode: "Speguli la luman modon de via aparato"
drive: "Disko"
fileName: "Dosiernomo"
selectFile: "Elekti dosieron"
@ -262,7 +264,7 @@ inputNewFolderName: "Entajpu novan nomon de la dosierujo"
hasChildFilesOrFolders: "La dosierujo ne estas forviŝebla, ĉar ĝi ne malplenas."
copyUrl: "Kopii URL"
rename: "Alinomi"
avatar: "Ikono"
avatar: "Bildsimbolo"
banner: "Standardo"
nsfw: "Enhavo ne estas deca por laborejo (NSFW)"
disconnectedFromServer: "Malkonektita de servilo"
@ -277,7 +279,7 @@ normal: "Normala"
instanceName: "Nomo de la nodo"
instanceDescription: "Priskribo de la nodo "
maintainerName: "Nomo de la administranto"
maintainerEmail: "Retpoŝto de la administranto"
maintainerEmail: "Retpoŝta adreso de la administranto"
tosUrl: "URL de kondiĉoj de uzado"
thisYear: "Ĉi-jare"
thisMonth: "Ĉi-monate"
@ -296,9 +298,9 @@ enableRegistration: "Ebligi novan uzanton registriĝon"
invite: "Inviti"
driveCapacityPerLocalAccount: "Volumo de disko po unu loka 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"
backgroundImageUrl: "URL de fona bildo"
backgroundImageUrl: "URL de la fona bildo"
basicInfo: "Baza informo"
pinnedUsers: "Alpinglita uzanto"
pinnedUsersDescription: "Listigu uzantnomojn apartige en ĉiu linio por alpingli al la paĝoj ekz \"Esplori\"."
@ -328,33 +330,35 @@ silenceConfirm: "Ĉu vi certas ke vi volas mutigi la uzanton?"
unsilence: "Malmutigi"
unsilenceConfirm: "Ĉu vi certas ke vi volas malmutigi la uzanton?"
popularUsers: "Popularaj uzantoj"
recentlyUpdatedUsers: "Uzantoj kiuj lastatempe sendis noton"
recentlyRegisteredUsers: "Novaliĝintaj uzantoj"
recentlyDiscoveredUsers: "Lastatempe trovitaj uzantoj"
exploreUsersCount: "Tio estas {count} uzantoj"
recentlyUpdatedUsers: "Lastatempe afiŝintaj uzantoj"
recentlyRegisteredUsers: "Lastatempe aniĝintaj uzantoj"
recentlyDiscoveredUsers: "Lastatempe eltrovitaj uzantoj"
exploreUsersCount: "Jen {count} uzantoj"
exploreFediverse: "Esplori la Fediverson"
popularTags: "Popularaj kradvortoj"
userList: "Listoj"
about: "Informoj"
aboutMisskey: "Pri Misskey"
administrator: "Administranto"
token: "Ĵetono"
token: "Peco"
twoStepAuthentication: "Dua-faktora aŭtentiko"
moderator: "Kontrolisto"
nUsersMentioned: "{n} uzanto(j) menciis"
securityKey: "Sekureca ŝlosilo"
securityKeyName: "Nomo de la ŝlosilo"
registerSecurityKey: "Registri ŝlosilon de sekureco"
lastUsed: "Plej malnove uzita"
lastUsed: "La plej malnove uzita"
unregister: "Malregistriĝi"
passwordLessLogin: "Ensaluti sen pasvorto"
passwordLessLogin: "Saluti sen pasvorto"
resetPassword: "Restarigi pasvorton"
newPasswordIs: "La nova pasvorto estas {password}."
reduceUiAnimation: "Redukti la animacioj de la fasado"
reduceUiAnimation: "Redukti la animaciojn de la fasado"
share: "Kundividi"
notFound: "Ne trovita"
uploadFolder: "Dosierujo implicita por alŝuto"
cacheClear: "Malplenigi staplon"
markAsReadAllNotifications: "Marki ĉiujn sciigojn kiel legita"
markAsReadAllTalkMessages: "Marki ĉiujn retbabiladojn kiel legita"
help: "Manlibro de uzado"
inputMessageHere: "Entajpu masaĝo tie ĉi"
close: "Fermi"
@ -362,7 +366,7 @@ group: "Grupo"
groups: "Grupoj"
createGroup: "Krei grupon"
ownedGroups: "Administrataj grupoj"
joinedGroups: "Al grupoj kiuj vi aliĝis"
joinedGroups: "Grupoj al kiuj vi aliĝis"
invites: "Inviti"
groupName: "Grupa nomo"
members: "Membroj"
@ -377,13 +381,13 @@ retype: "Retajpu"
noteOf: "Noto de {user}"
inviteToGroup: "Inviti al grupo"
quoteAttached: "Kun citaĵo"
quoteQuestion: "Ĉu vi aldonas citaĵon?"
quoteQuestion: "Ĉu vi volas aldoni citaĵon?"
noMessagesYet: "Ankoraŭ neniu mesaĝo"
newMessageExists: "Vi ricevis novan mesaĝon."
onlyOneFileCanBeAttached: "Oni povas aldoni nur unu dosieron po mesaĝo."
signinRequired: "Bonvolu ensaluti"
signinRequired: "Bonvolu saluti"
invitations: "Inviti"
invitationCode: "Invita kodo"
invitationCode: "Kodo de invito"
available: "Disposabla"
unavailable: "Ne disponebla"
usernameInvalidFormat: "La uzantnomo povas enhavi minusklajn kaj majusklajn literojn, numerojn, nur kaj '_'."
@ -394,10 +398,11 @@ normalPassword: "Normala pasvorto"
strongPassword: "Forta pasvorto"
passwordMatched: "Konforma"
passwordNotMatched: "Nekonforma"
signinWith: "Ensaluti kun {x}"
signinWith: "Saluti kun {x}"
or: "Aŭ"
language: "Lingvo"
uiLanguage: "Lingvo de fasado"
groupInvited: "Invitita al grupo"
aboutX: "Pri {x}"
useOsNativeEmojis: "Uzi la emoĵiojn implicitan de la operaciumo"
youHaveNoGroups: "Neniuj grupoj"
@ -408,7 +413,7 @@ category: "Kategorio"
tags: "Etikedoj"
docSource: "Fonto de la dokumento"
createAccount: "Krei konton"
existingAccount: "Ekzista konto"
existingAccount: "Ekzistan konton"
regenerate: "Regeneri"
fontSize: "Tipara grando"
noFollowRequests: "Vi ne havas peto de sekvado"
@ -426,9 +431,10 @@ objectStorageBaseUrl: "Baza URL"
objectStoragePrefix: "Prefix"
objectStorageRegion: "Regiono"
objectStorageUseSSL: "Oni uzas SSL"
objectStorageUseProxy: "Uzi retperilon"
serverLogs: "Servila protokolo"
deleteAll: "Forviŝi ĉiujn"
newNoteRecived: "Jen estas novaj notoj"
newNoteRecived: "Jen novaj notoj"
sounds: "Sonoj"
listen: "Aŭdi"
none: "Neniu"
@ -467,15 +473,15 @@ enableInfiniteScroll: "Ebligi infinitan rulumon"
visibility: "Videbleco"
poll: "Balotujo"
useCw: "Kaŝi enhavo"
enablePlayer: "Vidi videon"
disablePlayer: "Fermi videon"
enablePlayer: "Vidigi la filmeton"
disablePlayer: "Malfermi la filmeton"
expandTweet: "Disvolvi pepon"
themeEditor: "Redaktilo de koloraroj"
description: "Priskribo"
describeFile: "Priskribi la bildon"
enterFileDescription: "Priskribu"
author: "Aŭtoro"
manage: "Administro"
manage: "Bonteni"
plugins: "Kromaĵoj"
deck: "Kartaro"
useFullReactionPicker: "Uzi la tuton de la elektilon de reagoj"
@ -484,13 +490,14 @@ height: "Alteco"
large: "Granda"
medium: "Meza"
small: "Malgranda"
generateAccessToken: "Generi ĵetonon de aliro"
generateAccessToken: "Generi aŭtentikigan pecon"
permission: "Permesoj"
enableAll: "Ebligi ĉiujn"
disableAll: "Malebligi ĉiujn"
notificationType: "Tipo de sciigoj"
edit: "Redakti"
emailServer: "Retpoŝta servilo"
enableEmail: "Ebligi dissendon el retpoŝto"
email: "Retpoŝto"
emailAddress: "Retpoŝta adreso"
smtpConfig: "Agordoj de SMTP servilo"
@ -513,14 +520,14 @@ create: "Krei"
notificationSetting: "Agordoj de sciigoj"
useGlobalSetting: "Oni uzas malloka agordo"
other: "Aliaj"
regenerateLoginToken: "Regeneri la ĵetonon de aliro"
regenerateLoginToken: "Regeneri la aŭtentikigan pecon"
fileIdOrUrl: "Dosiera identigilo aŭ URL"
chatOpenBehavior: "Konduto por malfermi la fenestron de babilejo"
behavior: "Konduto"
sample: "Ekzemplo"
abuseReports: "Signaloj"
reportAbuse: "Signalo"
reportAbuseOf: "Signali kontraŭ {name}'(o)"
reportAbuseOf: "Signali kontraŭ {name}"
send: "Sendi"
openInNewTab: "Malfermi en nova langeto"
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}"
random: "Hazarde"
system: "Sistemo"
switchUi: "Modifi la aspektigon"
desktop: "Labortablo"
createNew: "Krei novan"
optional: "Opciaj"
@ -563,7 +571,7 @@ wide: "Vasta"
narrow: "Malvasta"
showTitlebar: "Videbligi titolan stangon"
clearCache: "Malplenigi staplon"
onlineUsersCount: "{n} uzanto(j) estas surlinea"
onlineUsersCount: "{n} uzantoj estas surlineaj"
nUsers: "{n} uzanto(j)"
nNotes: "{n} notoj"
myTheme: "Miaj koloraroj"
@ -578,11 +586,12 @@ saveConfirm: "Ĉu vi konservas la ŝanĝon?"
deleteConfirm: "Ĉu certas forviŝi?"
closeAccount: "Forigi konton"
currentVersion: "Nuna versio"
latestVersion: "Plej nova versio"
latestVersion: "La plej nova versio"
youAreRunningUpToDateClient: "Vi uzas la plej novan version de via kliento."
newVersionOfClientAvailable: "Nova versio de via kliento estas disponebla."
inUse: "Uzata"
editCode: "Redakti kodon"
receiveAnnouncementFromInstance: "Ricevi informojn sciigintajn de la nodo"
emailNotification: "Sciigoj per retpoŝto"
inChannelSearch: "Serĉi en kanalo"
useReactionPickerForContextMenu: "Dekstre-klaki por malfermi la elektilon de reagoj"
@ -599,8 +608,9 @@ offline: "Forkonektita"
instanceBlocking: "Bloki specifajn nodojn"
selectAccount: "Elekti konton"
user: "Uzantoj"
administration: "Administro"
administration: "Bontenado"
accounts: "Kontoj"
recentPosts: "Novaj afiŝoj"
shareWithNote: "Kundividi en noto"
ads: "Reklamaĵo"
memo: "Memorigilo"
@ -617,14 +627,18 @@ troubleshooting: "Problemsolvi"
learnMore: "Lernu pli"
translate: "Traduki"
translatedFrom: "Tradukita el {x}"
breakFollow: "Ĉesigi la sekvadon al vi"
itsOn: "Ŝaltita"
unread: "Nelegita"
controlPanel: "Ŝaltpodio"
manageAccounts: "Bonteni la kontojn"
classic: "Klasika"
ffVisibility: "Videbleco pri viaj sekvataro/sekvantaro\n"
ffVisibilityDescription: "Agordi la videblecon kiu povas vidi tiujn kiujn vi sekvas kaj tiujn kiuj sekvas vin."
continueThread: "Vidi pli mesaĝarojn"
incorrectPassword: "Nevalida pasvorto"
leaveGroup: "Eliĝi el la grupo"
leaveGroupConfirm: "Ĉu vi certas ke vi volas eliĝi el la grupo {name}?"
_emailUnavailable:
used: "La retpoŝto jam estas uzita."
format: "Nevalida formato."
@ -647,12 +661,12 @@ _gallery:
like: "Ŝati"
_email:
_follow:
title: "Vi estas eksekvita"
title: "Eksekvis vin"
_receiveFollowRequest:
title: "Vi ricevis peton de sekvado"
_plugin:
install: "Instali kromaĵon"
manage: "Administri kromaĵojn"
manage: "Bonteni kromaĵojn"
_registry:
key: "Ŝlosilo"
keys: "Ŝlosiloj"
@ -699,9 +713,9 @@ _channel:
edit: "Redakti kanalon"
setBanner: "Apliki standardan bildon"
removeBanner: "Forviŝi la standardan bildon"
owned: "Posedaĵo"
owned: "Bontenitaj de vi"
following: "Sekvante"
usersCount: "{n} partoprenanto(j)"
usersCount: "{n} partoprenantoj"
_menuDisplay:
top: "Supro"
hide: "Kaŝi"
@ -711,7 +725,7 @@ _wordMute:
hard: "Per la servilo"
mutedNotes: "Silentigitaj notoj"
_theme:
manage: "Administri kolorarojn"
manage: "Bonteni kolorarojn"
code: "Kolorara kodo"
description: "Priskribo"
defaultValue: "Implicitaĵa valoro"
@ -728,19 +742,19 @@ _theme:
renote: "Plusendita"
buttonBg: "Fono de butono"
driveFolderBg: "Fono de dosierujo de la disko"
messageBg: "Fono de retbabilejo"
messageBg: "Fono de la retbabilejo"
_sfx:
note: "Nova noto"
noteMy: "Mia noto"
notification: "Sciigoj"
chat: "Retbabili"
chatBg: "Retbabili (BG)"
antenna: "Ricevo de anteno"
antenna: "Ricevo de la anteno"
channel: "Sciigoj de kanalo"
_ago:
future: "Futuro"
justNow: "Ĵus"
secondsAgo: "Antaŭ {n} sekundo(j)"
secondsAgo: "Antaŭ {n} sekundoj"
minutesAgo: "Antaŭ {n} minutoj"
hoursAgo: "Antaŭ {n} horo(j)"
daysAgo: "Antaŭ {n} tago(j)"
@ -770,8 +784,8 @@ _permissions:
"write:favorites": "Redakti vian liston de preferaĵoj"
"read:following": "Vidi la infomaciojn pri tio, kion vi sekvas"
"write:following": "Sekvi aŭ malsekvi alian uzanton"
"read:messaging": "Vidi vian retbabiladon"
"write:messaging": "Retbabilejo"
"read:messaging": "Vidi viajn retbabiladojn"
"write:messaging": "Administri viajn retbabiladojn"
"read:mutes": "Vidi vian liston de silentigitoj"
"write:mutes": "Redakti vian liston de silentigitoj"
"write:notes": "Krei / Forviŝi noton"
@ -780,6 +794,7 @@ _permissions:
"read:reactions": "Vidi reagojn"
"write:reactions": "Redakti viajn reagojn"
"read:page-likes": "Vidi ŝatojn de paĝo"
"read:user-groups": "Vidi viajn grupojn de uzantoj"
"read:channels": "Vidi kanalojn"
_antennaSources:
all: "Ĉiuj notoj"
@ -827,23 +842,23 @@ _visibility:
_postForm:
replyPlaceholder: "Respondi la noton…"
quotePlaceholder: "Citi la noton…"
channelPlaceholder: "Mencii en kanalo…"
channelPlaceholder: "Mencii en la kanalo…"
_profile:
name: "Nomo"
username: "Uzantnomo"
description: "Sinprezento"
metadata: "Kromaj informoj"
metadataEdit: "Redakti kromaj informoj"
metadataEdit: "Redakti kromajn informojn"
changeAvatar: "Ŝanĝi profilbildon"
changeBanner: "Ŝanĝi standardon"
_exportOrImport:
allNotes: "Ĉiuj notoj"
followingList: "Sekvataj uzantoj"
followingList: "Sekvatoj"
muteList: "Silentigoj"
blockingList: "Blokitoj"
userLists: "Listoj"
_charts:
federationInstancesTotal: "La totala nombro de nodoj kunfederantaj"
federationInstancesTotal: "La totala nombro de nodoj federantaj"
usersTotal: "La totala nombro de la uzantoj"
activeUsers: "La nombro de la uzantoj aktivaj"
notesTotal: "La totala nombro de notoj"
@ -890,6 +905,7 @@ _pages:
title: "Temlinio"
url: "URL de paĝo"
alignCenter: "Centrigi"
hideTitleWhenPinned: "Kaŝi la titolon de la paĝo kiam alpinglita"
chooseBlock: "Aldoni blokon"
contentBlocks: "Enhavo"
inputBlocks: "Enigo"
@ -914,6 +930,7 @@ _pages:
default: "Implicitaĵa valoro"
_canvas:
id: "Kanvasa identigilo"
width: "Larĝeco"
_note:
id: "Identigilo de noto"
_switch:
@ -924,8 +941,11 @@ _pages:
_button:
text: "Titolo"
_action:
_dialog:
content: "Enhavo"
_pushEvent:
event: "Nomo de la evento"
no-variable: "Neniu"
_radioButton:
title: "Titolo"
default: "Implicitaĵa valoro"
@ -986,11 +1006,12 @@ _notification:
youGotQuote: "{name} citis"
youRenoted: "{name} plusendis"
youGotPoll: "{name} balotis"
youGotMessagingMessageFromUser: "{name} sentis mesaĝon al vi."
youGotMessagingMessageFromGroup: "Retbabilan mesaĝon oni sendis al la grupo {name}"
youGotMessagingMessageFromUser: "{name} sendis al vi mesaĝon"
youGotMessagingMessageFromGroup: "Oni sendis al la grupo {name} mesaĝon"
youWereFollowed: "eksekvis vin"
youReceivedFollowRequest: "Vi ricevis peton de sekvado"
yourFollowRequestAccepted: "Via peto de sekvado estis akceptita."
youWereInvitedToGroup: "Invitita al grupo"
_types:
all: "Ĉio"
follow: "Novaj sekvatoj"
@ -1001,6 +1022,7 @@ _notification:
reaction: "Reagoj"
receiveFollowRequest: "Ricevi peton de sekvado"
followRequestAccepted: "Akceptita peto por sekvado"
groupInvited: "Invitita al grupo"
_deck:
profile: "Agordaro"
_columns:

View File

@ -104,7 +104,6 @@ clickToShow: "Click para ver"
sensitive: "Marcado como sensible"
add: "Agregar"
reaction: "Reacción"
reactionSettingDescription: "Asigne sus reacción favoritas que desean anclar en el selector de reacciones."
reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir."
rememberNoteVisibility: "Recordar visibilidad"
attachCancel: "Quitar adjunto"
@ -738,6 +737,7 @@ lastCommunication: "Última comunicación"
resolved: "Resuelto"
unresolved: "Sin resolver"
controlPanel: "Panel de control"
hide: "Ocultar"
_accountDelete:
accountDelete: "Eliminar Cuenta"
_ad:

View File

@ -106,7 +106,6 @@ clickToShow: "Cliquer pour afficher"
sensitive: "Contenu sensible"
add: "Ajouter"
reaction: "Réactions"
reactionSettingDescription: "Choisissez vos réactions préférées que vous souhaitez épingler dans le sélecteur de réactions."
reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter."
rememberNoteVisibility: "Activer l'option \" se souvenir de la visibilité des notes \" vous permet de réutiliser automatiquement la visibilité utilisée lors de la publication de votre note précédente."
attachCancel: "Supprimer le fichier attaché"
@ -798,6 +797,7 @@ filter: "Filtre"
controlPanel: "Panneau de contrôle"
manageAccounts: "Gérer les comptes"
classic: "Classique"
hide: "Masquer"
_emailUnavailable:
format: "Le format de cette adresse de courriel est invalide"
mx: "Ce serveur de courriels est invalide"

View File

@ -106,7 +106,6 @@ clickToShow: "Klik untuk melihat"
sensitive: "Konten sensitif"
add: "Tambahkan"
reaction: "Reaksi"
reactionSettingDescription: "Masukkan reaksi favorit yang ingin kamu sematkan pada bilah reaksi"
reactionSettingDescription2: "Geser untuk memindah urutkan, klik untuk menghapus, tekan \"+\" untuk menambahkan"
rememberNoteVisibility: "Ingat pengaturan visibilitas catatan"
attachCancel: "Hapus lampiran"
@ -810,6 +809,7 @@ continueThread: "Lihat lanjutan thread"
deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?"
incorrectPassword: "Kata sandi salah."
voteConfirm: "Konfirmasi suara kamu untuk ({choice})"
hide: "Sembunyikan"
_emailUnavailable:
used: "Alamat surel ini telah digunakan"
format: "Format tidak valid."

View File

@ -46,7 +46,10 @@ const primaries = {
'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)
.reduce((a, [k ,v]) => (a[k] = (() => {

View File

@ -103,7 +103,6 @@ clickToShow: "Clicca per visualizzare"
sensitive: "Contenuto sensibile"
add: "Aggiungi"
reaction: "Reazione"
reactionSettingDescription: "Scegli le reazioni che preferisci e fissale nel pannello di reazioni."
reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
attachCancel: "Rimuovi allegato"
@ -745,6 +744,7 @@ global: "Federata"
sent: "Inviare"
hashtags: "Hashtag"
troubleshooting: "Risoluzione problemi"
hide: "Nascondere"
_ffVisibility:
public: "Pubblico"
_ad:

View File

@ -106,7 +106,7 @@ clickToShow: "クリックして表示"
sensitive: "閲覧注意"
add: "追加"
reaction: "リアクション"
reactionSettingDescription: "リアクションピッカーに表示するリアクションを設定します。"
reactionSetting: "ピッカーに表示するリアクション"
reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。"
rememberNoteVisibility: "公開範囲を記憶する"
attachCancel: "添付取り消し"
@ -448,6 +448,7 @@ uiLanguage: "UIの表示言語"
groupInvited: "グループに招待されました"
aboutX: "{x}について"
useOsNativeEmojis: "OSネイティブの絵文字を使用"
disableDrawer: "メニューをドロワーで表示しない"
youHaveNoGroups: "グループがありません"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
noHistory: "履歴はありません"
@ -613,7 +614,6 @@ regenerateLoginToken: "ログイントークンを再生成"
regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。"
setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
fileIdOrUrl: "ファイルIDまたはURL"
chatOpenBehavior: "チャットを開くときの動作"
behavior: "動作"
sample: "サンプル"
abuseReports: "通報"
@ -685,6 +685,7 @@ center: "中央"
wide: "広い"
narrow: "狭い"
reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
needReloadToApply: "反映には再起動が必要です。"
showTitlebar: "タイトルバーを表示する"
clearCache: "キャッシュをクリア"
onlineUsersCount: "{n}人がオンライン"
@ -813,6 +814,11 @@ deleteAccountConfirm: "アカウントが削除されます。よろしいです
incorrectPassword: "パスワードが間違っています。"
voteConfirm: "「{choice}」に投票しますか?"
hide: "隠す"
leaveGroup: "グループから抜ける"
leaveGroupConfirm: "「{name}」から抜けますか?"
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
welcomeBackWithName: "おかえりなさい、{name}さん"
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
_emailUnavailable:
used: "既に使用されています"

View File

@ -104,7 +104,6 @@ clickToShow: "押したら見えるで"
sensitive: "ちょっとアカンやつやで"
add: "増やす"
reaction: "リアクション"
reactionSettingDescription: "リアクションピッカーに出しとくリアクションを選んでや。"
reactionSettingDescription2: "ドラッグで並び替え、クリックで削除、+を押して追加やで。"
rememberNoteVisibility: "公開範囲覚えといて"
attachCancel: "のっけるのやめる"
@ -204,7 +203,7 @@ noJobs: "ジョブはあらへん"
federating: "連合しとる"
blocked: "ブロックしとる"
suspended: "配信せぇへん"
all: "みな"
all: "みな"
subscribing: "購読しとる"
publishing: "配信しとる"
notResponding: "応答してへんで"
@ -653,6 +652,7 @@ low: "低い"
global: "グローバル"
sent: "送信"
hashtags: "ハッシュタグ"
hide: "隠す"
_ad:
back: "戻る"
_gallery:
@ -848,7 +848,7 @@ _permissions:
_auth:
permissionAsk: "このアプリは次の権限を要求しとるで"
_antennaSources:
all: "みなのノート"
all: "みなのノート"
homeTimeline: "フォローしとるユーザーのノート"
_weekday:
sunday: "日曜日"
@ -896,7 +896,7 @@ _poll:
votesCount: "{n}票"
vote: "投票する"
_visibility:
publicDescription: "みのユーザーに公開"
publicDescription: "みなに公開"
home: "ホーム"
followers: "フォロワー"
_profile:

View File

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

View File

@ -106,7 +106,6 @@ clickToShow: "Klik om te bekijken"
sensitive: "NSFW"
add: "Toevoegen"
reaction: "Reacties"
reactionSettingDescription: "Configureer welke reacties je wilt weergeven in de reactiekiezer."
reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, Druk op \"+\" om toe te voegen"
rememberNoteVisibility: "Vergeet niet de notitie zichtbaarheidsinstellingen"
attachCancel: "Verwijder bijlage"
@ -120,16 +119,144 @@ unblock: "Deblokkeren"
suspend: "Opschorten"
unsuspend: "Heractiveren"
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"
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"
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"
pinnedNotes: "Vastgemaakte notitie"
userList: "Lijsten"
smtpHost: "Server"
smtpUser: "Gebruikersnaam"
smtpPass: "Wachtwoord"
clearCache: "Cache opschonen"
user: "Gebruikers"
muteThread: "Discussies dempen "
unmuteThread: "Dempen van discussie ongedaan maken"
hide: "Verbergen"
_email:
_follow:
title: "volgde jou"
@ -144,12 +271,17 @@ _theme:
_sfx:
note: "Notities"
notification: "Meldingen"
chat: "Chat"
_widgets:
notifications: "Meldingen"
timeline: "Tijdlijn"
activity: "Activiteit"
federation: "Federatie"
jobQueue: "Job Queue"
_cw:
show: "Laad meer"
_visibility:
home: "Startpagina"
followers: "Volgers"
_profile:
username: "Gebruikersnaam"
@ -158,7 +290,18 @@ _exportOrImport:
muteList: "Dempen"
blockingList: "Blokkeren"
userLists: "Lijsten"
excludeMutingUsers: "Negeer gedempte gebruikers"
excludeInactiveUsers: "Negeer inactieve gebruikers"
_timelines:
home: "Startpagina"
_rooms:
_roomType:
default: "Standaard"
_furnitures:
monitor: "Monitor"
_pages:
blocks:
image: "Afbeeldingen"
script:
categories:
list: "Lijsten"

View File

@ -104,7 +104,6 @@ clickToShow: "Kliknij, aby wyświetlić"
sensitive: "NSFW"
add: "Dodaj"
reaction: "Reakcja"
reactionSettingDescription: "Przypisz swoje ulubione reakcje, które chcesz przypiąć w wyborze reakcji."
reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać"
rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu"
attachCancel: "Usuń załącznik"
@ -738,6 +737,7 @@ ratio: "Stosunek"
global: "Globalna"
sent: "Wyślij"
hashtags: "Hashtag"
hide: "Ukryj"
_ffVisibility:
public: "Publikuj"
_ad:

View File

@ -106,7 +106,6 @@ clickToShow: "Нажмите для просмотра"
sensitive: "Содержимое не для всех"
add: "Добавить"
reaction: "Реакции"
reactionSettingDescription: "Подберите, что будет у вас в палитре реакций"
reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»."
rememberNoteVisibility: "Запоминать видимость заметок"
attachCancel: "Удалить вложение"
@ -802,6 +801,8 @@ makeReactionsPublicDescription: "Список сделанных вами реа
classic: "Классика"
unmuteThread: "Отключить звук"
ffVisibilityDescription: "Вы можете установить объем вашей следующей/последней информации."
hide: "Спрятать"
leaveGroupConfirm: "Вы хотите оставить \"{name}\"?"
_emailUnavailable:
used: "Уже используется"
format: "Неправильный формат"
@ -923,6 +924,7 @@ _mfm:
sparkle: "Блеск"
sparkleDescription: "Добавьте эффект искрящихся частиц."
rotate: "Повернуть"
rotateDescription: "Повернуть на указанный угол."
_reversi:
reversi: "Реверси"
gameSettings: "Настройки игры"

View File

@ -103,7 +103,6 @@ clickToShow: "Натисніть для перегляду"
sensitive: "NSFW"
add: "Додати"
reaction: "Реакції"
reactionSettingDescription: "Виберіть свої улюблені реакції, які хочете закріпити в селекторі реакцій."
reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати."
rememberNoteVisibility: "Пам’ятати параметри видимісті"
attachCancel: "Видалити вкладення"
@ -692,6 +691,7 @@ middle: "Середній"
global: "Глобальна"
sent: "Відправити"
hashtags: "Хештеґ"
hide: "Сховати"
_ad:
back: "Назад"
_gallery:

View File

@ -106,7 +106,7 @@ clickToShow: "点击以显示"
sensitive: "敏感内容"
add: "添加"
reaction: "回应"
reactionSettingDescription: "选择您想要置顶的回应。"
reactionSetting: "在选择器中显示的回应"
reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。"
rememberNoteVisibility: "保存上次设置的可见性"
attachCancel: "删除附件"
@ -592,6 +592,7 @@ smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS"
smtpSecureInfo: "使用STARTTLS时关闭。"
testEmail: "邮件发送测试"
wordMute: "文字屏蔽"
instanceMute: "实例的屏蔽"
userSaysSomething: "{name}说了什么"
makeActive: "启用"
display: "显示"
@ -811,6 +812,10 @@ continueThread: "查看更多帖子"
deleteAccountConfirm: "将要删除账户。是否确认?"
incorrectPassword: "密码错误"
voteConfirm: "确定投给“{choice}” "
hide: "隐藏"
leaveGroup: "离开群组"
leaveGroupConfirm: "确定离开「{name}」?"
useDrawerReactionPickerForMobile: "在移动设备上使用抽屉显示"
_emailUnavailable:
used: "已经被使用过"
format: "无效的格式"
@ -1001,6 +1006,11 @@ _wordMute:
soft: "软屏蔽"
hard: "硬屏蔽"
mutedNotes: "被屏蔽的帖子"
_instanceMute:
instanceMuteDescription: "屏蔽配置实例中的所有帖子和转帖,包括实例的用户回复。"
instanceMuteDescription2: "设置时用换行符来分隔"
title: "隐藏实例已设置的帖子。"
heading: "屏蔽实例"
_theme:
explore: "寻找主题"
install: "安装主题"
@ -1274,6 +1284,8 @@ _exportOrImport:
muteList: "屏蔽"
blockingList: "拉黑"
userLists: "列表"
excludeMutingUsers: "排除屏蔽用户"
excludeInactiveUsers: "排除不活跃用户"
_charts:
federationInstancesIncDec: "联合:增加/减少"
federationInstancesTotal: "联合总数"

View File

@ -104,7 +104,6 @@ clickToShow: "按一下以顯示"
sensitive: "敏感內容"
add: "新增"
reaction: "情感"
reactionSettingDescription: "置頂「反應」表情符號\n"
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
rememberNoteVisibility: "記住貼文可見性"
attachCancel: "移除附件"
@ -754,6 +753,7 @@ ratio: "%"
global: "公開"
sent: "發送"
hashtags: "#tag"
hide: "隱藏"
_ffVisibility:
public: "發佈"
_ad:

View File

@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "12.98.0",
"version": "12.100.2",
"codename": "indigo",
"repository": {
"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",
"stringz": "2.1.0",
"style-loader": "3.3.1",
"summaly": "2.4.1",
"summaly": "2.5.0",
"syslog-pro": "1.0.0",
"systeminformation": "5.9.9",
"throttle-debounce": "3.0.1",

View File

@ -1,2 +1,47 @@
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
// ブラウザで直接表示することを許可するファイルの種類のリスト
// ここに含まれないものは application/octet-stream としてレスポンスされる
// SVGはXSSを生むので許可しない
export const FILE_TYPE_BROWSERSAFE = [
// Images
'image/png',
'image/gif',
'image/jpeg',
'image/webp',
'image/apng',
'image/bmp',
'image/tiff',
'image/x-icon',
// OggS
'audio/opus',
'video/ogg',
'audio/ogg',
'application/ogg',
// ISO/IEC base media file format
'video/quicktime',
'video/mp4',
'audio/mp4',
'video/x-m4v',
'audio/x-m4a',
'video/3gpp',
'video/3gpp2',
'video/mpeg',
'audio/mpeg',
'video/webm',
'audio/webm',
'audio/aac',
'audio/x-flac',
'audio/vnd.wave',
];
/*
https://github.com/sindresorhus/file-type/blob/main/supported.js
https://github.com/sindresorhus/file-type/blob/main/core.js
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
*/

View File

@ -3,10 +3,10 @@ const types = require('pg').types;
types.setTypeParser(20, Number);
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 config from '@/config/index';
import { dbLogger } from './logger';
import { User } from '@/models/entities/user';
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 { UserPending } from '@/models/entities/user-pending';
import { entities as charts } from '@/services/chart/entities';
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
class MyCustomLogger implements Logger {
@ -175,7 +177,7 @@ export const entities = [
Ad,
PasswordResetRequest,
UserPending,
...charts as any,
...charts,
];
export function initDb(justBorrow = false, sync = false, forceRecreate = false) {

View File

@ -99,7 +99,10 @@ export async function getFileInfo(path: string): Promise<FileInfo> {
/**
* Detect MIME Type and extension
*/
export async function detectType(path: string) {
export async function detectType(path: string): Promise<{
mime: string;
ext: string | null;
}> {
// Check 0 byte
const fileSize = await getFileSize(path);
if (fileSize === 0) {

View File

@ -39,16 +39,20 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
const metaStream = fs.createWriteStream(metaPath, { flags: 'a' });
await new Promise<void>((res, rej) => {
metaStream.write('[', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
const writeMeta = (text: string): Promise<void> => {
return new Promise<void>((res, rej) => {
metaStream.write(text, err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
});
};
await writeMeta(`{"metaVersion":1,"emojis":[`);
const customEmojis = await Emojis.find({
where: {
@ -72,34 +76,17 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
logger.error(e);
}
await new Promise<void>((res, rej) => {
const content = JSON.stringify({
id: exportId,
downloaded: downloaded,
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 content = JSON.stringify({
id: exportId,
downloaded: downloaded,
emoji: emoji,
});
const isFirst = customEmojis.indexOf(emoji) === 0;
await writeMeta(isFirst ? content : ',\n' + content);
}
await new Promise<void>((res, rej) => {
metaStream.write(']', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
await writeMeta(']}');
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' });
await new Promise<void>((res, rej) => {
stream.write('[', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
const write = (text: string): Promise<void> => {
return new Promise<void>((res, rej) => {
stream.write(text, err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
});
};
await write('[');
let exportedNotesCount = 0;
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 });
}
const content = JSON.stringify(serialize(note, poll));
await new Promise<void>((res, rej) => {
const isFirst = exportedNotesCount === 0;
stream.write(isFirst ? content : ',\n' + content, err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
const isFirst = exportedNotesCount === 0;
await write(isFirst ? content : ',\n' + content);
exportedNotesCount++;
}
@ -94,16 +89,7 @@ export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Prom
job.progress(exportedNotesCount / total);
}
await new Promise<void>((res, rej) => {
stream.write(']', err => {
if (err) {
logger.error(err);
rej(err);
} else {
res();
}
});
});
await write(']');
stream.end();
logger.succ(`Exported to: ${path}`);

View File

@ -14,6 +14,7 @@ import { detectType } from '@/misc/get-file-info';
import { convertToJpeg, convertToPngOrJpeg } from '@/services/drive/image-processor';
import { GenerateVideoThumbnail } from '@/services/drive/generate-video-thumbnail';
import { StatusError } from '@/misc/fetch';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
//const _filename = fileURLToPath(import.meta.url);
const _filename = __filename;
@ -27,6 +28,7 @@ const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void =>
ctx.set('Cache-Control', 'max-age=300');
};
// eslint-disable-next-line import/no-default-export
export default async function(ctx: Koa.Context) {
const key = ctx.params.key;
@ -81,7 +83,7 @@ export default async function(ctx: Koa.Context) {
const image = await convertFile();
ctx.body = image.data;
ctx.set('Content-Type', image.type);
ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
ctx.set('Cache-Control', 'max-age=31536000, immutable');
} catch (e) {
serverLogger.error(`${e}`);
@ -112,14 +114,14 @@ export default async function(ctx: Koa.Context) {
}).toString();
ctx.body = InternalStorage.read(key);
ctx.set('Content-Type', mime);
ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(mime) ? mime : 'application/octet-stream');
ctx.set('Cache-Control', 'max-age=31536000, immutable');
ctx.set('Content-Disposition', contentDisposition('inline', filename));
} else {
const readable = InternalStorage.read(file.accessKey!);
readable.on('error', commonReadableHandlerGenerator(ctx));
ctx.body = readable;
ctx.set('Content-Type', file.type);
ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.type) ? file.type : 'application/octet-stream');
ctx.set('Cache-Control', 'max-age=31536000, immutable');
ctx.set('Content-Disposition', contentDisposition('inline', file.name));
}

View File

@ -6,6 +6,7 @@ import { createTemp } from '@/misc/create-temp';
import { downloadUrl } from '@/misc/download-url';
import { detectType } from '@/misc/get-file-info';
import { StatusError } from '@/misc/fetch';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
export async function proxyMedia(ctx: Koa.Context) {
const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url;
@ -18,7 +19,7 @@ export async function proxyMedia(ctx: Koa.Context) {
const { mime, ext } = await detectType(path);
if (!mime.startsWith('image/')) throw 403;
if (!FILE_TYPE_BROWSERSAFE.includes(mime)) throw 403;
let image: IImage;

View File

@ -4,6 +4,7 @@ block vars
- const user = note.user;
- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
- const url = `${config.url}/notes/${note.id}`;
- const isRenote = note.renote && note.text == null && note.fileIds.length == 0 && note.poll == null;
block title
= `${title} | ${instanceName}`
@ -19,7 +20,7 @@ block og
meta(property='og:image' content= user.avatarUrl)
block meta
if user.host || profile.noCrawle
if user.host || isRenote || profile.noCrawle
meta(name='robots' content='noindex')
meta(name='misskey:user-username' content=user.username)

View File

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

View File

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core';
import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema';
import { DriveFiles } from '@/models/index';
import { Not, IsNull } from 'typeorm';
import { DriveFile } from '@/models/entities/drive-file';
import { name, schema } from '../schemas/drive';
import { name, schema } from './entities/drive';
type DriveLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class DriveChart extends Chart<DriveLog> {
constructor() {
super(name, schema);
@ -71,7 +75,7 @@ export default class DriveChart extends Chart<DriveLog> {
}
@autobind
public async update(file: DriveFile, isAdditional: boolean) {
public async update(file: DriveFile, isAdditional: boolean): Promise<void> {
const update: Obj = {};
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 = {
type: 'object' 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 = {
/**
*
@ -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 = {
type: 'object' 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 = {
type: 'object' 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 = {
type: 'object' 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 = {
type: 'object' 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 = {
total: {
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 = {
type: 'object' 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 = {
type: 'object' 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: {
type: 'number' as const,
@ -8,9 +12,6 @@ export const logSchema = {
},
};
/**
*
*/
export const schema = {
type: 'object' 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 = {
type: 'object' 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 = {
type: 'object' 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 = {
type: 'object' 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 = {
/**
*
@ -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 Chart, { Obj, DeepPartial } from '../../core';
import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema';
import { Instances } from '@/models/index';
import { name, schema } from '../schemas/federation';
import { name, schema } from './entities/federation';
type FederationLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class FederationChart extends Chart<FederationLog> {
constructor() {
super(name, schema);
@ -45,7 +49,7 @@ export default class FederationChart extends Chart<FederationLog> {
}
@autobind
public async update(isAdditional: boolean) {
public async update(isAdditional: boolean): Promise<void> {
const update: Obj = {};
update.total = isAdditional ? 1 : -1;

View File

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

View File

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

View File

@ -1,10 +1,14 @@
import autobind from 'autobind-decorator';
import Chart, { DeepPartial } from '../../core';
import Chart, { DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema';
import { name, schema } from '../schemas/network';
import { name, schema } from './entities/network';
type NetworkLog = SchemaType<typeof schema>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class NetworkChart extends Chart<NetworkLog> {
constructor() {
super(name, schema);
@ -32,7 +36,7 @@ export default class NetworkChart extends Chart<NetworkLog> {
}
@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> = {
incomingRequests: incomingRequests,
totalTime: time,

View File

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

View File

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

View File

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core';
import Chart, { Obj, DeepPartial } from '../core';
import { SchemaType } from '@/misc/schema';
import { Followings, Users } from '@/models/index';
import { Not, IsNull } from 'typeorm';
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>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
constructor() {
super(name, schema, true);
@ -100,7 +104,7 @@ export default class PerUserFollowingChart extends Chart<PerUserFollowingLog> {
}
@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 = {};
update.total = isFollow ? 1 : -1;

View File

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator';
import Chart, { Obj, DeepPartial } from '../../core';
import Chart, { Obj, DeepPartial } from '../core';
import { User } from '@/models/entities/user';
import { SchemaType } from '@/misc/schema';
import { Notes } from '@/models/index';
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>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
constructor() {
super(name, schema, true);
@ -46,7 +50,7 @@ export default class PerUserNotesChart extends Chart<PerUserNotesLog> {
}
@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 = {
diffs: {},
};

View File

@ -1,13 +1,17 @@
import autobind from 'autobind-decorator';
import Chart, { DeepPartial } from '../../core';
import Chart, { DeepPartial } from '../core';
import { User } from '@/models/entities/user';
import { Note } from '@/models/entities/note';
import { SchemaType } from '@/misc/schema';
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>;
/**
*
*/
// eslint-disable-next-line import/no-default-export
export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
constructor() {
super(name, schema, true);
@ -36,7 +40,7 @@ export default class PerUserReactionsChart extends Chart<PerUserReactionsLog> {
}
@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({
[Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 },
}, note.userId);

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ type Log = {
/**
*
*/
group: string | null;
group?: string | null;
/**
* Unixタイムスタンプ()
@ -38,7 +38,7 @@ type Log = {
date: number;
};
const camelToSnake = (str: string) => {
const camelToSnake = (str: string): string => {
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>> {
private static readonly columnPrefix = '___';
private static readonly columnDot = '_';
@ -57,7 +58,8 @@ export default abstract class Chart<T extends Record<string, any>> {
group: string | null;
}[] = [];
public schema: SimpleSchema;
protected repository: Repository<Log>;
protected repositoryForHour: Repository<Log>;
protected repositoryForDay: Repository<Log>;
protected abstract genNewLog(latest: T): DeepPartial<T>;
@ -181,9 +183,15 @@ export default abstract class Chart<T extends Record<string, any>> {
}
@autobind
public static schemaToEntity(name: string, schema: SimpleSchema): EntitySchema {
return new EntitySchema({
name: `__chart__${camelToSnake(name)}`,
public static schemaToEntity(name: string, schema: SimpleSchema, grouped = false): {
hour: EntitySchema,
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: {
id: {
type: 'integer',
@ -193,37 +201,45 @@ export default abstract class Chart<T extends Record<string, any>> {
date: {
type: 'integer',
},
group: {
type: 'varchar',
length: 128,
nullable: true,
},
...(grouped ? {
group: {
type: 'varchar',
length: 128,
},
} : {}),
...Chart.convertSchemaToFlatColumnDefinitions(schema),
},
indices: [{
columns: ['date', 'group'],
columns: grouped ? ['date', 'group'] : ['date'],
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) {
this.name = name;
this.schema = schema;
const entity = Chart.schemaToEntity(name, schema);
const keys = ['date'];
if (grouped) keys.push('group');
entity.options.uniques = [{
columns: keys,
}];
this.repository = getRepository<Log>(entity);
const { hour, day } = Chart.schemaToEntity(name, schema, grouped);
this.repositoryForHour = getRepository<Log>(hour);
this.repositoryForDay = getRepository<Log>(day);
}
@autobind
@ -247,24 +263,40 @@ export default abstract class Chart<T extends Record<string, any>> {
}
@autobind
private getLatestLog(group: string | null = null): Promise<Log | null> {
return this.repository.findOne({
private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise<Log | null> {
const repository =
span === 'hour' ? this.repositoryForHour :
span === 'day' ? this.repositoryForDay :
new Error('not happen') as never;
return repository.findOne(group ? {
group: group,
}, {
} : {}, {
order: {
date: -1,
},
}).then(x => x || null);
}
/**
* (=Hour or Day)
*/
@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 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 currentLog = await this.repository.findOne({
const repository =
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),
...(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) {
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);
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 lockKey = `${this.name}:${date}:${group}`;
const lockKey = group ? `${this.name}:${date}:${span}:${group}` : `${this.name}:${date}:${span}`;
const unlock = await getChartInsertLock(lockKey);
try {
// ロック内でもう1回チェックする
const currentLog = await this.repository.findOne({
const currentLog = await repository.findOne({
date: date,
...(group ? { group: group } : {}),
});
@ -315,13 +347,13 @@ export default abstract class Chart<T extends Record<string, any>> {
if (currentLog != null) return currentLog;
// 新規ログ挿入
log = await this.repository.insert({
group: group,
log = await repository.insert({
date: date,
...(group ? { group: group } : {}),
...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;
} finally {
@ -349,10 +381,10 @@ export default abstract class Chart<T extends Record<string, any>> {
// そのログは本来は 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[]>;
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);
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);
// ログ更新
await this.repository.createQueryBuilder()
.update()
.set(query)
.where('id = :id', { id: log.id })
.execute();
await Promise.all([
this.repositoryForHour.createQueryBuilder()
.update()
.set(query)
.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に入ったものは消さないようにする
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));
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
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 update = async (log: Log) => {
await this.repository.createQueryBuilder()
.update()
.set(Chart.convertObjectToFlattenColumns(data))
.where('id = :id', { id: log.id })
.execute();
const update = async (logHour: Log, logDay: Log): Promise<void> => {
await Promise.all([
this.repositoryForHour.createQueryBuilder()
.update()
.set(Chart.convertObjectToFlattenColumns(data))
.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
@ -418,13 +474,18 @@ export default abstract class Chart<T extends Record<string, any>> {
const gt =
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') :
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: {
group: group,
date: Between(Chart.dateToTimestamp(gt), Chart.dateToTimestamp(lt)),
...(group ? { group: group } : {}),
},
order: {
date: -1,
@ -435,9 +496,9 @@ export default abstract class Chart<T extends Record<string, any>> {
if (logs.length === 0) {
// もっとも新しいログを持ってくる
// (すくなくともひとつログが無いと隙間埋めできないため)
const recentLog = await this.repository.findOne({
const recentLog = await repository.findOne(group ? {
group: group,
}, {
} : {}, {
order: {
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)) {
// 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する
// (隙間埋めできないため)
const outdatedLog = await this.repository.findOne({
group: group,
const outdatedLog = await repository.findOne({
date: LessThan(Chart.dateToTimestamp(gt)),
...(group ? { group: group } : {}),
}, {
order: {
date: -1,
@ -467,60 +528,26 @@ export default abstract class Chart<T extends Record<string, any>> {
const chart: T[] = [];
if (span === 'hour') {
for (let i = (amount - 1); i >= 0; i--) {
const current = subtractTime(dateUTC([y, m, d, h]), i, 'hour');
for (let i = (amount - 1); i >= 0; i--) {
const current =
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) {
const data = Chart.convertFlattenColumnsToObject(log);
chart.unshift(Chart.countUniqueFields(data) as T);
} else {
// 隙間埋め
const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current));
const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null;
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);
if (log) {
const data = Chart.convertFlattenColumnsToObject(log);
chart.unshift(Chart.countUniqueFields(data) as T);
} else {
// 隙間埋め
const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current));
const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null;
chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) 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 }]
@ -528,7 +555,7 @@ export default abstract class Chart<T extends Record<string, any>> {
* { 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)) {
const p = path ? `${path}.${k}` : k;
if (typeof v === 'object' && !Array.isArray(v)) {
@ -542,7 +569,7 @@ export default abstract class Chart<T extends Record<string, any>> {
compact(chart[0]);
return res;
return res as ArrayValue<T>;
}
}

View File

@ -1,15 +1,27 @@
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import Chart from './core';
import { entity as FederationChart } from './charts/entities/federation';
import { entity as NotesChart } from './charts/entities/notes';
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);
const _filename = __filename;
const _dirname = dirname(_filename);
export const entities = Object.values(require('require-all')({
dirname: _dirname + '/charts/schemas',
filter: /^.+\.[jt]s$/,
resolve: (x: any) => {
return Chart.schemaToEntity(x.name, x.schema);
},
}));
export const entities = [
FederationChart.hour, FederationChart.day,
NotesChart.hour, NotesChart.day,
UsersChart.hour, UsersChart.day,
NetworkChart.hour, NetworkChart.day,
ActiveUsersChart.hour, ActiveUsersChart.day,
InstanceChart.hour, InstanceChart.day,
PerUserNotesChart.hour, PerUserNotesChart.day,
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 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 notesChart = new NotesChart();
export const usersChart = new UsersChart();

View File

@ -20,6 +20,7 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error';
import * as S3 from 'aws-sdk/clients/s3';
import { getS3 } from './s3';
import * as sharp from 'sharp';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
const logger = driveLogger.createSubLogger('register', 'yellow');
@ -241,6 +242,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
*/
async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) {
if (type === 'image/apng') type = 'image/png';
if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream';
const meta = await fetchMeta();

View File

@ -3,10 +3,33 @@ import config from '@/config/index';
import { SwSubscriptions } from '@/models/index';
import { fetchMeta } from '@/misc/fetch-meta';
import { Packed } from '@/misc/schema';
import { getNoteSummary } from '@/misc/get-note-summary';
type notificationType = 'notification' | 'unreadMessagingMessage';
type notificationBody = Packed<'Notification'> | Packed<'MessagingMessage'>;
// プッシュメッセージサーバーには文字数制限があるため、内容を削減します
function truncateNotification(notification: Packed<'Notification'>): any {
if (notification.note) {
return {
...notification,
note: {
...notification.note,
// textをgetNoteSummaryしたものに置き換える
text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note),
...{
cw: undefined,
reply: undefined,
renote: undefined,
user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる
}
}
};
}
return notification;
}
export default async function(userId: string, type: notificationType, body: notificationBody) {
const meta = await fetchMeta();
@ -32,7 +55,9 @@ export default async function(userId: string, type: notificationType, body: noti
};
push.sendNotification(pushSubscription, JSON.stringify({
type, body,
type,
body: type === 'notification' ? truncateNotification(body as Packed<'Notification'>) : body,
userId,
}), {
proxy: config.proxy,
}).catch((err: any) => {

View File

@ -3,13 +3,12 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import * as lolex from '@sinonjs/fake-timers';
import { async, initTestDb } from './utils';
import TestChart from '../src/services/chart/charts/classes/test';
import TestGroupedChart from '../src/services/chart/charts/classes/test-grouped';
import TestUniqueChart from '../src/services/chart/charts/classes/test-unique';
import * as _TestChart from '../src/services/chart/charts/schemas/test';
import * as _TestGroupedChart from '../src/services/chart/charts/schemas/test-grouped';
import * as _TestUniqueChart from '../src/services/chart/charts/schemas/test-unique';
import Chart from '../src/services/chart/core';
import TestChart from '../src/services/chart/charts/test';
import TestGroupedChart from '../src/services/chart/charts/test-grouped';
import TestUniqueChart from '../src/services/chart/charts/test-unique';
import * as _TestChart from '../src/services/chart/charts/entities/test';
import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped';
import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique';
describe('Chart', () => {
let testChart: TestChart;
@ -19,9 +18,9 @@ describe('Chart', () => {
beforeEach(async(async () => {
await initTestDb(false, [
Chart.schemaToEntity(_TestChart.name, _TestChart.schema),
Chart.schemaToEntity(_TestGroupedChart.name, _TestGroupedChart.schema),
Chart.schemaToEntity(_TestUniqueChart.name, _TestUniqueChart.schema)
_TestChart.entity.hour, _TestChart.entity.day,
_TestGroupedChart.entity.hour, _TestGroupedChart.entity.day,
_TestUniqueChart.entity.hour, _TestUniqueChart.entity.day,
]);
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 () => {
await testChart.increment();
await testChart.save();

View File

@ -188,6 +188,11 @@
node-fetch "^2.6.1"
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":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4"
@ -314,13 +319,6 @@
dependencies:
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":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@ -738,11 +736,6 @@
dependencies:
"@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":
version "2.5.0"
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"
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"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
@ -1290,7 +1283,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
"@types/color-name" "^1.1.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"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
@ -1483,16 +1476,6 @@ aws-sdk@2.1013.0:
uuid "3.3.2"
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:
version "0.19.2"
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"
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@*:
version "7.0.5"
resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f"
@ -1964,31 +1942,7 @@ chartjs-plugin-zoom@1.1.1:
dependencies:
hammerjs "^2.0.8"
cheerio-httpcli@0.8.2:
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:
cheerio@0.22.0:
version "0.22.0"
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
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"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
colors@^1.1.2, colors@^1.4.0:
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:
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@ -2329,7 +2278,7 @@ cross-env@7.0.3:
dependencies:
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"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
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:
ms "2.1.2"
debug@4.3.2, 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==
debug@4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
debug@=3.1.0, debug@~3.1.0:
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
@ -2563,6 +2512,13 @@ debug@^3.2.7:
dependencies:
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:
version "1.0.1"
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"
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:
version "1.0.1"
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:
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:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
@ -3509,16 +3445,6 @@ follow-redirects@1.5.10:
dependencies:
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:
version "3.0.0"
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"
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:
version "0.5.2"
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"
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"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
@ -3744,6 +3661,23 @@ globby@^11.0.4:
merge2 "^1.3.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:
version "11.8.2"
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"
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:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
@ -3843,7 +3764,7 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
he@1.2.0, he@^1.2.0:
he@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@ -3963,14 +3884,13 @@ http-signature@1.3.5:
jsprim "^1.2.2"
sshpk "^1.14.1"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
http2-wrapper@^1.0.0-beta.5.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
quick-lru "^5.1.1"
resolve-alpn "^1.0.0"
http2-wrapper@^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"
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:
version "3.0.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5"
@ -4031,13 +3946,6 @@ iconv-lite@^0.6.2:
dependencies:
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:
version "5.1.0"
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"
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:
version "0.1.4"
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"
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:
version "4.27.6"
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"
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:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
@ -4456,7 +4346,7 @@ is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.2"
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
is-typedarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
@ -4498,11 +4388,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
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:
version "26.6.2"
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"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jschardet@^3.0.0:
jschardet@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882"
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"
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"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
@ -4773,26 +4658,11 @@ koa-bodyparser@4.3.0:
co-body "^6.0.0"
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:
version "4.1.0"
resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"
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:
version "2.0.0"
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"
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:
version "2.13.4"
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e"
@ -4960,13 +4801,6 @@ lazystream@^1.0.0:
dependencies:
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:
version "0.4.1"
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"
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:
version "2.0.14"
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"
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:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@ -5284,18 +5102,13 @@ mime-types@2.1.34:
dependencies:
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"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies:
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:
version "4.0.0"
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"
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:
version "5.0.1"
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"
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:
version "0.9.15"
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:
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:
version "6.0.0"
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"
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:
version "1.0.2"
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"
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:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
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:
version "1.3.0"
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"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
path-key@^3.0.0, path-key@^3.1.0:
path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
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"
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:
version "2.5.0"
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"
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:
version "1.1.2"
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"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.28, psl@^1.1.33:
psl@^1.1.33:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
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"
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:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
@ -6966,22 +6723,6 @@ rename@1.0.4:
dependencies:
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:
version "3.0.0"
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"
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:
version "3.0.0"
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"
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:
version "1.1.9"
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"
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"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
@ -7294,7 +7004,7 @@ sigmund@^1.0.1:
resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
signal-exit@^3.0.0, signal-exit@^3.0.2:
signal-exit@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
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"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sshpk@^1.14.1, sshpk@^1.7.0:
sshpk@^1.14.1:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
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"
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:
version "0.3.1"
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"
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:
version "3.0.0"
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"
postcss-selector-parser "^6.0.4"
summaly@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/summaly/-/summaly-2.4.1.tgz#d2a8fa6bad10c1651eb0b849aab3009e87216a3d"
integrity sha512-1gETEQXqK5RD7yIGgdGeTwGL1uh+uj14u99atzNLNmvsxwdtZbPvDHZBPXkAW0cqsd8teoBJln5Dh1QeAhvGIg==
summaly@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/summaly/-/summaly-2.5.0.tgz#ec5af6e84857efcb6c844d896e83569e64a923ea"
integrity sha512-IzvO2s7yj/PUyH42qWjVjSPpIiPlgTRWGh33t4cIZKOqPQJ2INo7e83hXhHFr4hXTb3JRcIdCuM1ELjlrujiUQ==
dependencies:
cheerio-httpcli "0.8.2"
debug "4.3.2"
cheerio "0.22.0"
debug "4.3.3"
escape-regexp "0.0.1"
got "11.5.1"
html-entities "2.3.2"
koa "2.13.1"
request "2.88.2"
request-promise-native "1.0.9"
jschardet "3.0.0"
koa "2.13.4"
private-ip "2.3.3"
require-all "3.0.0"
trace-redirect "1.0.6"
@ -7889,14 +7590,6 @@ token-types@^4.1.1:
"@tokenizer/token" "^0.3.0"
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:
version "4.0.0"
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"
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:
version "1.2.0"
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"
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:
version "2.2.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
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:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"

View File

@ -1,5 +1,6 @@
import { del, get, set } from '@/scripts/idb-proxy';
import { reactive } from 'vue';
import * as misskey from 'misskey-js';
import { apiUrl } from '@/config';
import { waiting, api, popup, popupMenu, success } from '@/os';
import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
@ -8,13 +9,7 @@ import { i18n } from './i18n';
// TODO: 他のタブと永続化されたstateを同期
type Account = {
id: string;
token: string;
isModerator: boolean;
isAdmin: boolean;
isDeleted: boolean;
};
type Account = misskey.entities.MeDetailed;
const data = localStorage.getItem('account');

View File

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

View File

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

View File

@ -1,17 +1,17 @@
<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')">
<MkEmojiPicker ref="picker" class="ryghynhb _popup _shadow" :class="{ pointer: point === 'top' }" :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen"/>
</MkPopup>
<MkModal ref="modal" v-slot="{ type, maxHeight }" :z-priority="'middle'" :prefer-type="asReactionPicker && $store.state.reactionPickerUseDrawerForMobile === false ? 'popup' : 'auto'" :transparent-bg="true" :manual-showing="manualShowing" :src="src" @click="$refs.modal.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')">
<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"/>
</MkModal>
</template>
<script lang="ts">
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';
export default defineComponent({
components: {
MkPopup,
MkModal,
MkEmojiPicker,
},
@ -44,7 +44,7 @@ export default defineComponent({
methods: {
chosen(emoji: any) {
this.$emit('done', emoji);
this.$refs.popup.close();
this.$refs.modal.close();
},
opening() {
@ -57,20 +57,10 @@ export default defineComponent({
<style lang="scss" scoped>
.ryghynhb {
&.pointer {
&:before {
--size: 8px;
content: '';
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);
}
&.drawer {
border-radius: 24px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
}
</style>

View File

@ -1,5 +1,5 @@
<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()">
<div ref="emojis" class="emojis">
<section class="result">
@ -77,7 +77,7 @@
import { defineComponent, markRaw } from 'vue';
import { emojilist } from '@/scripts/emojilist';
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import Particle from '@/components/particle.vue';
import Ripple from '@/components/ripple.vue';
import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch';
import { isMobile } from '@/scripts/is-mobile';
@ -92,9 +92,17 @@ export default defineComponent({
props: {
showPinned: {
required: false,
default: true
default: true,
},
asReactionPicker: {
required: false,
},
maxHeight: {
type: Number,
required: false,
},
asDrawer: {
type: Boolean,
required: false
},
},
@ -288,9 +296,9 @@ export default defineComponent({
if (ev) {
const el = ev.currentTarget || ev.target;
const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2);
const y = rect.top + (el.clientHeight / 2);
os.popup(Particle, { x, y }, {}, 'end');
const x = rect.left + (el.offsetWidth / 2);
const y = rect.top + (el.offsetHeight / 2);
os.popup(Ripple, { x, y }, {}, 'end');
}
const key = this.getKey(emoji);
@ -353,26 +361,60 @@ export default defineComponent({
&.w1 {
width: calc((var(--eachSize) * 5) + (#{$pad} * 2));
--columns: 1fr 1fr 1fr 1fr 1fr;
}
&.w2 {
width: calc((var(--eachSize) * 6) + (#{$pad} * 2));
--columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}
&.w3 {
width: calc((var(--eachSize) * 7) + (#{$pad} * 2));
--columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}
&.h1 {
--height: calc((var(--eachSize) * 4) + (#{$pad} * 2));
height: calc((var(--eachSize) * 4) + (#{$pad} * 2));
}
&.h2 {
--height: calc((var(--eachSize) * 6) + (#{$pad} * 2));
height: calc((var(--eachSize) * 6) + (#{$pad} * 2));
}
&.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 {
@ -409,7 +451,7 @@ export default defineComponent({
}
> .emojis {
height: var(--height);
height: 100%;
overflow-y: auto;
overflow-x: hidden;

View File

@ -5,7 +5,7 @@
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
<input ref="inputEl"
v-model="v"
v-panel
v-adaptive-border
:type="type"
:disabled="disabled"
:required="required"
@ -243,7 +243,8 @@ export default defineComponent({
font-weight: normal;
font-size: 1em;
color: var(--fg);
border: solid 0.5px var(--panel);
background: var(--panel);
border: solid 1px var(--panel);
border-radius: 6px;
outline: none;
box-shadow: none;
@ -251,7 +252,7 @@ export default defineComponent({
transition: border-color 0.1s ease-out;
&:hover {
border-color: var(--inputBorderHover);
border-color: var(--inputBorderHover) !important;
}
}
@ -298,7 +299,7 @@ export default defineComponent({
&.focused {
> input {
border-color: var(--accent);
border-color: var(--accent) !important;
//box-shadow: 0 0 0 4px var(--focus);
}
}

View File

@ -3,7 +3,9 @@
<div class="label" @click="focus"><slot name="label"></slot></div>
<div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick">
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
<select ref="inputEl" v-model="v" v-panel
<select ref="inputEl"
v-model="v"
v-adaptive-border
class="select"
:disabled="disabled"
:required="required"
@ -226,7 +228,7 @@ export default defineComponent({
&:hover {
> .select {
border-color: var(--inputBorderHover);
border-color: var(--inputBorderHover) !important;
}
}
@ -242,6 +244,7 @@ export default defineComponent({
font-weight: normal;
font-size: 1em;
color: var(--fg);
background: var(--panel);
border: solid 1px var(--panel);
border-radius: 6px;
outline: none;
@ -295,7 +298,7 @@ export default defineComponent({
&.focused {
> select {
border-color: var(--accent);
border-color: var(--accent) !important;
}
}

View File

@ -2,10 +2,6 @@
<div
class="ziffeoms"
:class="{ disabled, checked }"
role="switch"
:aria-checked="checked"
:aria-disabled="disabled"
@click.prevent="toggle"
>
<input
ref="input"
@ -13,18 +9,20 @@
:disabled="disabled"
@keydown.enter="toggle"
>
<span v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button">
<span class="handle"></span>
<span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
<i class="check fas fa-check"></i>
</span>
<span class="label">
<span><slot></slot></span>
<span @click="toggle"><slot></slot></span>
<p class="caption"><slot name="caption"></slot></p>
</span>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, ref, toRefs } from 'vue';
import * as os from '@/os';
import Ripple from '@/components/ripple.vue';
export default defineComponent({
props: {
@ -37,17 +35,28 @@ export default defineComponent({
default: false
}
},
computed: {
checked(): boolean {
return this.modelValue;
}
setup(props, context) {
const button = ref<HTMLElement>();
const checked = toRefs(props).modelValue;
const toggle = () => {
if (props.disabled) return;
context.emit('update:modelValue', !checked.value);
if (!checked.value) {
const rect = button.value.getBoundingClientRect();
const x = rect.left + (button.value.offsetWidth / 2);
const y = rect.top + (button.value.offsetHeight / 2);
os.popup(Ripple, { x, y, particle: false }, {}, 'end');
}
};
return {
button,
checked,
toggle,
};
},
methods: {
toggle() {
if (this.disabled) return;
this.$emit('update:modelValue', !this.checked);
}
}
});
</script>
@ -55,16 +64,7 @@ export default defineComponent({
.ziffeoms {
position: relative;
display: flex;
cursor: pointer;
transition: all 0.3s;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
transition: all 0.2s ease;
> * {
user-select: none;
@ -80,27 +80,32 @@ export default defineComponent({
> .button {
position: relative;
display: inline-block;
display: inline-flex;
flex-shrink: 0;
margin: 0;
width: 36px;
height: 26px;
background: var(--switchBg);
box-sizing: border-box;
width: 23px;
height: 23px;
outline: none;
border-radius: 999px;
background: var(--panel);
border: solid 1px var(--panel);
border-radius: 4px;
cursor: pointer;
transition: inherit;
> .handle {
position: absolute;
top: 0;
bottom: 0;
left: 5px;
margin: auto 0;
border-radius: 100%;
transition: background-color 0.3s, transform 0.3s;
width: 16px;
height: 16px;
background-color: #fff;
> .check {
margin: auto;
opacity: 0;
color: var(--fgOnAccent);
font-size: 13px;
transform: scale(0.5);
transition: all 0.2s ease;
}
}
&:hover {
> .button {
border-color: var(--inputBorderHover) !important;
}
}
@ -108,13 +113,13 @@ export default defineComponent({
margin-left: 16px;
margin-top: 2px;
display: block;
cursor: pointer;
transition: inherit;
color: var(--fg);
> span {
display: block;
line-height: 20px;
cursor: pointer;
transition: inherit;
}
@ -129,12 +134,6 @@ export default defineComponent({
}
}
&:hover {
> .button {
background-color: var(--accentedBg);
}
}
&.disabled {
opacity: 0.6;
cursor: not-allowed;
@ -142,11 +141,12 @@ export default defineComponent({
&.checked {
> .button {
background-color: var(--accent);
border-color: var(--accent);
background-color: var(--accent) !important;
border-color: var(--accent) !important;
> .handle {
transform: translateX(10px);
> .check {
opacity: 1;
transform: scale(1);
}
}
}

View File

@ -4,7 +4,7 @@
<div class="input" :class="{ disabled, focused, tall, pre }">
<textarea ref="inputEl"
v-model="v"
v-panel
v-adaptive-border
:class="{ code, _monospace: code }"
:disabled="disabled"
:required="required"
@ -210,7 +210,8 @@ export default defineComponent({
font-weight: normal;
font-size: 1em;
color: var(--fg);
border: solid 0.5px var(--panel);
background: var(--panel);
border: solid 1px var(--panel);
border-radius: 6px;
outline: none;
box-shadow: none;
@ -218,13 +219,13 @@ export default defineComponent({
transition: border-color 0.1s ease-out;
&:hover {
border-color: var(--inputBorderHover);
border-color: var(--inputBorderHover) !important;
}
}
&.focused {
> textarea {
border-color: var(--accent);
border-color: var(--accent) !important;
}
}

View File

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

View File

@ -106,11 +106,6 @@ export default defineComponent({
return;
}
if (this.to.startsWith('/my/messaging')) {
if (ColdDeviceStorage.get('chatOpenBehavior') === 'window') return this.window();
if (ColdDeviceStorage.get('chatOpenBehavior') === 'popout') return this.popout();
}
if (this.behavior) {
if (this.behavior === 'window') {
return this.window();

View File

@ -6,7 +6,7 @@
<i v-else-if="info.icon" class="icon" :class="info.icon"></i>
<div class="title">
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/>
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="true" class="title"/>
<div v-else-if="info.title" class="title">{{ info.title }}</div>
<div v-if="!narrow && info.subtitle" class="subtitle">
{{ info.subtitle }}
@ -268,6 +268,7 @@ export default defineComponent({
> .titleContainer {
display: flex;
align-items: center;
max-width: 400px;
overflow: auto;
white-space: nowrap;
text-align: left;

View File

@ -1,7 +1,5 @@
<template>
<component :is="self ? 'MkA' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
<component :is="self ? 'MkA' : 'a'" ref="el" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@contextmenu.stop="() => {}"
>
<template v-if="!self">
@ -20,11 +18,11 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, ref } from 'vue';
import { toUnicode as decodePunycode } from 'punycode/';
import { url as local } from '@/config';
import { isTouchUsing } from '@/scripts/touch';
import * as os from '@/os';
import { useTooltip } from '@/scripts/use-tooltip';
export default defineComponent({
props: {
@ -35,74 +33,36 @@ export default defineComponent({
rel: {
type: String,
required: false,
default: null,
}
},
data() {
const self = this.url.startsWith(local);
setup(props) {
const self = props.url.startsWith(local);
const url = new URL(props.url);
const el = ref();
useTooltip(el, (showing) => {
os.popup(import('@/components/url-preview-popup.vue'), {
showing,
url: props.url,
source: el.value,
}, {}, 'closed');
});
return {
local,
schema: null as string | null,
hostname: null as string | null,
port: null as string | null,
pathname: null as string | null,
query: null as string | null,
hash: null as string | null,
schema: url.protocol,
hostname: decodePunycode(url.hostname),
port: url.port,
pathname: decodeURIComponent(url.pathname),
query: decodeURIComponent(url.search),
hash: decodeURIComponent(url.hash),
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
showTimer: null,
hideTimer: null,
checkTimer: null,
close: null,
el,
};
},
created() {
const url = new URL(this.url);
this.schema = url.protocol;
this.hostname = decodePunycode(url.hostname);
this.port = url.port;
this.pathname = decodeURIComponent(url.pathname);
this.query = decodeURIComponent(url.search);
this.hash = decodeURIComponent(url.hash);
},
methods: {
async showPreview() {
if (!document.body.contains(this.$el)) return;
if (this.close) return;
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
url: this.url,
source: this.$el
});
this.close = () => {
dispose();
};
this.checkTimer = setInterval(() => {
if (!document.body.contains(this.$el)) this.closePreview();
}, 1000);
},
closePreview() {
if (this.close) {
clearInterval(this.checkTimer);
this.close();
this.close = null;
}
},
onMouseover() {
if (isTouchUsing) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
if (isTouchUsing) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
}
}
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<MkModal ref="modal" @click="$refs.modal.close()" @closed="$emit('closed')">
<MkModal ref="modal" :z-priority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="xubzgfga">
<header>{{ image.name }}</header>
<img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/>

View File

@ -1,5 +1,5 @@
<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="main">
<template v-for="item in items">

View File

@ -105,6 +105,7 @@ export default defineComponent({
return {
previewable,
gallery,
pswpZIndex: os.claimZIndex('middle'),
};
},
});
@ -188,3 +189,11 @@ export default defineComponent({
}
}
</style>
<style lang="scss">
.pswp {
//
//z-index: v-bind(pswpZIndex);
z-index: 2000000;
}
</style>

View File

@ -7,7 +7,7 @@
</div>
<div class="body">
<div class="content">
<Mfm :text="text" :author="$i" :i="$i"/>
<Mfm :text="text.trim()" :author="$i" :i="$i"/>
</div>
</div>
</div>
@ -61,6 +61,7 @@ export default defineComponent({
width: 40px;
height: 40px;
border-radius: 8px;
pointer-events: none;
}
> .main {
@ -69,6 +70,7 @@ export default defineComponent({
> .header {
margin-bottom: 2px;
font-weight: bold;
}
> .body {

View File

@ -0,0 +1,74 @@
<template>
<div class="mk-notification-toast" :style="{ zIndex }">
<transition name="notification-toast" appear @after-leave="$emit('closed')">
<XNotification v-if="showing" :notification="notification" class="notification _acrylic"/>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XNotification from './notification.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XNotification
},
props: {
notification: {
type: Object,
required: true
}
},
emits: ['closed'],
data() {
return {
showing: true,
zIndex: os.claimZIndex('high'),
};
},
mounted() {
setTimeout(() => {
this.showing = false;
}, 6000);
}
});
</script>
<style lang="scss" scoped>
.notification-toast-enter-active, .notification-toast-leave-active {
transition: opacity 0.3s, transform 0.3s !important;
}
.notification-toast-enter-from, .notification-toast-leave-to {
opacity: 0;
transform: translateX(-250px);
}
.mk-notification-toast {
position: fixed;
left: 0;
width: 250px;
top: 32px;
padding: 0 32px;
pointer-events: none;
@media (max-width: 700px) {
top: initial;
bottom: 112px;
padding: 0 16px;
}
@media (max-width: 500px) {
bottom: 92px;
padding: 0 8px;
}
> .notification {
height: 100%;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
border-radius: 8px;
overflow: hidden;
}
}
</style>

View File

@ -16,7 +16,13 @@
<template #headerLeft>
<button v-if="history.length > 0" v-tooltip="$ts.goBack" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button>
</template>
<div class="yrolvcoq">
<template #headerRight>
<button v-tooltip="$ts.showInPage" class="_button" @click="expand()"><i class="fas fa-expand-alt"></i></button>
<button v-tooltip="$ts.popout" class="_button" @click="popout()"><i class="fas fa-external-link-alt"></i></button>
<button class="_button" @click="menu"><i class="fas fa-ellipsis-h"></i></button>
</template>
<div class="yrolvcoq" :style="{ background: pageInfo?.bg }">
<MkStickyContainer>
<template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template>
<component :is="component" v-bind="props" :ref="changePage"/>
@ -33,6 +39,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
import { resolve } from '@/router';
import { url } from '@/config';
import * as symbols from '@/symbols';
import * as os from '@/os';
export default defineComponent({
components: {
@ -139,6 +146,23 @@ export default defineComponent({
this.props = props;
},
menu(ev) {
os.popupMenu([{
icon: 'fas fa-external-link-alt',
text: this.$ts.openInNewTab,
action: () => {
window.open(this.url, '_blank');
this.$refs.window.close();
}
}, {
icon: 'fas fa-link',
text: this.$ts.copyLink,
action: () => {
copyToClipboard(this.url);
}
}], ev.currentTarget || ev.target);
},
back() {
this.navigate(this.history.pop(), false);
},

View File

@ -1,5 +1,5 @@
<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()"/>
</MkModal>
</template>

View File

@ -2,7 +2,7 @@
<button
v-if="count > 0"
ref="buttonRef"
v-particle="canToggle"
v-ripple="canToggle"
class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction()"

View File

@ -1,5 +1,5 @@
<template>
<div class="vswabwbm" :style="{ top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }">
<div class="vswabwbm" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }">
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<circle fill="none" cx="64" cy="64">
<animate attributeName="r"
@ -52,7 +52,8 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, onMounted } from 'vue';
import * as os from '@/os';
export default defineComponent({
props: {
@ -63,37 +64,46 @@ export default defineComponent({
y: {
type: Number,
required: true
},
particle: {
type: Boolean,
required: false,
default: true,
}
},
emits: ['end'],
data() {
setup(props, context) {
const particles = [];
const origin = 64;
const colors = ['#FF1493', '#00FFFF', '#FFE202'];
for (let i = 0; i < 12; i++) {
const angle = Math.random() * (Math.PI * 2);
const pos = Math.random() * 16;
const velocity = 16 + (Math.random() * 48);
particles.push({
size: 4 + (Math.random() * 8),
xA: origin + (Math.sin(angle) * pos),
yA: origin + (Math.cos(angle) * pos),
xB: origin + (Math.sin(angle) * (pos + velocity)),
yB: origin + (Math.cos(angle) * (pos + velocity)),
color: colors[Math.floor(Math.random() * colors.length)]
});
if (props.particle) {
for (let i = 0; i < 12; i++) {
const angle = Math.random() * (Math.PI * 2);
const pos = Math.random() * 16;
const velocity = 16 + (Math.random() * 48);
particles.push({
size: 4 + (Math.random() * 8),
xA: origin + (Math.sin(angle) * pos),
yA: origin + (Math.cos(angle) * pos),
xB: origin + (Math.sin(angle) * (pos + velocity)),
yB: origin + (Math.cos(angle) * (pos + velocity)),
color: colors[Math.floor(Math.random() * colors.length)]
});
}
}
onMounted(() => {
setTimeout(() => {
context.emit('end');
}, 1100);
});
return {
particles
particles,
zIndex: os.claimZIndex('high'),
};
},
mounted() {
setTimeout(() => {
this.$emit('end');
}, 1100);
}
});
</script>
@ -101,7 +111,6 @@ export default defineComponent({
.vswabwbm {
pointer-events: none;
position: fixed;
z-index: 1000000;
width: 128px;
height: 128px;

View File

@ -51,14 +51,13 @@
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span>
</template>
</MkInput>
<label v-if="meta.tosUrl" class="_formBlock tou">
<input v-model="ToSAgreement" type="checkbox">
<MkSwitch v-if="meta.tosUrl" v-model="ToSAgreement" class="_formBlock tou">
<I18n :src="$ts.agreeTo">
<template #0>
<a :href="meta.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a>
</template>
</I18n>
</label>
</MkSwitch>
<captcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
<captcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
@ -258,11 +257,5 @@ export default defineComponent({
.captcha {
margin: 16px 0;
}
> .tou {
display: block;
margin: 16px 0;
cursor: pointer;
}
}
</style>

View File

@ -1,73 +1,70 @@
<template>
<div class="mk-toast">
<transition name="notification-slide" appear @after-leave="$emit('closed')">
<XNotification v-if="showing" :notification="notification" class="notification _acrylic"/>
<transition name="toast" appear @after-leave="$emit('closed')">
<div v-if="showing" class="body _acrylic" :style="{ zIndex }">
<div class="message">
{{ message }}
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XNotification from './notification.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XNotification
},
props: {
notification: {
type: Object,
required: true
}
message: {
type: String,
required: true,
},
},
emits: ['closed'],
data() {
return {
showing: true
showing: true,
zIndex: os.claimZIndex('high'),
};
},
mounted() {
setTimeout(() => {
this.showing = false;
}, 6000);
}, 4000);
}
});
</script>
<style lang="scss" scoped>
.notification-slide-enter-active, .notification-slide-leave-active {
.toast-enter-active, .toast-leave-active {
transition: opacity 0.3s, transform 0.3s !important;
}
.notification-slide-enter-from, .notification-slide-leave-to {
.toast-enter-from, .toast-leave-to {
opacity: 0;
transform: translateX(-250px);
transform: translateY(-100%);
}
.mk-toast {
position: fixed;
z-index: 10000;
left: 0;
width: 250px;
top: 32px;
padding: 0 32px;
pointer-events: none;
@media (max-width: 700px) {
top: initial;
bottom: 112px;
padding: 0 16px;
}
@media (max-width: 500px) {
bottom: 92px;
padding: 0 8px;
}
> .notification {
height: 100%;
> .body {
position: fixed;
left: 0;
right: 0;
top: 0;
margin: 0 auto;
margin-top: 16px;
min-width: 300px;
max-width: calc(100% - 32px);
width: min-content;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
border-radius: 8px;
overflow: hidden;
overflow: clip;
text-align: center;
pointer-events: none;
> .message {
padding: 16px 24px;
}
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<transition :name="$store.state.animation ? 'fade' : ''" appear>
<div class="nvlagfpb" @contextmenu.prevent.stop="() => {}">
<div class="nvlagfpb" :style="{ zIndex }" @contextmenu.prevent.stop="() => {}">
<MkMenu :items="items" class="_popup _shadow" :align="'left'" @close="$emit('closed')"/>
</div>
</transition>
@ -10,6 +10,7 @@
import { defineComponent } from 'vue';
import contains from '@/scripts/contains';
import MkMenu from './menu.vue';
import * as os from '@/os';
export default defineComponent({
components: {
@ -29,6 +30,11 @@ export default defineComponent({
},
},
emits: ['closed'],
data() {
return {
zIndex: os.claimZIndex('high'),
};
},
computed: {
keymap(): any {
return {
@ -82,7 +88,6 @@ export default defineComponent({
<style lang="scss" scoped>
.nvlagfpb {
position: absolute;
z-index: 65535;
}
.fade-enter-active, .fade-leave-active {

View File

@ -1,8 +1,8 @@
<template>
<div ref="items" v-hotkey="keymap"
class="rrevdjwt"
:class="{ center: align === 'center' }"
:style="{ width: width ? width + 'px' : null, maxHeight: maxHeight ? maxHeight + 'px' : null }"
:class="{ center: align === 'center', asDrawer }"
:style="{ width: (width && !asDrawer) ? width + 'px' : null, maxHeight: maxHeight ? maxHeight + 'px' : null }"
@contextmenu.self="e => e.preventDefault()"
>
<template v-for="(item, i) in items2">
@ -56,6 +56,10 @@ export default defineComponent({
type: Boolean,
required: false
},
asDrawer: {
type: Boolean,
required: false
},
align: {
type: String,
requried: false
@ -276,8 +280,31 @@ export default defineComponent({
> .divider {
margin: 8px 0;
height: 1px;
background: var(--divider);
border-top: solid 0.5px var(--divider);
}
&.asDrawer {
padding: 12px 0 calc(env(safe-area-inset-bottom, 0px) + 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>

View File

@ -1,5 +1,5 @@
<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="header">
<button v-if="withOkButton" class="_button" @click="$emit('close')"><i class="fas fa-times"></i></button>

View File

@ -1,17 +1,19 @@
<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">
<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<div class="bg _modalBg" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
<div ref="content" class="content" :class="{ popup, fixed, top: position === 'top' }" :style="{ zIndex }" @click.self="onBgClick">
<slot></slot>
<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="{ 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" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick">
<slot :max-height="maxHeight" :type="type"></slot>
</div>
</div>
</transition>
</template>
<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';
import { defaultStore } from '@/store';
function getFixedContainer(el: Element | null): Element | null {
if (el == null || el.tagName === 'BODY') return null;
@ -27,6 +29,7 @@ export default defineComponent({
provide: {
modal: true
},
props: {
manualShowing: {
type: Boolean,
@ -38,61 +41,86 @@ export default defineComponent({
required: false
},
src: {
type: Object as PropType<HTMLElement>,
required: false,
default: null,
},
position: {
required: false
preferType: {
required: false,
type: String,
default: 'auto',
},
front: {
zPriority: {
type: String as PropType<'low' | 'middle' | 'high'>,
required: false,
default: 'low',
},
noOverlap: {
type: Boolean,
required: false,
default: true,
},
transparentBg: {
type: Boolean,
required: false,
default: false,
}
},
emits: ['opening', 'click', 'esc', 'close', 'closed'],
data() {
return {
zIndex: os.claimZIndex(this.front),
showing: true,
fixed: false,
transformOrigin: 'center',
contentClicking: false,
};
},
computed: {
keymap(): any {
return {
'esc': () => this.$emit('esc'),
};
},
popup(): boolean {
return this.src != null;
}
},
mounted() {
this.$watch('src', () => {
this.fixed = getFixedContainer(this.src) != null;
this.$nextTick(() => {
this.align();
});
}, { immediate: true });
this.$nextTick(() => {
const popover = this.$refs.content as any;
new ResizeObserver((entries, observer) => {
this.align();
}).observe(popover);
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.zPriority);
const type = computed(() => {
if (props.preferType === 'auto') {
if (!defaultStore.state.disableDrawer && 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;
const rect = this.src.getBoundingClientRect();
const rect = props.src.getBoundingClientRect();
const width = popover.offsetWidth;
const height = popover.offsetHeight;
@ -100,102 +128,143 @@ export default defineComponent({
let left;
let top;
if (this.srcCenter) {
const x = rect.left + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + (this.src.offsetHeight / 2);
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 + (this.fixed ? 0 : window.pageXOffset) + (this.src.offsetWidth / 2);
const y = rect.top + (this.fixed ? 0 : window.pageYOffset) + this.src.offsetHeight;
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 (this.fixed) {
if (fixed.value) {
//
if (left + width > window.innerWidth) {
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 {
//
if (left + width - window.pageXOffset > window.innerWidth) {
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) {
top = 0;
top = MARGIN;
}
if (left < 0) {
left = 0;
}
if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) {
this.transformOrigin = 'center top';
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 {
this.transformOrigin = 'center';
transformOrigin.value = 'center';
}
popover.style.left = left + 'px';
popover.style.top = top + 'px';
},
};
childRendered() {
const childRendered = () => {
//
const content = this.$refs.content.children[0];
content.addEventListener('mousedown', e => {
this.contentClicking = true;
const el = content.value!.children[0];
el.addEventListener('mousedown', e => {
contentClicking = true;
window.addEventListener('mouseup', e => {
// click mouseup
setTimeout(() => {
this.contentClicking = false;
contentClicking = false;
}, 100);
}, { passive: true, once: true });
}, { passive: true });
},
};
close() {
this.showing = false;
this.$emit('close');
},
onMounted(() => {
watch(() => props.src, async () => {
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() {
if (this.contentClicking) return;
this.$emit('click');
},
await nextTick()
onClosed() {
this.$emit('closed');
}
}
align();
}, { 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>
<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>
.modal-enter-active, .modal-leave-active {
> .bg {
transition: opacity 0.3s !important;
transition: opacity 0.2s !important;
}
> .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 {
@ -206,17 +275,19 @@ export default defineComponent({
> .content {
pointer-events: none;
opacity: 0;
transform-origin: var(--transformOrigin);
transform: scale(0.9);
}
}
.modal-popup-enter-active, .modal-popup-leave-active {
> .bg {
transition: opacity 0.3s !important;
transition: opacity 0.2s !important;
}
> .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 {
@ -227,48 +298,112 @@ export default defineComponent({
> .content {
pointer-events: none;
opacity: 0;
transform-origin: var(--transformOrigin);
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 {
> .content:not(.popup) {
position: fixed;
top: 0;
bottom: 0;
left: 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%);
> .bg {
&.transparent {
background: transparent;
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
}
> ::v-deep(*) {
&.dialog {
> .content {
position: fixed;
top: 0;
bottom: 0;
left: 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%);
}
&.top {
> ::v-deep(*) {
margin-top: 0;
margin: auto;
}
&.top {
> ::v-deep(*) {
margin-top: 0;
}
}
}
}
> .content.popup {
position: absolute;
&.popup {
> .content {
position: absolute;
&.fixed {
position: fixed;
&.fixed {
position: fixed;
}
}
}
&.drawer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: clip;
> .content {
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: auto;
> ::v-deep(*) {
margin: auto;
}
}
}
}
</style>

View File

@ -5,7 +5,12 @@
<MkError v-else-if="error" @retry="init()"/>
<div v-else-if="empty" key="_empty_" class="empty">
<slot name="empty"></slot>
<slot name="empty">
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.nothing }}</div>
</div>
</slot>
</div>
<div v-else class="cxiknjgy">

View File

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

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