Merge branch 'develop' into feat/scylladb

This commit is contained in:
Namekuji 2023-09-02 05:07:53 -04:00
commit 494243cba8
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
449 changed files with 13903 additions and 3319 deletions

3
.gitignore vendored
View file

@ -32,6 +32,9 @@ coverage
# docker dev config # docker dev config
/dev/docker-compose.yml /dev/docker-compose.yml
# ESLint
.eslintcache
# misskey # misskey
built built
db db

View file

@ -1,7 +1,6 @@
{ {
"recommendations": [ "recommendations": [
"editorconfig.editorconfig", "editorconfig.editorconfig",
"rome.rome",
"vue.volar", "vue.volar",
"vue.vscode-typescript-vue-plugin", "vue.vscode-typescript-vue-plugin",
"arcanis.vscode-zipfs", "arcanis.vscode-zipfs",
@ -13,6 +12,7 @@
"mrmlnc.vscode-json5", "mrmlnc.vscode-json5",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"redhat.vscode-yaml", "redhat.vscode-yaml",
"yoavbls.pretty-ts-errors" "yoavbls.pretty-ts-errors",
"biomejs.biome"
] ]
} }

12
biome.json Normal file
View file

@ -0,0 +1,12 @@
{
"$schema": "https://biomejs.dev/schemas/1.0.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}

View file

@ -61,6 +61,8 @@ services:
# sonic: # sonic:
# restart: unless-stopped # restart: unless-stopped
# image: docker.io/valeriansaliou/sonic:v1.4.0 # image: docker.io/valeriansaliou/sonic:v1.4.0
# logging:
# driver: none
# networks: # networks:
# - calcnet # - calcnet
# volumes: # volumes:

View file

@ -15,7 +15,7 @@ gotIt: "Ho capito!"
cancel: "Annulla" cancel: "Annulla"
enterUsername: "Inserisci un nome utente" enterUsername: "Inserisci un nome utente"
renotedBy: "Boost da {user}" renotedBy: "Boost da {user}"
noNotes: "Nessuna nota!" noNotes: "Nessun post"
noNotifications: "Nessuna notifica" noNotifications: "Nessuna notifica"
instance: "Server" instance: "Server"
settings: "Impostazioni" settings: "Impostazioni"
@ -35,10 +35,10 @@ users: "Utenti"
addUser: "Aggiungi utente" addUser: "Aggiungi utente"
favorite: "Aggiungi ai preferiti" favorite: "Aggiungi ai preferiti"
favorites: "Preferiti" favorites: "Preferiti"
unfavorite: "Rimuovi nota dai preferiti" unfavorite: "Rimuovi post dai preferiti"
favorited: "Aggiunta ai tuoi preferiti." favorited: "Aggiunto ai tuoi preferiti."
alreadyFavorited: "Già tra i tuoi preferiti." alreadyFavorited: "Già tra i tuoi preferiti."
cantFavorite: "Impossibile aggiungere la nota ai preferiti." cantFavorite: "Impossibile aggiungere il post ai preferiti."
pin: "Fissa sul profilo" pin: "Fissa sul profilo"
unpin: "Non fissare sul profilo" unpin: "Non fissare sul profilo"
copyContent: "Copia il contenuto" copyContent: "Copia il contenuto"
@ -71,7 +71,7 @@ driveFileDeleteConfirm: "Vuoi davvero eliminare il file \"{name}\"? Sarà rimoss
unfollowConfirm: "Vuoi davvero smettere di seguire {name}?" unfollowConfirm: "Vuoi davvero smettere di seguire {name}?"
exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando
sarà compiuta, il file verrà aggiunto direttamente al Drive." sarà compiuta, il file verrà aggiunto direttamente al Drive."
importRequested: "Hai richiesto un'importazione. Può volerci tempo. " importRequested: "Hai richiesto un'importazione. Potrebbe impiegare del tempo."
lists: "Liste" lists: "Liste"
noLists: "Nessuna lista" noLists: "Nessuna lista"
note: "Post" note: "Post"
@ -84,10 +84,10 @@ manageLists: "Gestisci liste"
error: "Errore" error: "Errore"
somethingHappened: "Si è verificato un problema" somethingHappened: "Si è verificato un problema"
retry: "Riprova" retry: "Riprova"
pageLoadError: "Caricamento pagina non riuscito. " pageLoadError: "Errore nel caricamento della pagina."
pageLoadErrorDescription: "Questo viene normalmente causato dalla rete o dalla cache pageLoadErrorDescription: "Di solito succede per errori di rete o a causa della cache
del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." del browser. Prova a pulire la cache o a riprovare più tardi."
serverIsDead: "Il server non risponde. Si prega di attendere e riprovare più tardi." serverIsDead: "Il server non risponde. Attendi e riprova più tardi."
youShouldUpgradeClient: "Per visualizzare la pagina è necessario aggiornare il client youShouldUpgradeClient: "Per visualizzare la pagina è necessario aggiornare il client
alla nuova versione e ricaricare." alla nuova versione e ricaricare."
enterListName: "Nome della lista" enterListName: "Nome della lista"
@ -122,7 +122,7 @@ markAsSensitive: "Segna come sensibile"
unmarkAsSensitive: "Segna come non sensibile" unmarkAsSensitive: "Segna come non sensibile"
enterFileName: "Nome del file" enterFileName: "Nome del file"
mute: "Silenzia" mute: "Silenzia"
unmute: "Riattiva" unmute: "Non silenziare"
block: "Blocca" block: "Blocca"
unblock: "Sblocca" unblock: "Sblocca"
suspend: "Sospendi" suspend: "Sospendi"
@ -135,25 +135,25 @@ selectList: "Seleziona una lista"
selectAntenna: "Scegli un'antenna" selectAntenna: "Scegli un'antenna"
selectWidget: "Seleziona widget" selectWidget: "Seleziona widget"
editWidgets: "Modifica i widget" editWidgets: "Modifica i widget"
editWidgetsExit: "Modifica fine" editWidgetsExit: "Fine modifica"
customEmojis: "Emoji personalizzati" customEmojis: "Emoji personalizzati"
emoji: "Emoji" emoji: "Emoji"
emojis: "Emoji" emojis: "Emoji"
emojiName: "Nome dell'emoji" emojiName: "Nome dell'emoji"
emojiUrl: "URL dell'emoji" emojiUrl: "URL dell'emoji"
addEmoji: "Aggiungi un emoji" addEmoji: "Aggiungi un emoji"
settingGuide: "Configurazione suggerita" settingGuide: "Impostazioni suggerite"
cacheRemoteFiles: "Memorizzazione nella cache dei file remoti" cacheRemoteFiles: "Memorizzazione nella cache dei file remoti"
cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno
scaricati direttamente dal loro server. L'opzione permette di risparmiare spazio scaricati direttamente dal loro server. L'opzione permette di risparmiare spazio
ma aumenta il traffico di rete e non verranno generate anteprime." ma aumenta il traffico di rete e non verranno generate anteprime."
flagAsBot: "Io sono un robot" flagAsBot: "Questo account è un bot"
flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche, flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche,
attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori
allo scopo di prevenire catene dinterazione senza fine con altri bot, e di adeguare allo scopo di prevenire catene dinterazione senza fine con altri bot, e di adeguare
i sistemi interni di Firefish perché trattino questo account come un bot." i sistemi interni di Firefish perché trattino questo account come un bot."
flagAsCat: "Io sono un gatto" flagAsCat: "Sei un gatto? 😺"
flagAsCatDescription: "Abilita l'opzione \"Io sono un gatto\" per l'account." flagAsCatDescription: "Ti compariranno le orecchie e parlerai come un gatto!"
autoAcceptFollowed: "Accetta in automatico i follow dagli account che segui" autoAcceptFollowed: "Accetta in automatico i follow dagli account che segui"
addAccount: "Aggiungi account" addAccount: "Aggiungi account"
loginFailed: "Accesso non riuscito" loginFailed: "Accesso non riuscito"
@ -171,17 +171,17 @@ proxyAccountDescription: "Un account proxy è un account che funziona da followe
una lista, le attività di quell'utente potrebbero comunque non essere visualizzate una lista, le attività di quell'utente potrebbero comunque non essere visualizzate
in locale se nessun altro utente lo segue su questo server, l'account proxy si occuperà in locale se nessun altro utente lo segue su questo server, l'account proxy si occuperà
di seguire e acquisire i post." di seguire e acquisire i post."
host: "Server remoto" host: "Host"
selectUser: "Seleziona utente" selectUser: "Seleziona utente"
recipient: "Destinatario" recipient: "Destinatario(i)"
annotation: "Descrizione" annotation: "Annotazioni"
federation: "Federazione" federation: "Federazione"
instances: "Server" instances: "Server"
registeredAt: "Registrato presso" registeredAt: "Registrato presso"
latestRequestSentAt: "Ultima richiesta inviata" latestRequestSentAt: "Ultima richiesta inviata"
latestRequestReceivedAt: "Ultima richiesta ricevuta" latestRequestReceivedAt: "Ultima richiesta ricevuta"
latestStatus: "Ultimo stato" latestStatus: "Ultimo stato"
storageUsage: "Volume di dischi" storageUsage: "Spazio occupato"
charts: "Grafici" charts: "Grafici"
perHour: "All'ora" perHour: "All'ora"
perDay: "al giorno" perDay: "al giorno"
@ -190,9 +190,9 @@ blockThisInstance: "Blocca questo server"
operations: "Operazioni" operations: "Operazioni"
software: "Software" software: "Software"
version: "Versione" version: "Versione"
metadata: "Metadato" metadata: "Metadati"
monitor: "Monitorare" monitor: "Monitor"
jobQueue: "Coda di lavoro" jobQueue: "Coda dei job"
cpuAndMemory: "CPU e Memoria" cpuAndMemory: "CPU e Memoria"
network: "Rete" network: "Rete"
disk: "Disco" disk: "Disco"
@ -203,7 +203,7 @@ clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?"
clearQueueConfirmText: "I post ancora in coda non verranno più federati. Solitamente, clearQueueConfirmText: "I post ancora in coda non verranno più federati. Solitamente,
non è necessario eseguire questa operazione." non è necessario eseguire questa operazione."
clearCachedFiles: "Svuota cache" clearCachedFiles: "Svuota cache"
clearCachedFilesConfirm: "Vuoi davvero svuotare la cache da tutti i file remoti?" clearCachedFilesConfirm: "Vuoi davvero svuotare la cache di tutti i file remoti?"
blockedInstances: "Server bloccati" blockedInstances: "Server bloccati"
blockedInstancesDescription: "Elenca gli hostname dei server che vuoi bloccare. Non blockedInstancesDescription: "Elenca gli hostname dei server che vuoi bloccare. Non
potranno più comunicare con il tuo server." potranno più comunicare con il tuo server."
@ -213,7 +213,7 @@ blockedUsers: "Account bloccati"
noUsers: "Nessun utente trovato" noUsers: "Nessun utente trovato"
editProfile: "Modifica profilo" editProfile: "Modifica profilo"
noteDeleteConfirm: "Vuoi eliminare questo post?" noteDeleteConfirm: "Vuoi eliminare questo post?"
pinLimitExceeded: "Non puoi fissare più post di così" pinLimitExceeded: "Hai già fissato il massimo possibile di post"
intro: "L'installazione di Firefish è finita! Si prega di creare un account amministratore." intro: "L'installazione di Firefish è finita! Si prega di creare un account amministratore."
done: "Fine" done: "Fine"
processing: "Elaborazione in corso" processing: "Elaborazione in corso"
@ -221,12 +221,12 @@ preview: "Anteprima"
default: "Predefinito" default: "Predefinito"
noCustomEmojis: "Nessun emoji" noCustomEmojis: "Nessun emoji"
noJobs: "Nessun lavoro" noJobs: "Nessun lavoro"
federating: "Federando" federating: "Federazione in corso"
blocked: "Bloccato" blocked: "Bloccato"
suspended: "Sospes@" suspended: "Sospeso"
all: "Tutti" all: "Tutti"
subscribing: "Iscrivendo" subscribing: "Sottoscrizione in corso"
publishing: "Pubblicando" publishing: "Pubblicazione in corso"
notResponding: "Nessuna risposta" notResponding: "Nessuna risposta"
instanceFollowing: "Seguiti da te su questo server" instanceFollowing: "Seguiti da te su questo server"
instanceFollowers: "Chi ti segue su questo server" instanceFollowers: "Chi ti segue su questo server"
@ -238,8 +238,8 @@ currentPassword: "Password attuale"
newPassword: "Nuova Password" newPassword: "Nuova Password"
newPasswordRetype: "Conferma password" newPasswordRetype: "Conferma password"
attachFile: "Allega file" attachFile: "Allega file"
more: "Altri!" more: "Altro!"
featured: "Tendenze" featured: "In primo piano"
usernameOrUserId: "Nome utente o ID utente" usernameOrUserId: "Nome utente o ID utente"
noSuchUser: "Nessun utente trovato" noSuchUser: "Nessun utente trovato"
lookup: "Cercare" lookup: "Cercare"
@ -249,7 +249,7 @@ remove: "Elimina"
removed: "Il tuo Tweet è stato eliminato" removed: "Il tuo Tweet è stato eliminato"
removeAreYouSure: "Eliminare \"{x}\"?" removeAreYouSure: "Eliminare \"{x}\"?"
deleteAreYouSure: "Eliminare \"{x}\"?" deleteAreYouSure: "Eliminare \"{x}\"?"
resetAreYouSure: "Reimposta" resetAreYouSure: "Vuoi reimpostare?"
saved: "Salvato" saved: "Salvato"
messaging: "Messaggi" messaging: "Messaggi"
upload: "Carica" upload: "Carica"
@ -265,8 +265,8 @@ noMoreHistory: "Non c'è più cronologia da visualizzare"
startMessaging: "Nuovo messaggio" startMessaging: "Nuovo messaggio"
nUsersRead: "Letto da {n} persone" nUsersRead: "Letto da {n} persone"
agreeTo: "Sono d'accordo con {0}" agreeTo: "Sono d'accordo con {0}"
tos: "Termini di servizio" tos: "Termini d'uso"
start: "Inizia!" start: "Inizia"
home: "Home" home: "Home"
remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è
un utente remoto." un utente remoto."
@ -274,7 +274,7 @@ activity: "Attività"
images: "Immagini" images: "Immagini"
birthday: "Compleanno" birthday: "Compleanno"
yearsOld: "{age}Anni" yearsOld: "{age}Anni"
registeredDate: "Iscrizione a.." registeredDate: "Iscrizione il"
location: "Posizione" location: "Posizione"
theme: "Tema" theme: "Tema"
themeForLightMode: "Tema da utilizzare per il modo chiaro" themeForLightMode: "Tema da utilizzare per il modo chiaro"
@ -285,7 +285,7 @@ lightThemes: "Tema Chiaro"
darkThemes: "Tema Scuro" darkThemes: "Tema Scuro"
syncDeviceDarkMode: "Sincronizza il tema scuro con le impostazioni del dispositivo" syncDeviceDarkMode: "Sincronizza il tema scuro con le impostazioni del dispositivo"
drive: "Drive" drive: "Drive"
fileName: "Nome dell'allegato" fileName: "Nome file"
selectFile: "Scelta allegato" selectFile: "Scelta allegato"
selectFiles: "Scelta allegato" selectFiles: "Scelta allegato"
selectFolder: "Seleziona cartella" selectFolder: "Seleziona cartella"
@ -298,25 +298,25 @@ deleteFolder: "Elimina cartella"
addFile: "Allega" addFile: "Allega"
emptyDrive: "Il Drive è vuoto" emptyDrive: "Il Drive è vuoto"
emptyFolder: "La cartella è vuota" emptyFolder: "La cartella è vuota"
unableToDelete: "Eliminazione impossibile" unableToDelete: "Impossibile rimuovere"
inputNewFileName: "Inserisci nome del nuovo file" inputNewFileName: "Inserisci nome del nuovo file"
inputNewDescription: "Inserisci una nuova descrizione" inputNewDescription: "Inserisci una nuova descrizione"
inputNewFolderName: "Inserisci nome della nuova cartella" inputNewFolderName: "Inserisci nome della nuova cartella"
circularReferenceFolder: "La cartella di destinazione è una sottocartella della cartella circularReferenceFolder: "La cartella di destinazione è una sottocartella della cartella
che vuoi spostare." che vuoi spostare."
hasChildFilesOrFolders: "La cartella non può essere rimossa perché non è vuota" hasChildFilesOrFolders: "La cartella non può essere rimossa perché non è vuota."
copyUrl: "Copia URL" copyUrl: "Copia URL"
rename: "Modifica nome" rename: "Modifica nome"
avatar: "Foto del profilo" avatar: "Foto del profilo"
banner: "Intestazione" banner: "Intestazione"
nsfw: "Contenuti sensibili" nsfw: "Contenuti sensibili"
whenServerDisconnected: "Quando la connessione col server è persa" whenServerDisconnected: "Quando la connessione col server è persa"
disconnectedFromServer: "Disconness@ dal server" disconnectedFromServer: "Server disconnesso"
reload: "Ricarica" reload: "Ricarica"
doNothing: "Nessun'azione" doNothing: "Nessun'azione"
reloadConfirm: "Vuoi ricaricare?" reloadConfirm: "Vuoi ricaricare?"
watch: "Osserva" watch: "Osserva"
unwatch: "Smetti di Osserva" unwatch: "Smetti di osservare"
accept: "Accetta" accept: "Accetta"
reject: "Rifiuta" reject: "Rifiuta"
normal: "Normale" normal: "Normale"
@ -324,7 +324,7 @@ instanceName: "Nome del server"
instanceDescription: "Descrizione del server" instanceDescription: "Descrizione del server"
maintainerName: "Nome dell'Amministratore" maintainerName: "Nome dell'Amministratore"
maintainerEmail: "Indirizzo e-mail dell'Amministratore" maintainerEmail: "Indirizzo e-mail dell'Amministratore"
tosUrl: "Termini di servizio URL" tosUrl: "URL Termini d'uso"
thisYear: "Anno" thisYear: "Anno"
thisMonth: "Mese" thisMonth: "Mese"
today: "Oggi" today: "Oggi"
@ -333,8 +333,8 @@ monthX: "{month}"
yearX: "{year}" yearX: "{year}"
pages: "Pagine" pages: "Pagine"
integration: "Integrazioni" integration: "Integrazioni"
connectService: "Connessione" connectService: "Connetti"
disconnectService: "Disconnessione " disconnectService: "Disconnetti"
enableLocalTimeline: "Abilita Timeline locale" enableLocalTimeline: "Abilita Timeline locale"
enableGlobalTimeline: "Abilita Timeline federata" enableGlobalTimeline: "Abilita Timeline federata"
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e
@ -342,16 +342,16 @@ disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori
registration: "Iscriviti" registration: "Iscriviti"
enableRegistration: "Permettere nuove registrazioni" enableRegistration: "Permettere nuove registrazioni"
invite: "Invita" invite: "Invita"
driveCapacityPerLocalAccount: "Volume del Drive per utente locale" driveCapacityPerLocalAccount: "Dimensione Drive per utenti locali"
driveCapacityPerRemoteAccount: "Volume del Drive per utente remoto" driveCapacityPerRemoteAccount: "Dimensione Drive per utenti remoti"
inMb: "in Megabytes" inMb: "In megabytes"
iconUrl: "URL di icona (favicon, ecc.)" iconUrl: "URL icona"
bannerUrl: "URL dell'immagine d'intestazione" bannerUrl: "URL dell'immagine banner"
backgroundImageUrl: "URL dello sfondo" backgroundImageUrl: "URL dello sfondo"
basicInfo: "Informazioni fondamentali" basicInfo: "Informazioni di base"
pinnedUsers: "Utenti in evidenza" pinnedUsers: "Utenti in fissati"
pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina pinnedUsersDescription: "Elenca gli utenti che vuoi fissare in cima alla pagina \"\
\"Esplora\", un@ per riga." Esplora\", uno per riga."
pinnedPages: "Pagine in evidenza" pinnedPages: "Pagine in evidenza"
pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima
alla home page del server. Una pagina per riga." alla home page del server. Una pagina per riga."
@ -371,20 +371,20 @@ avoidMultiCaptchaConfirm: "Utilizzare diversi Captcha può causare interferenze.
antennas: "Antenne" antennas: "Antenne"
manageAntennas: "Gestore delle antenne" manageAntennas: "Gestore delle antenne"
name: "Nome" name: "Nome"
antennaSource: "Fonte dell'antenna" antennaSource: "Origine dell'antenna"
antennaKeywords: "Parole chiavi da ricevere" antennaKeywords: "Parole chiavi da ricevere"
antennaExcludeKeywords: "Parole chiavi da escludere" antennaExcludeKeywords: "Parole chiavi da escludere"
antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"AND\"
con un'interruzzione riga indica la condizione \"O\"." . Separare con un'interruzione riga indica la condizione \"OR\"."
notifyAntenna: "Notifica i nuovi post" notifyAntenna: "Notifica i nuovi post"
withFileAntenna: "Solo post con file allegati" withFileAntenna: "Solo post con allegati"
enableServiceworker: "Abilita ServiceWorker" enableServiceworker: "Abilita ServiceWorker"
antennaUsersDescription: "Inserisci solo un nome utente per riga" antennaUsersDescription: "Inserisci solo un nome utente per riga"
caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole" caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole"
withReplies: "Includere le risposte" withReplies: "Includere le risposte"
connectedTo: "Stai seguendo questi account" connectedTo: "Stai seguendo questi account"
notesAndReplies: "Post e risposte" notesAndReplies: "Post e risposte"
withFiles: "Con file in allegato" withFiles: "Con file allegati"
silence: "Silenzia" silence: "Silenzia"
silenceConfirm: "Vuoi davvero silenziare l'utente?" silenceConfirm: "Vuoi davvero silenziare l'utente?"
unsilence: "Riattiva" unsilence: "Riattiva"
@ -433,7 +433,7 @@ invites: "Inviti"
groupName: "Nome del gruppo" groupName: "Nome del gruppo"
members: "Membri" members: "Membri"
transfer: "Trasferisci" transfer: "Trasferisci"
messagingWithUser: "Iniziare una chat con un altr@ utente" messagingWithUser: "Chat privata"
messagingWithGroup: "Chattare in gruppo" messagingWithGroup: "Chattare in gruppo"
title: "Titolo" title: "Titolo"
text: "Testo" text: "Testo"
@ -452,8 +452,8 @@ invitations: "Invita"
invitationCode: "Codice di invito" invitationCode: "Codice di invito"
checking: "Confermando" checking: "Confermando"
available: "Consigliati" available: "Consigliati"
unavailable: "Il nome utente è già in uso" unavailable: "Nome già in uso"
usernameInvalidFormat: "Il nome utente può contenere solo lettere, numeri e '_'" usernameInvalidFormat: "Puoi usare solo lettere maiuscole, minuscole, numeri e '_'"
tooShort: "Troppo breve" tooShort: "Troppo breve"
tooLong: "Troppo lungo" tooLong: "Troppo lungo"
weakPassword: "Password debole" weakPassword: "Password debole"
@ -556,14 +556,14 @@ scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScr
output: "Uscita" output: "Uscita"
script: "Script" script: "Script"
disablePagesScript: "Disabilita AiScript nelle pagine" disablePagesScript: "Disabilita AiScript nelle pagine"
updateRemoteUser: "Aggiornare le informazioni di utente remot@" updateRemoteUser: "Aggiorna le informazioni dell'utente remoto"
deleteAllFiles: "Elimina tutti i file" deleteAllFiles: "Elimina tutti i file"
deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?" deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?"
removeAllFollowing: "Smetti di seguire tutti" removeAllFollowing: "Smetti di seguire tutti"
removeAllFollowingDescription: "Smetti di seguire tutti gli account del server {host}. removeAllFollowingDescription: "Smetti di seguire tutti gli account del server {host}.
È utile specialmente se il server non esiste più." È utile specialmente se il server non esiste più."
userSuspended: "L'utente è sospes@." userSuspended: "L'utente è sospeso."
userSilenced: "L'utente è silenziat@." userSilenced: "L'utente è silenziato."
yourAccountSuspendedTitle: "Questo account è sospeso." yourAccountSuspendedTitle: "Questo account è sospeso."
yourAccountSuspendedDescription: "Questo account è stato sospeso a causa di una violazione yourAccountSuspendedDescription: "Questo account è stato sospeso a causa di una violazione
dei termini di servizio del server. Contattare l'amministrazione per i dettagli. dei termini di servizio del server. Contattare l'amministrazione per i dettagli.
@ -906,8 +906,7 @@ _ad:
reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso" reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso"
_forgotPassword: _forgotPassword:
enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo
profilo. Il collegamento necessario per ripristinare la password verrà inviato profilo. Il di link ripristino della password verrà inviato a questo indirizzo."
a questo indirizzo."
ifNoEmail: "Se non hai registrato alcun indirizzo e-mail, contatta l'admin del server." ifNoEmail: "Se non hai registrato alcun indirizzo e-mail, contatta l'admin del server."
contactAdmin: "Poiché questo server non permette l'uso di indirizzi mail, contatta contactAdmin: "Poiché questo server non permette l'uso di indirizzi mail, contatta
l'admin per poter ripristinare la password." l'admin per poter ripristinare la password."
@ -970,7 +969,7 @@ _mfm:
blockCode: "Codice (blocco)" blockCode: "Codice (blocco)"
inlineMath: "Espressione matematica(Immersione)" inlineMath: "Espressione matematica(Immersione)"
blockMath: "Formula matematica (blocco)" blockMath: "Formula matematica (blocco)"
quote: "Cita il nota" quote: "Citazione"
emoji: "Emoji personalizzati" emoji: "Emoji personalizzati"
search: "Cerca" search: "Cerca"
flip: "Inverti" flip: "Inverti"
@ -989,8 +988,8 @@ _mfm:
x4: "Estremamente più grande" x4: "Estremamente più grande"
x4Description: "Mostra il contenuto estremamente più ingrandito." x4Description: "Mostra il contenuto estremamente più ingrandito."
blur: "Sfocatura" blur: "Sfocatura"
blurDescription: "È possibile rendere sfocato il contenuto. Spostando il cursore blurDescription: "È possibile rendere sfocato il contenuto. Diventerà visibile al
su di esso tornerà visibile chiaramente." passaggio del puntatore."
font: "Tipo di carattere" font: "Tipo di carattere"
fontDescription: "Puoi scegliere il tipo di carattere per il contenuto." fontDescription: "Puoi scegliere il tipo di carattere per il contenuto."
rainbow: "Arcobaleno" rainbow: "Arcobaleno"
@ -1123,7 +1122,7 @@ _theme:
header: "Intestazione" header: "Intestazione"
navBg: "Sfondo della barra laterale" navBg: "Sfondo della barra laterale"
navFg: "Testo della barra laterale" navFg: "Testo della barra laterale"
navHoverFg: "Testo della barra laterale (al passaggio del mouse)" navHoverFg: "Testo della barra laterale (hover)"
navActive: "Testo della barra laterale (attivo)" navActive: "Testo della barra laterale (attivo)"
navIndicator: "Indicatore di barra laterale" navIndicator: "Indicatore di barra laterale"
link: "Link" link: "Link"
@ -1138,19 +1137,19 @@ _theme:
infoWarnFg: "Testo di avviso" infoWarnFg: "Testo di avviso"
cwBg: "Sfondo del CW" cwBg: "Sfondo del CW"
cwFg: "Testo del pulsante CW" cwFg: "Testo del pulsante CW"
cwHoverBg: "Sfondo del pulsante CW (sorvolato)" cwHoverBg: "Sfondo del pulsante CW (hover)"
toastBg: "Sfondo di notifica a comparsa" toastBg: "Sfondo di notifica a comparsa"
toastFg: "Testo di notifica a comparsa" toastFg: "Testo di notifica a comparsa"
buttonBg: "Sfondo del pulsante" buttonBg: "Sfondo del pulsante"
buttonHoverBg: "Sfondo del pulsante (sorvolato)" buttonHoverBg: "Sfondo del pulsante (hover)"
inputBorder: "Inquadra casella di testo" inputBorder: "Inquadra casella di testo"
listItemHoverBg: "Sfondo della voce di elenco (sorvolato)" listItemHoverBg: "Sfondo della voce di elenco (hover)"
driveFolderBg: "Sfondo della cartella di disco" driveFolderBg: "Sfondo della cartella di disco"
badge: "Distintivo" badge: "Distintivo"
messageBg: "Sfondo della chat" messageBg: "Sfondo della chat"
modalBg: Sfondo modale modalBg: Sfondo modale
scrollbarHandle: Barra di scorrimento scrollbarHandle: Barra di scorrimento
scrollbarHandleHover: Barra di scorrimento (Hover) scrollbarHandleHover: Barra di scorrimento (hover)
accent: Accento accent: Accento
fgHighlighted: Testo evidenziato fgHighlighted: Testo evidenziato
accentLighten: Accento (chiaro) accentLighten: Accento (chiaro)
@ -1512,7 +1511,7 @@ _pages:
width: "Larghezza" width: "Larghezza"
height: "Altezza" height: "Altezza"
id: Canvas ID id: Canvas ID
note: "Post embedded" note: "Post integrato"
_note: _note:
id: "Post ID" id: "Post ID"
idDescription: "In alternativa puoi incollare qui l'URL del post." idDescription: "In alternativa puoi incollare qui l'URL del post."
@ -1670,7 +1669,7 @@ _pages:
arg2: Valore massimo arg2: Valore massimo
strLen: Lunghezza del testo strLen: Lunghezza del testo
join: Concatenazione testo join: Concatenazione testo
splitStrByLine: Suddividi il testo al fine riga splitStrByLine: Suddividi su più righe
subtract: Sottrazione subtract: Sottrazione
lt: < A è minore di B lt: < A è minore di B
gt: '> A è maggiore di B' gt: '> A è maggiore di B'
@ -1741,7 +1740,7 @@ _notification:
fileUploaded: "File caricato correttamente" fileUploaded: "File caricato correttamente"
youGotMention: "{name} ti ha menzionato" youGotMention: "{name} ti ha menzionato"
youGotReply: "{name} ti ha risposto" youGotReply: "{name} ti ha risposto"
youGotQuote: "{name} ha citato il tuo Nota e ha detto" youGotQuote: "{name} ti ha citato"
youRenoted: "Boost da {name}" youRenoted: "Boost da {name}"
youGotPoll: "{name} ha votato" youGotPoll: "{name} ha votato"
youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio"
@ -1789,7 +1788,7 @@ _deck:
widgets: "Widget" widgets: "Widget"
notifications: "Notifiche" notifications: "Notifiche"
tl: "Timeline" tl: "Timeline"
antenna: "Antenne" antenna: "Antenna"
list: "Liste" list: "Liste"
mentions: "Menzioni" mentions: "Menzioni"
direct: "Messaggi diretti" direct: "Messaggi diretti"
@ -2060,7 +2059,7 @@ shuffle: Casuale
subscribePushNotification: Abilita le notifiche push subscribePushNotification: Abilita le notifiche push
unsubscribePushNotification: Disabilita le notifiche push unsubscribePushNotification: Disabilita le notifiche push
pushNotificationAlreadySubscribed: Le notifiche push sono già abilitate pushNotificationAlreadySubscribed: Le notifiche push sono già abilitate
driveCapOverrideCaption: Imposta la capacità predefinita inserendo il valore 0. driveCapOverrideCaption: Reimposta la capacità predefinita inserendo il valore 0.
numberOfPageCacheDescription: Aumentare questo numero migliorerà l'esperienza degli numberOfPageCacheDescription: Aumentare questo numero migliorerà l'esperienza degli
utenti ma aumenterà il carico sul server e l'uso di memoria. utenti ma aumenterà il carico sul server e l'uso di memoria.
type: Tipo type: Tipo

View file

@ -1,7 +1,7 @@
---
_lang_: "Português" _lang_: "Português"
headlineFirefish: "Uma rede ligada por notas" headlineFirefish: "Uma rede ligada por notas"
introFirefish: "Bem-vindo! Firefish é um serviço de microblogue descentralizado de código aberto.\nCria \"notas\" e partilha o que te ocorre com todos à tua volta. 📡\nCom \"reações\" podes também expressar logo o que sentes às notas de todos. 👍\nExploremos um novo mundo! 🚀" introFirefish: "Bem-vindo! Firefish é um serviço de microblogue descentralizado de
código aberto, gratuito para sempre! 🚀"
monthAndDay: "{day}/{month}" monthAndDay: "{day}/{month}"
search: "Buscar" search: "Buscar"
notifications: "Notificações" notifications: "Notificações"
@ -44,7 +44,8 @@ copyContent: "Copiar conteúdos"
copyLink: "Copiar hiperligação" copyLink: "Copiar hiperligação"
delete: "Eliminar" delete: "Eliminar"
deleteAndEdit: "Eliminar e editar" deleteAndEdit: "Eliminar e editar"
deleteAndEditConfirm: "Tens a certeza que pretendes eliminar esta nota e editá-la? Irás perder todas as suas reações, renotas e respostas." deleteAndEditConfirm: "Tens a certeza que pretendes eliminar esta nota e editá-la?
Irás perder todas as suas reações, renotas e respostas."
addToList: "Adicionar a lista" addToList: "Adicionar a lista"
sendMessage: "Enviar uma mensagem" sendMessage: "Enviar uma mensagem"
copyUsername: "Copiar nome de utilizador" copyUsername: "Copiar nome de utilizador"
@ -64,9 +65,11 @@ import: "Importar"
export: "Exportar" export: "Exportar"
files: "Ficheiros" files: "Ficheiros"
download: "Descarregar" download: "Descarregar"
driveFileDeleteConfirm: "Tens a certeza que pretendes apagar o ficheiro \"{name}\"? As notas que tenham este ficheiro anexado serão também apagadas." driveFileDeleteConfirm: "Tens a certeza que pretendes apagar o ficheiro \"{name}\"\
? As notas que tenham este ficheiro anexado serão também apagadas."
unfollowConfirm: "Tens a certeza que queres deixar de seguir {name}?" unfollowConfirm: "Tens a certeza que queres deixar de seguir {name}?"
exportRequested: "Pediste uma exportação. Este processo pode demorar algum tempo. Será adicionado à tua Drive após a conclusão do processo." exportRequested: "Pediste uma exportação. Este processo pode demorar algum tempo.
Será adicionado à tua Drive após a conclusão do processo."
importRequested: "Pediste uma importação. Este processo pode demorar algum tempo." importRequested: "Pediste uma importação. Este processo pode demorar algum tempo."
lists: "Listas" lists: "Listas"
noLists: "Não tens nenhuma lista" noLists: "Não tens nenhuma lista"
@ -81,9 +84,12 @@ error: "Erro"
somethingHappened: "Ocorreu um erro" somethingHappened: "Ocorreu um erro"
retry: "Tentar novamente" retry: "Tentar novamente"
pageLoadError: "Ocorreu um erro ao carregar a página." pageLoadError: "Ocorreu um erro ao carregar a página."
pageLoadErrorDescription: "Isto é normalmente causado por erros de rede ou pela cache do browser. Experimenta limpar a cache e tenta novamente após algum tempo." pageLoadErrorDescription: "Isto é normalmente causado por erros de rede ou pela cache
serverIsDead: "O servidor não está respondendo. Por favor espere um pouco e tente novamente." do browser. Experimenta limpar a cache e tenta novamente após algum tempo."
youShouldUpgradeClient: "Para visualizar essa página, por favor recarregue-a para atualizar seu cliente." serverIsDead: "O servidor não está respondendo. Por favor espere um pouco e tente
novamente."
youShouldUpgradeClient: "Para visualizar essa página, por favor recarregue-a para
atualizar seu cliente."
enterListName: "Insira um nome para a lista" enterListName: "Insira um nome para a lista"
privacy: "Privacidade" privacy: "Privacidade"
makeFollowManuallyApprove: "Pedidos de seguimento precisam ser aprovados" makeFollowManuallyApprove: "Pedidos de seguimento precisam ser aprovados"
@ -108,7 +114,8 @@ sensitive: "Conteúdo sensível"
add: "Adicionar" add: "Adicionar"
reaction: "Reações" reaction: "Reações"
reactionSetting: "Quais reações a mostrar no selecionador de reações" reactionSetting: "Quais reações a mostrar no selecionador de reações"
reactionSettingDescription2: "Arraste para reordenar, clique para excluir, pressione + para adicionar." reactionSettingDescription2: "Arraste para reordenar, clique para excluir, pressione
+ para adicionar."
rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas" rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas"
attachCancel: "Remover anexo" attachCancel: "Remover anexo"
markAsSensitive: "Marcar como sensível" markAsSensitive: "Marcar como sensível"
@ -137,13 +144,18 @@ emojiUrl: "URL do Emoji"
addEmoji: "Adicionar um Emoji" addEmoji: "Adicionar um Emoji"
settingGuide: "Guia de configuração" settingGuide: "Guia de configuração"
cacheRemoteFiles: "Memória transitória de arquivos remotos" cacheRemoteFiles: "Memória transitória de arquivos remotos"
cacheRemoteFilesDescription: "Se você desabilitar essa configuração, os arquivos remotos não serão armazenados em memória transitória e serão vinculados diretamente. Economiza o armazenamento do servidor, mas não gera miniaturas, o que aumenta o tráfego." cacheRemoteFilesDescription: "Se você desabilitar essa configuração, os arquivos remotos
não serão armazenados em memória transitória e serão vinculados diretamente. Economiza
o armazenamento do servidor, mas não gera miniaturas, o que aumenta o tráfego."
flagAsBot: "Marcar conta como robô" flagAsBot: "Marcar conta como robô"
flagAsBotDescription: "Se esta conta for operada por um programa, ative este sinalizador. Quando ativado, serve como um sinalizador para evitar o encadeamento de reações para outros programadores, e o manuseio do sistema do Firefish é adequado para bots." flagAsBotDescription: "Se esta conta for operada por um programa, ative este sinalizador.
Quando ativado, serve como um sinalizador para evitar o encadeamento de reações
para outros programadores, e o manuseio do sistema do Firefish é adequado para bots."
flagAsCat: "Marcar conta como gato" flagAsCat: "Marcar conta como gato"
flagAsCatDescription: "Ative essa opção para marcar essa conta como gato." flagAsCatDescription: "Ative essa opção para marcar essa conta como gato."
flagShowTimelineReplies: "Mostrar respostas na linha de tempo" flagShowTimelineReplies: "Mostrar respostas na linha de tempo"
flagShowTimelineRepliesDescription: "Quando ativado, a linha do tempo mostra as respostas às outras notas do utilizador, além da nota do utilizador." flagShowTimelineRepliesDescription: "Quando ativado, a linha do tempo mostra as respostas
às outras notas do utilizador, além da nota do utilizador."
autoAcceptFollowed: "Aprove automaticamente os seguidores dos seguintes utilizadores" autoAcceptFollowed: "Aprove automaticamente os seguidores dos seguintes utilizadores"
addAccount: "Adicionar Conta" addAccount: "Adicionar Conta"
loginFailed: "Não consegui logar" loginFailed: "Não consegui logar"
@ -156,7 +168,10 @@ searchWith: "Buscar: {q}"
youHaveNoLists: "Não tem nenhuma lista" youHaveNoLists: "Não tem nenhuma lista"
followConfirm: "Tem certeza que quer deixar de seguir {name}?" followConfirm: "Tem certeza que quer deixar de seguir {name}?"
proxyAccount: "Conta proxy" proxyAccount: "Conta proxy"
proxyAccountDescription: "Uma conta proxy é uma conta que atua como seguidora remota para utilizadores sob determinadas condições. Por exemplo, quando um utilizador lista um utilizador remoto, a atividade não será entregue à instância, a menos que alguém esteja seguindo o utilizador listado, portanto, a conta proxy deve seguir." proxyAccountDescription: "Uma conta proxy é uma conta que atua como seguidora remota
para utilizadores sob determinadas condições. Por exemplo, quando um utilizador
lista um utilizador remoto, a atividade não será entregue à instância, a menos que
alguém esteja seguindo o utilizador listado, portanto, a conta proxy deve seguir."
host: "hospedeiro" host: "hospedeiro"
selectUser: "Selecionar utilizador" selectUser: "Selecionar utilizador"
recipient: "Morada" recipient: "Morada"
@ -186,11 +201,15 @@ instanceInfo: "Informações da instância"
statistics: "Estatisticas" statistics: "Estatisticas"
clearQueue: "Limpar a fila" clearQueue: "Limpar a fila"
clearQueueConfirmTitle: "Quer limpar a fila?" clearQueueConfirmTitle: "Quer limpar a fila?"
clearQueueConfirmText: "Postagens não entregues não serão mais entregues. Normalmente você não precisa fazer isso." clearQueueConfirmText: "Postagens não entregues não serão mais entregues. Normalmente
você não precisa fazer isso."
clearCachedFiles: "Limpar memória transitória" clearCachedFiles: "Limpar memória transitória"
clearCachedFilesConfirm: "Tem certeza de que deseja excluir todos os arquivos remotos armazenados em memória transitória?" clearCachedFilesConfirm: "Tem certeza de que deseja excluir todos os arquivos remotos
armazenados em memória transitória?"
blockedInstances: "Instância bloqueada" blockedInstances: "Instância bloqueada"
blockedInstancesDescription: "Defina os anfitriões das instâncias que deseja bloquear, separados por quebras de linha. Uma instância bloqueada não poderá interagir com esta instância." blockedInstancesDescription: "Defina os anfitriões das instâncias que deseja bloquear,
separados por quebras de linha. Uma instância bloqueada não poderá interagir com
esta instância."
muteAndBlock: "Silenciar e bloquear" muteAndBlock: "Silenciar e bloquear"
mutedUsers: "Silenciar utilizador" mutedUsers: "Silenciar utilizador"
blockedUsers: "Utilizadores bloqueados" blockedUsers: "Utilizadores bloqueados"
@ -238,7 +257,9 @@ saved: "Salvo"
messaging: "Chat" messaging: "Chat"
upload: "Enviando" upload: "Enviando"
keepOriginalUploading: "Manter a imagem original" keepOriginalUploading: "Manter a imagem original"
keepOriginalUploadingDescription: "Mantenha a versão original ao carregar a imagem. Quando desligado, a imagem para publicação na web será gerada no navegador no momento do upload." keepOriginalUploadingDescription: "Mantenha a versão original ao carregar a imagem.
Quando desligado, a imagem para publicação na web será gerada no navegador no momento
do upload."
fromDrive: "\nDa unidade" fromDrive: "\nDa unidade"
fromUrl: "Da URL" fromUrl: "Da URL"
uploadFromUrl: "Carregamento de URL" uploadFromUrl: "Carregamento de URL"
@ -262,8 +283,8 @@ yearsOld: "{age} anos"
registeredDate: "Data de registro" registeredDate: "Data de registro"
location: "Lugar, colocar" location: "Lugar, colocar"
theme: "tema" theme: "tema"
themeForLightMode: "Temas usados no modo de luz" themeForLightMode: "Tema a usar no Modo Diurno"
themeForDarkMode: "Temas usados no modo escuro" themeForDarkMode: "Temas usados no Modo Noturno"
light: "Claro" light: "Claro"
dark: "Escuro" dark: "Escuro"
lightThemes: "Tema claro" lightThemes: "Tema claro"
@ -271,7 +292,7 @@ darkThemes: "Tema escuro"
syncDeviceDarkMode: "Sincronize com o modo escuro do dispositivo" syncDeviceDarkMode: "Sincronize com o modo escuro do dispositivo"
drive: "Unidades" drive: "Unidades"
fileName: "Nome do Ficheiro" fileName: "Nome do Ficheiro"
selectFile: "Selecione os arquivos" selectFile: "Selecione o arquivo"
selectFiles: "Selecione os arquivos" selectFiles: "Selecione os arquivos"
selectFolder: "Selecionar uma pasta" selectFolder: "Selecionar uma pasta"
selectFolders: "Selecionar uma pasta" selectFolders: "Selecionar uma pasta"
@ -286,8 +307,9 @@ emptyFolder: "A pasta está vazia"
unableToDelete: "Não é possível eliminar" unableToDelete: "Não é possível eliminar"
inputNewFileName: "Por favor, digite um novo nome para a pasta!" inputNewFileName: "Por favor, digite um novo nome para a pasta!"
inputNewDescription: "Insira uma nova legenda" inputNewDescription: "Insira uma nova legenda"
inputNewFolderName: "Por favor, digite um novo nome para a pasta!" inputNewFolderName: "Por favor, digite um novo nome para a pasta"
circularReferenceFolder: "A pasta de destino é uma subpasta da pasta que você deseja mover." circularReferenceFolder: "A pasta de destino é uma subpasta da pasta que você deseja
mover."
hasChildFilesOrFolders: "Esta pasta não está vazia e não pode ser excluída." hasChildFilesOrFolders: "Esta pasta não está vazia e não pode ser excluída."
copyUrl: "Copiar URL" copyUrl: "Copiar URL"
rename: "Renomear" rename: "Renomear"
@ -321,7 +343,8 @@ connectService: "Conectar"
disconnectService: "Desconectar" disconnectService: "Desconectar"
enableLocalTimeline: "Ativar linha do tempo local" enableLocalTimeline: "Ativar linha do tempo local"
enableGlobalTimeline: "Ativar linha do tempo global" enableGlobalTimeline: "Ativar linha do tempo global"
disablingTimelinesInfo: "Se você desabilitar essas linhas do tempo, administradores e moderadores ainda poderão usá-las por conveniência." disablingTimelinesInfo: "Se você desabilitar essas linhas do tempo, administradores
e moderadores ainda poderão usá-las por conveniência."
registration: "Registar" registration: "Registar"
enableRegistration: "Permitir que qualquer pessoa se registre" enableRegistration: "Permitir que qualquer pessoa se registre"
invite: "Convidar" invite: "Convidar"
@ -333,9 +356,11 @@ bannerUrl: "URL da imagem do banner"
backgroundImageUrl: "URL da imagem de fundo" backgroundImageUrl: "URL da imagem de fundo"
basicInfo: "Informações básicas" basicInfo: "Informações básicas"
pinnedUsers: "Utilizador fixado" pinnedUsers: "Utilizador fixado"
pinnedUsersDescription: "Descreva os utilizadores que você deseja fixar na página \"Localizar\", etc., separados por quebras de linha." pinnedUsersDescription: "Descreva os utilizadores que você deseja fixar na página
\"Localizar\", etc., separados por quebras de linha."
pinnedPages: "Página fixada" pinnedPages: "Página fixada"
pinnedPagesDescription: "Descreva o caminho da página que você deseja fixar na página superior da instância, separada por quebras de linha." pinnedPagesDescription: "Descreva o caminho da página que você deseja fixar na página
superior da instância, separada por quebras de linha."
pinnedClipId: "ID do clipe a ser fixado" pinnedClipId: "ID do clipe a ser fixado"
pinnedNotes: "Post fixado" pinnedNotes: "Post fixado"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
@ -346,18 +371,21 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Habilitar reCAPTCHA" enableRecaptcha: "Habilitar reCAPTCHA"
recaptchaSiteKey: "Chave do sítio web" recaptchaSiteKey: "Chave do sítio web"
recaptchaSecretKey: "Chave secreta" recaptchaSecretKey: "Chave secreta"
avoidMultiCaptchaConfirm: "O uso de vários captchas pode causar interferência. Deseja desativar outros captchas? Você também pode cancelar e deixar vários captchas ativados." avoidMultiCaptchaConfirm: "O uso de vários captchas pode causar interferência. Deseja
desativar outros captchas? Você também pode cancelar e deixar vários captchas ativados."
antennas: "Antenas" antennas: "Antenas"
manageAntennas: "Gestão de antena" manageAntennas: "Gestão de antena"
name: "Nome" name: "Nome"
antennaSource: "Origem de entrada" antennaSource: "Origem de entrada"
antennaKeywords: "Palavras-chave recebidas" antennaKeywords: "Palavras-chave recebidas"
antennaExcludeKeywords: "Palavras-chave negativas" antennaExcludeKeywords: "Palavras-chave negativas"
antennaKeywordsDescription: "Se você separá-lo com um espaço, será uma especificação AND, e se você separá-lo com uma quebra de linha, será uma especificação OR." antennaKeywordsDescription: "Se você separá-lo com um espaço, será uma especificação
AND, e se você separá-lo com uma quebra de linha, será uma especificação OR."
notifyAntenna: "Notificar novas notas" notifyAntenna: "Notificar novas notas"
withFileAntenna: "Apenas notas com arquivos anexados" withFileAntenna: "Apenas notas com arquivos anexados"
enableServiceworker: "Ative as notificações push para o seu navegador" enableServiceworker: "Ative as notificações push para o seu navegador"
antennaUsersDescription: "Especificar nomes de utilizador separados por quebras de linha" antennaUsersDescription: "Especificar nomes de utilizador separados por quebras de
linha"
caseSensitive: "Maiúsculas e minúsculas" caseSensitive: "Maiúsculas e minúsculas"
withReplies: "Incluindo resposta" withReplies: "Incluindo resposta"
connectedTo: "Você está conectado à seguinte conta" connectedTo: "Você está conectado à seguinte conta"
@ -433,15 +461,19 @@ showFeaturedNotesInTimeline: "Mostrar notas recomendadas na linha do tempo"
objectStorage: "Armazenamento de objetos" objectStorage: "Armazenamento de objetos"
useObjectStorage: "Usar armazenamento de objetos" useObjectStorage: "Usar armazenamento de objetos"
objectStorageBaseUrl: "URL base" objectStorageBaseUrl: "URL base"
objectStorageBaseUrlDesc: "O URL usado para referência. Se você estiver usando um CDN ou Proxy, seu URL, S3:'https: // <bucket> .s3.amazonaws.com', GCS, etc .:'https://storage.googleapis.com/ <bucket>' ." objectStorageBaseUrlDesc: "O URL usado para referência. Se você estiver usando um
CDN ou Proxy, seu URL, S3:'https: // <bucket> .s3.amazonaws.com', GCS, etc .:'https://storage.googleapis.com/
<bucket>' ."
objectStorageBucket: "Bucket" objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Especifique o nome do bucket do serviço a ser usado." objectStorageBucketDesc: "Especifique o nome do bucket do serviço a ser usado."
objectStoragePrefix: "Prefixo" objectStoragePrefix: "Prefixo"
objectStoragePrefixDesc: "Ele é armazenado neste diretório de prefixo." objectStoragePrefixDesc: "Ele é armazenado neste diretório de prefixo."
objectStorageEndpoint: "Ponto final" objectStorageEndpoint: "Ponto final"
objectStorageEndpointDesc: "Especifique vazio para S3, caso contrário, especifique o ponto final para cada serviço. Especifique como'<host>'ou'<host>: <port>'." objectStorageEndpointDesc: "Especifique vazio para S3, caso contrário, especifique
o ponto final para cada serviço. Especifique como'<host>'ou'<host>: <port>'."
objectStorageRegion: "Região" objectStorageRegion: "Região"
objectStorageRegionDesc: "Especifique uma região como 'xx-east-1'. Caso seu serviço não tenha o conceito de região, ele deve estar vazio ou 'us-east-1'." objectStorageRegionDesc: "Especifique uma região como 'xx-east-1'. Caso seu serviço
não tenha o conceito de região, ele deve estar vazio ou 'us-east-1'."
objectStorageUseSSL: "Usar SSL" objectStorageUseSSL: "Usar SSL"
objectStorageUseSSLDesc: "Desative-o se não quiser usar https para conexões de API" objectStorageUseSSLDesc: "Desative-o se não quiser usar https para conexões de API"
objectStorageUseProxy: "Usar proxy" objectStorageUseProxy: "Usar proxy"
@ -449,7 +481,8 @@ objectStorageUseProxyDesc: "Se você não usa proxy para conexão de API, desati
objectStorageSetPublicRead: "Definir 'public-read' ao fazer o upload" objectStorageSetPublicRead: "Definir 'public-read' ao fazer o upload"
serverLogs: "Registro do servidor" serverLogs: "Registro do servidor"
deleteAll: "Apagar Tudo" deleteAll: "Apagar Tudo"
showFixedPostForm: "Exibir o formulário de postagem na parte superior da linha do tempo" showFixedPostForm: "Exibir o formulário de postagem na parte superior da linha do
tempo"
newNoteRecived: "Nova nota recebida" newNoteRecived: "Nova nota recebida"
sounds: "Sons" sounds: "Sons"
listen: "Ouvir" listen: "Ouvir"
@ -618,7 +651,8 @@ _pages:
_dailyRannum: _dailyRannum:
arg1: "Valor mínimo" arg1: "Valor mínimo"
arg2: "Valor máximo" arg2: "Valor máximo"
dailyRandomPick: "Escolher aleatoriamente de uma lista (Muda uma vez por dia para cada usuário)" dailyRandomPick: "Escolher aleatoriamente de uma lista (Muda uma vez por dia
para cada usuário)"
_dailyRandomPick: _dailyRandomPick:
arg1: "Listas" arg1: "Listas"
seedRandom: "Aleatório (com semente)" seedRandom: "Aleatório (com semente)"
@ -634,7 +668,8 @@ _pages:
_seedRandomPick: _seedRandomPick:
arg1: "Semente" arg1: "Semente"
arg2: "Listas" arg2: "Listas"
DRPWPM: "Escolher aleatoriamente de uma lista ponderada (Muda uma vez por dia para cada usuário)" DRPWPM: "Escolher aleatoriamente de uma lista ponderada (Muda uma vez por dia
para cada usuário)"
_DRPWPM: _DRPWPM:
arg1: "Lista de texto" arg1: "Lista de texto"
pick: "Escolhe a partir da lista" pick: "Escolhe a partir da lista"
@ -665,7 +700,8 @@ _pages:
_for: _for:
arg1: "Número de repetições" arg1: "Número de repetições"
arg2: "Ação" arg2: "Ação"
typeError: "Espaço {slot} aceita valores de tipo \"{expect}\", mas o valor dado é do tipo \"{actual}\"!" typeError: "Espaço {slot} aceita valores de tipo \"{expect}\", mas o valor dado
é do tipo \"{actual}\"!"
thereIsEmptySlot: "O espaço {slot} está vazio!" thereIsEmptySlot: "O espaço {slot} está vazio!"
types: types:
string: "Texto" string: "Texto"
@ -730,3 +766,5 @@ _deck:
list: "Listas" list: "Listas"
mentions: "Menções" mentions: "Menções"
direct: "Notas diretas" direct: "Notas diretas"
editNote: Editar post
edited: Editado a {date} às {time}

View file

@ -1,16 +1,16 @@
{ {
"name": "firefish", "name": "firefish",
"version": "1.0.5-dev7", "version": "1.0.5-dev8",
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.joinfirefish.org/firefish/firefish.git" "url": "https://git.joinfirefish.org/firefish/firefish.git"
}, },
"packageManager": "pnpm@8.7.0", "packageManager": "pnpm@8.7.1",
"private": true, "private": true,
"scripts": { "scripts": {
"rebuild": "pnpm run clean && pnpm node ./scripts/build-greet.js && pnpm -r --parallel run build && pnpm run gulp", "rebuild": "pnpm run clean && ./scripts/build-greet.sh && pnpm -r --parallel run build && pnpm run gulp",
"build": "pnpm node ./scripts/build-greet.js && pnpm -r --parallel run build && pnpm run gulp", "build": "./scripts/build-greet.sh && pnpm -r --parallel run build && pnpm run gulp",
"start": "pnpm --filter backend run start", "start": "pnpm --filter backend run start",
"start:test": "pnpm --filter backend run start:test", "start:test": "pnpm --filter backend run start:test",
"init": "pnpm run migrate", "init": "pnpm run migrate",
@ -40,18 +40,19 @@
"chokidar": "^3.3.1" "chokidar": "^3.3.1"
}, },
"dependencies": { "dependencies": {
"@bull-board/api": "5.7.2", "@bull-board/api": "5.8.0",
"@bull-board/ui": "5.7.2", "@bull-board/ui": "5.8.0",
"@napi-rs/cli": "^2.16.2", "@napi-rs/cli": "^2.16.2",
"@tensorflow/tfjs": "^3.21.0", "@tensorflow/tfjs": "^4.10.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"seedrandom": "^3.0.5" "seedrandom": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.0.0",
"@types/gulp": "4.0.13", "@types/gulp": "4.0.13",
"@types/gulp-rename": "2.0.2", "@types/gulp-rename": "2.0.2",
"@types/node": "20.4.9", "@types/node": "20.5.8",
"chalk": "4.1.2", "add": "2.0.6",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.11.0", "cypress": "10.11.0",
"execa": "5.1.1", "execa": "5.1.1",
@ -61,8 +62,8 @@
"gulp-replace": "1.1.4", "gulp-replace": "1.1.4",
"gulp-terser": "2.1.0", "gulp-terser": "2.1.0",
"install-peers": "^1.0.4", "install-peers": "^1.0.4",
"rome": "^12.1.3", "pnpm": "8.7.1",
"start-server-and-test": "1.15.2", "start-server-and-test": "1.15.2",
"typescript": "5.1.6" "typescript": "5.2.2"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

View file

@ -44,8 +44,8 @@
"test": "pnpm run cargo:test && pnpm run build:napi && ava", "test": "pnpm run cargo:test && pnpm run build:napi && ava",
"universal": "napi universal", "universal": "napi universal",
"version": "napi version", "version": "napi version",
"format": "cargo fmt --all", "format": "cargo fmt --all -- --check",
"lint": "cargo clippy --fix", "lint": "cargo clippy --fix --allow-dirty --allow-staged && cargo fmt --all -- --check",
"cargo:test": "pnpm run cargo:unit && pnpm run cargo:integration", "cargo:test": "pnpm run cargo:unit && pnpm run cargo:integration",
"cargo:unit": "cargo test unit_test && cargo test -F napi unit_test", "cargo:unit": "cargo test unit_test && cargo test -F napi unit_test",
"cargo:integration": "cargo test -F noarray int_test -- --test-threads=1" "cargo:integration": "cargo test -F noarray int_test -- --test-threads=1"

File diff suppressed because one or more lines are too long

View file

@ -17,19 +17,19 @@
"build": "pnpm swc src -d built -D", "build": "pnpm swc src -d built -D",
"build:debug": "pnpm swc src -d built -s -D", "build:debug": "pnpm swc src -d built -s -D",
"watch": "pnpm swc src -d built -D -w", "watch": "pnpm swc src -d built -D -w",
"lint": "pnpm rome check --apply *", "lint": "pnpm biome check --apply **/*.ts ; pnpm run format",
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha", "mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"test": "pnpm run mocha", "test": "pnpm run mocha",
"format": "pnpm rome format * --write" "format": "pnpm biome format * --write"
}, },
"optionalDependencies": { "optionalDependencies": {
"@swc/core-android-arm64": "1.3.11", "@swc/core-android-arm64": "1.3.11",
"@tensorflow/tfjs-node": "3.21.1" "@tensorflow/tfjs-node": "3.21.1"
}, },
"dependencies": { "dependencies": {
"@bull-board/api": "5.7.2", "@bull-board/api": "5.8.0",
"@bull-board/koa": "5.7.2", "@bull-board/koa": "5.8.0",
"@bull-board/ui": "5.7.2", "@bull-board/ui": "5.8.0",
"@discordapp/twemoji": "14.1.2", "@discordapp/twemoji": "14.1.2",
"@elastic/elasticsearch": "7.17.0", "@elastic/elasticsearch": "7.17.0",
"@koa/cors": "3.4.3", "@koa/cors": "3.4.3",
@ -42,18 +42,17 @@
"@tensorflow/tfjs": "^4.2.0", "@tensorflow/tfjs": "^4.2.0",
"adm-zip": "^0.5.10", "adm-zip": "^0.5.10",
"ajv": "8.12.0", "ajv": "8.12.0",
"archiver": "5.3.1", "archiver": "6.0.0",
"argon2": "^0.30.3", "argon2": "^0.31.1",
"autolinker": "4.0.0", "autolinker": "4.0.0",
"autwh": "0.1.0", "autwh": "0.1.0",
"aws-sdk": "2.1413.0", "aws-sdk": "2.1413.0",
"axios": "^1.4.0", "axios": "^1.4.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"bull": "4.11.2",
"cassandra-driver": "4.6.4", "cassandra-driver": "4.6.4",
"bull": "4.11.3",
"cacheable-lookup": "TheEssem/cacheable-lookup", "cacheable-lookup": "TheEssem/cacheable-lookup",
"cbor": "8.1.0",
"chalk": "5.3.0", "chalk": "5.3.0",
"chalk-template": "0.4.0", "chalk-template": "0.4.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
@ -65,19 +64,19 @@
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "17.1.6", "file-type": "18.5.0",
"firefish-js": "workspace:*", "firefish-js": "workspace:*",
"fluent-ffmpeg": "2.1.2", "fluent-ffmpeg": "2.1.2",
"got": "12.5.3", "got": "13.0.0",
"gunzip-maybe": "^1.4.2", "gunzip-maybe": "^1.4.2",
"hpagent": "0.1.2", "hpagent": "1.2.0",
"ioredis": "5.3.2", "ioredis": "5.3.2",
"ip-cidr": "3.1.0", "ip-cidr": "3.1.0",
"is-svg": "4.3.2", "is-svg": "5.0.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsdom": "20.0.3", "jsdom": "22.1.0",
"json5": "2.2.3", "json5": "2.2.3",
"jsonld": "8.2.0", "jsonld": "8.2.1",
"jsrsasign": "10.8.6", "jsrsasign": "10.8.6",
"koa": "2.14.2", "koa": "2.14.2",
"koa-body": "^6.0.1", "koa-body": "^6.0.1",
@ -91,10 +90,10 @@
"koa-slow": "2.1.0", "koa-slow": "2.1.0",
"koa-views": "7.0.2", "koa-views": "7.0.2",
"megalodon": "workspace:*", "megalodon": "workspace:*",
"meilisearch": "0.33.0", "meilisearch": "0.34.1",
"mfm-js": "0.23.3", "mfm-js": "0.23.3",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"msgpackr": "1.9.6", "msgpackr": "1.9.7",
"multer": "1.4.4-lts.1", "multer": "1.4.4-lts.1",
"native-utils": "link:native-utils", "native-utils": "link:native-utils",
"nested-property": "4.0.0", "nested-property": "4.0.0",
@ -106,19 +105,18 @@
"os-utils": "0.0.14", "os-utils": "0.0.14",
"otpauth": "^9.1.4", "otpauth": "^9.1.4",
"parse5": "7.1.2", "parse5": "7.1.2",
"pg": "8.11.2", "pg": "8.11.3",
"private-ip": "2.3.4", "private-ip": "3.0.1",
"probe-image-size": "7.2.3", "probe-image-size": "7.2.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"punycode": "2.3.0", "punycode": "2.3.0",
"pureimage": "0.3.15", "pureimage": "0.4.8",
"qrcode": "1.5.3", "qrcode": "1.5.3",
"qs": "6.11.2", "qs": "6.11.2",
"random-seed": "0.3.0", "random-seed": "0.3.0",
"ratelimiter": "3.4.1", "ratelimiter": "3.4.1",
"re2": "1.20.1", "re2": "1.20.3",
"redis-lock": "0.1.4", "redis-semaphore": "5.5.0",
"redis-semaphore": "5.4.0",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rename": "1.0.4", "rename": "1.0.4",
"rndstr": "1.0.0", "rndstr": "1.0.0",
@ -126,12 +124,12 @@
"sanitize-html": "2.11.0", "sanitize-html": "2.11.0",
"seedrandom": "^3.0.5", "seedrandom": "^3.0.5",
"semver": "7.5.4", "semver": "7.5.4",
"sharp": "0.32.4", "sharp": "0.32.5",
"sonic-channel": "^1.3.1", "sonic-channel": "^1.3.1",
"stringz": "2.1.0", "stringz": "2.1.0",
"summaly": "2.7.0", "summaly": "2.7.0",
"syslog-pro": "1.0.0", "syslog-pro": "1.0.0",
"systeminformation": "5.18.13", "systeminformation": "5.21.3",
"tar-stream": "^3.1.6", "tar-stream": "^3.1.6",
"tesseract.js": "^4.1.1", "tesseract.js": "^4.1.1",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
@ -140,7 +138,7 @@
"typeorm": "0.3.17", "typeorm": "0.3.17",
"ulid": "2.3.0", "ulid": "2.3.0",
"uuid": "9.0.0", "uuid": "9.0.0",
"web-push": "3.6.4", "web-push": "3.6.5",
"websocket": "1.0.34", "websocket": "1.0.34",
"xev": "3.0.2" "xev": "3.0.2"
}, },
@ -149,7 +147,6 @@
"@swc/core": "^1.3.75", "@swc/core": "^1.3.75",
"@types/adm-zip": "^0.5.0", "@types/adm-zip": "^0.5.0",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/cbor": "6.0.0",
"@types/escape-regexp": "0.0.1", "@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.21", "@types/fluent-ffmpeg": "2.1.21",
"@types/js-yaml": "4.0.5", "@types/js-yaml": "4.0.5",

View file

@ -30,40 +30,34 @@ const themeColor = chalk.hex("#31748f");
function greet() { function greet() {
if (!envOption.quiet) { if (!envOption.quiet) {
//#region Firefish logo //#region Firefish logo
const v = `v${meta.version}`;
console.log( console.log(
themeColor( themeColor(
" ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄ ▄▄ ▄▄ ◯ ", "██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
), ),
); );
console.log( console.log(
themeColor( themeColor(
"█ █ █ ▄ █ █ █ █ █ █ █ █ █ ○ ▄ ▄", "██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
), ),
); );
console.log( console.log(
themeColor( themeColor(
"█ ▄▄▄█ █ █ █ █ █ ▄▄▄█ ▄▄▄█ █ ▄▄▄▄▄█ █▄█ █ ⚬ █▄▄ █▄▄ ", "█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
), ),
); );
console.log( console.log(
themeColor( themeColor(
"█ █▄▄▄█ █ █▄▄█▄█ █▄▄▄█ █▄▄▄█ █ █▄▄▄▄▄█ █ ▄▄▄▄▄▄ ▄", "██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
), ),
); );
console.log( console.log(
themeColor( themeColor(
"█ ▄▄▄█ █ ▄▄ █ ▄▄▄█ ▄▄▄█ █▄▄▄▄▄ █ ▄ █ █ █ █▄▄", "██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
), ),
); );
console.log( console.log(
themeColor( themeColor(
"█ █ █ █ █ █ █ █▄▄▄█ █ █ █▄▄▄▄▄█ █ █ █ █ █ ● ● █", "╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
),
);
console.log(
themeColor(
"█▄▄▄█ █▄▄▄█▄▄▄█ █▄█▄▄▄▄▄▄▄█▄▄▄█ █▄▄▄█▄▄▄▄▄▄▄█▄▄█ █▄▄█ ▀▄▄▄▄▄▄▀",
), ),
); );
//#endregion //#endregion

View file

@ -1,2 +1,2 @@
// rome-ignore lint/suspicious/noExplicitAny: i have no idea // biome-ignore lint/suspicious/noExplicitAny: i have no idea
type FIXME = any; type FIXME = any;

View file

@ -1,33 +1,40 @@
import { redisClient } from "../db/redis.js"; import { redisClient } from "../db/redis.js";
import { promisify } from "node:util"; import { Mutex } from "redis-semaphore";
import redisLock from "redis-lock";
/** /**
* Retry delay (ms) for lock acquisition * Retry delay (ms) for lock acquisition
*/ */
const retryDelay = 100; const retryDelay = 100;
const lock: (key: string, timeout?: number) => Promise<() => void> = redisClient
? promisify(redisLock(redisClient, retryDelay))
: async () => () => {};
/** /**
* Get AP Object lock * Get AP Object lock
* @param uri AP object ID * @param uri AP object ID
* @param timeout Lock timeout (ms), The timeout releases previous lock. * @param timeout Lock timeout (ms), The timeout releases previous lock.
* @returns Unlock function * @returns Unlock function
*/ */
export function getApLock(uri: string, timeout = 30 * 1000) { export async function getApLock(uri: string, timeout = 30 * 1000) {
return lock(`ap-object:${uri}`, timeout); const lock = new Mutex(redisClient, `ap-object:${uri}`, {
lockTimeout: timeout,
retryInterval: retryDelay,
});
await lock.acquire();
} }
export function getFetchInstanceMetadataLock( export async function getFetchInstanceMetadataLock(
host: string, host: string,
timeout = 30 * 1000, timeout = 30 * 1000,
) { ) {
return lock(`instance:${host}`, timeout); const lock = new Mutex(redisClient, `instance:${host}`, {
lockTimeout: timeout,
retryInterval: retryDelay,
});
await lock.acquire();
} }
export function getChartInsertLock(lockKey: string, timeout = 30 * 1000) { export async function getChartInsertLock(lockKey: string, timeout = 30 * 1000) {
return lock(`chart-insert:${lockKey}`, timeout); const lock = new Mutex(redisClient, `chart-insert:${lockKey}`, {
lockTimeout: timeout,
retryInterval: retryDelay,
});
await lock.acquire();
} }

View file

@ -32,8 +32,6 @@ export default async function (
// Interrupt if you block the announcement destination // Interrupt if you block the announcement destination
if (await shouldBlockInstance(extractDbHost(uri))) return; if (await shouldBlockInstance(extractDbHost(uri))) return;
const unlock = await getApLock(uri);
try { try {
// Check if something with the same URI is already registered // Check if something with the same URI is already registered
const exist = await fetchNote(uri); const exist = await fetchNote(uri);
@ -60,9 +58,10 @@ export default async function (
throw e; throw e;
} }
if (!(await Notes.isVisibleForMe(renote, actor.id))) if (renote != null && !(await Notes.isVisibleForMe(renote, actor.id))) {
return "skip: invalid actor for this activity"; console.log("skip: invalid actor for this activity");
return;
}
logger.info(`Creating the (Re)Note: ${uri}`); logger.info(`Creating the (Re)Note: ${uri}`);
const activityAudience = await parseAudience( const activityAudience = await parseAudience(
@ -79,6 +78,6 @@ export default async function (
uri, uri,
}); });
} finally { } finally {
unlock(); await getApLock(uri);
} }
} }

View file

@ -31,8 +31,6 @@ export default async function (
} }
} }
const unlock = await getApLock(uri);
try { try {
const exist = await fetchNote(note); const exist = await fetchNote(note);
if (exist) return "skip: note exists"; if (exist) return "skip: note exists";
@ -46,6 +44,6 @@ export default async function (
throw e; throw e;
} }
} finally { } finally {
unlock(); await getApLock(uri);
} }
} }

View file

@ -13,8 +13,6 @@ export default async function (
): Promise<string> { ): Promise<string> {
logger.info(`Deleting the Note: ${uri}`); logger.info(`Deleting the Note: ${uri}`);
const unlock = await getApLock(uri);
try { try {
const dbResolver = new DbResolver(); const dbResolver = new DbResolver();
const note = await dbResolver.getNoteFromApId(uri); const note = await dbResolver.getNoteFromApId(uri);
@ -39,6 +37,6 @@ export default async function (
await deleteNode(actor, note); await deleteNode(actor, note);
return "ok: note deleted"; return "ok: note deleted";
} finally { } finally {
unlock(); await getApLock(uri);
} }
} }

View file

@ -68,13 +68,13 @@ export class LdSignature {
...options, ...options,
"@context": "https://w3id.org/identity/v1", "@context": "https://w3id.org/identity/v1",
}; };
transformedOptions.type = undefined; delete transformedOptions["type"];
transformedOptions.id = undefined; delete transformedOptions["id"];
transformedOptions.signatureValue = undefined; delete transformedOptions["signatureValue"];
const canonizedOptions = await this.normalize(transformedOptions); const canonizedOptions = await this.normalize(transformedOptions);
const optionsHash = this.sha256(canonizedOptions); const optionsHash = this.sha256(canonizedOptions);
const transformedData = { ...data }; const transformedData = { ...data };
transformedData.signature = undefined; delete transformedData["signature"];
const cannonidedData = await this.normalize(transformedData); const cannonidedData = await this.normalize(transformedData);
if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`); if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`);
const documentHash = this.sha256(cannonidedData); const documentHash = this.sha256(cannonidedData);

View file

@ -444,8 +444,6 @@ export async function resolveNote(
`host ${extractDbHost(uri)} is blocked`, `host ${extractDbHost(uri)} is blocked`,
); );
const unlock = await getApLock(uri);
try { try {
//#region Returns if already registered with this server //#region Returns if already registered with this server
const exist = await fetchNote(uri); const exist = await fetchNote(uri);
@ -468,7 +466,7 @@ export async function resolveNote(
// Since the attached Note Object may be disguised, always specify the uri and fetch it from the server. // Since the attached Note Object may be disguised, always specify the uri and fetch it from the server.
return await createNote(uri, resolver, true); return await createNote(uri, resolver, true);
} finally { } finally {
unlock(); await getApLock(uri);
} }
} }

View file

@ -1,5 +1,4 @@
import { promisify } from "node:util"; import { decode } from "msgpackr";
import * as cbor from "cbor";
import define from "../../../define.js"; import define from "../../../define.js";
import { import {
UserProfiles, UserProfiles,
@ -12,7 +11,6 @@ import { procedures, hash } from "../../../2fa.js";
import { publishMainStream } from "@/services/stream.js"; import { publishMainStream } from "@/services/stream.js";
import { comparePassword } from "@/misc/password.js"; import { comparePassword } from "@/misc/password.js";
const cborDecodeFirst = promisify(cbor.decodeFirst) as any;
const rpIdHashReal = hash(Buffer.from(config.hostname, "utf-8")); const rpIdHashReal = hash(Buffer.from(config.hostname, "utf-8"));
export const meta = { export const meta = {
@ -64,7 +62,7 @@ export default define(meta, paramDef, async (ps, user) => {
const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, "utf-8")); const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, "utf-8"));
const attestation = await cborDecodeFirst(ps.attestationObject); const attestation = decode(Buffer.from(ps.attestationObject, "utf-8"));
const rpIdHash = attestation.authData.slice(0, 32); const rpIdHash = attestation.authData.slice(0, 32);
if (!rpIdHashReal.equals(rpIdHash)) { if (!rpIdHashReal.equals(rpIdHash)) {
@ -81,7 +79,7 @@ export default define(meta, paramDef, async (ps, user) => {
const credentialIdLength = authData.readUInt16BE(53); const credentialIdLength = authData.readUInt16BE(53);
const credentialId = authData.slice(55, 55 + credentialIdLength); const credentialId = authData.slice(55, 55 + credentialIdLength);
const publicKeyData = authData.slice(55 + credentialIdLength); const publicKeyData = authData.slice(55 + credentialIdLength);
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData); const publicKey: Map<number, any> = decode(publicKeyData);
if (publicKey.get(3) !== -7) { if (publicKey.get(3) !== -7) {
throw new Error("alg mismatch"); throw new Error("alg mismatch");
} }

View file

@ -3,10 +3,11 @@
"name": "Firefish", "name": "Firefish",
"description": "An open source, decentralized social media platform that's free forever!", "description": "An open source, decentralized social media platform that's free forever!",
"start_url": "/", "start_url": "/",
"scope": "/",
"display": "standalone", "display": "standalone",
"background_color": "#1f1d2e", "background_color": "#1f1d2e",
"theme_color": "#31748f", "theme_color": "#31748f",
"orientation": "portrait-primary", "orientation": "any",
"icons": [ "icons": [
{ {
"src": "/static-assets/icons/192.png", "src": "/static-assets/icons/192.png",
@ -21,7 +22,7 @@
"purpose": "any" "purpose": "any"
}, },
{ {
"src": "/static-assets/icons/maskable.png", "src": "/static-assets/icons/512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"

View file

@ -11,7 +11,6 @@ export const manifestHandler = async (ctx: Koa.Context) => {
const instance = await fetchMeta(true); const instance = await fetchMeta(true);
res.short_name = instance.name || "Firefish"; res.short_name = instance.name || "Firefish";
res.name = instance.name || "Firefish";
if (instance.themeColor) res.theme_color = instance.themeColor; if (instance.themeColor) res.theme_color = instance.themeColor;
for (const icon of res.icons) { for (const icon of res.icons) {
icon.src = `${icon.src}?v=${config.version.replace(/[^0-9]/g, "")}`; icon.src = `${icon.src}?v=${config.version.replace(/[^0-9]/g, "")}`;

View file

@ -7,16 +7,16 @@ doctype html
// //
- -
▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄ ▄▄ ▄▄ ◯
█ █ █ ▄ █ █ █ █ █ █ █ █ █ ○ ▄ ▄ ██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄
█ ▄▄▄█ █ █ █ █ █ ▄▄▄█ ▄▄▄█ █ ▄▄▄▄▄█ █▄█ █ ⚬ █▄▄ █▄▄ ██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄
█ █▄▄▄█ █ █▄▄█▄█ █▄▄▄█ █▄▄▄█ █ █▄▄▄▄▄█ █ ▄▄▄▄▄▄ ▄ █████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄
█ ▄▄▄█ █ ▄▄ █ ▄▄▄█ ▄▄▄█ █▄▄▄▄▄ █ ▄ █ █ █ █▄▄ ██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄
█ █ █ █ █ █ █ █▄▄▄█ █ █ █▄▄▄▄▄█ █ █ █ █ █ ● ● █ ██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █
█▄▄▄█ █▄▄▄█▄▄▄█ █▄█▄▄▄▄▄▄▄█▄▄▄█ █▄▄▄█▄▄▄▄▄▄▄█▄▄█ █▄▄█ ▀▄▄▄▄▄▄▀ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀
Thank you for using Firefish! Thank you for using Firefish!
If you are reading this message... how about joining the development? If you're reading this message... how about helping out with development?
https://git.joinfirefish.org/firefish/firefish https://git.joinfirefish.org/firefish/firefish
html html

View file

@ -430,7 +430,6 @@ export default abstract class Chart<T extends Schema> {
? `${this.name}:${date}:${span}:${group}` ? `${this.name}:${date}:${span}:${group}`
: `${this.name}:${date}:${span}`; : `${this.name}:${date}:${span}`;
const unlock = await getChartInsertLock(lockKey);
try { try {
// ロック内でもう1回チェックする // ロック内でもう1回チェックする
const currentLog = (await repository.findOneBy({ const currentLog = (await repository.findOneBy({
@ -466,14 +465,14 @@ export default abstract class Chart<T extends Schema> {
return log; return log;
} finally { } finally {
unlock(); await getChartInsertLock(lockKey);
} }
} }
protected commit(diff: Commit<T>, group: string | null = null): void { protected commit(diff: Commit<T>, group: string | null = null): void {
for (const [k, v] of Object.entries(diff)) { for (const [k, v] of Object.entries(diff)) {
if (v == null || v === 0 || (Array.isArray(v) && v.length === 0)) if (v == null || v === 0 || (Array.isArray(v) && v.length === 0))
// rome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined // biome-ignore lint/performance/noDelete: needs to be deleted not just set to undefined
delete diff[k]; delete diff[k];
} }
this.buffer.push({ this.buffer.push({

View file

@ -15,8 +15,6 @@ export async function fetchInstanceMetadata(
instance: Instance, instance: Instance,
force = false, force = false,
): Promise<void> { ): Promise<void> {
const unlock = await getFetchInstanceMetadataLock(instance.host);
if (!force) { if (!force) {
const _instance = await Instances.findOneBy({ host: instance.host }); const _instance = await Instances.findOneBy({ host: instance.host });
const now = Date.now(); const now = Date.now();
@ -24,7 +22,7 @@ export async function fetchInstanceMetadata(
_instance?.infoUpdatedAt && _instance?.infoUpdatedAt &&
now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24 now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24
) { ) {
unlock(); await getFetchInstanceMetadataLock(instance.host);
return; return;
} }
} }
@ -53,7 +51,7 @@ export async function fetchInstanceMetadata(
} as Record<string, any>; } as Record<string, any>;
if (info) { if (info) {
updates.softwareName = info.software?.name.toLowerCase(); updates.softwareName = info.software?.name?.toLowerCase() || null;
updates.softwareVersion = info.software?.version; updates.softwareVersion = info.software?.version;
updates.openRegistrations = info.openRegistrations; updates.openRegistrations = info.openRegistrations;
updates.maintainerName = info.metadata updates.maintainerName = info.metadata
@ -80,24 +78,24 @@ export async function fetchInstanceMetadata(
} catch (e) { } catch (e) {
logger.error(`Failed to update metadata of ${instance.host}: ${e}`); logger.error(`Failed to update metadata of ${instance.host}: ${e}`);
} finally { } finally {
unlock(); await getFetchInstanceMetadataLock(instance.host);
} }
} }
type NodeInfo = { type NodeInfo = {
openRegistrations?: any; openRegistrations?: boolean;
software?: { software?: {
name?: any; name?: string;
version?: any; version?: string;
}; };
metadata?: { metadata?: {
name?: any; name?: string;
nodeName?: any; nodeName?: string;
nodeDescription?: any; nodeDescription?: string;
description?: any; description?: string;
maintainer?: { maintainer?: {
name?: any; name?: string;
email?: any; email?: string;
}; };
}; };
}; };

View file

@ -177,7 +177,7 @@ export default async (
data: Option, data: Option,
silent = false, silent = false,
) => ) =>
// rome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME // biome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME
new Promise<Note>(async (res, rej) => { new Promise<Note>(async (res, rej) => {
const dontFederateInitially = data.visibility === "hidden"; const dontFederateInitially = data.visibility === "hidden";

View file

@ -1,6 +1,7 @@
{ {
"extends": ["@eslint-sets/vue3", "@eslint-sets/vue3-ts"], "extends": ["@eslint-sets/vue3", "@eslint-sets/vue3-ts"],
"plugins": ["file-progress", "prettier"], "plugins": ["file-progress", "prettier"],
"ignorePatterns": ["**/*.json5"],
"rules": { "rules": {
"file-progress/activate": 1 "file-progress/activate": 1
} }

View file

@ -5,9 +5,9 @@
"watch": "pnpm vite build --watch --mode development", "watch": "pnpm vite build --watch --mode development",
"build": "pnpm vite build", "build": "pnpm vite build",
"build:debug": "pnpm run build", "build:debug": "pnpm run build",
"lint": "pnpm rome check **/*.ts --apply && pnpm run lint:vue", "lint": "pnpm biome check **/*.ts --apply ; pnpm run lint:vue",
"lint:vue": "pnpm paralint --ext .vue --fix '**/*.vue' --cache", "lint:vue": "pnpm eslint src --fix '**/*.vue' --cache ; pnpm run format",
"format": "pnpm rome format * --write && pnpm prettier --write '**/*.{scss,vue}' --cache --cache-strategy metadata" "format": "pnpm biome format * --write && pnpm prettier --write '**/*.{scss,vue}' --cache --cache-strategy metadata"
}, },
"devDependencies": { "devDependencies": {
"@discordapp/twemoji": "14.1.2", "@discordapp/twemoji": "14.1.2",
@ -16,7 +16,7 @@
"@phosphor-icons/web": "^2.0.3", "@phosphor-icons/web": "^2.0.3",
"@rollup/plugin-alias": "5.0.0", "@rollup/plugin-alias": "5.0.0",
"@rollup/plugin-json": "6.0.0", "@rollup/plugin-json": "6.0.0",
"@rollup/pluginutils": "^5.0.3", "@rollup/pluginutils": "^5.0.4",
"@syuilo/aiscript": "0.11.1", "@syuilo/aiscript": "0.11.1",
"@types/escape-regexp": "0.0.1", "@types/escape-regexp": "0.0.1",
"@types/glob": "8.1.0", "@types/glob": "8.1.0",
@ -28,15 +28,15 @@
"@types/seedrandom": "3.0.5", "@types/seedrandom": "3.0.5",
"@types/throttle-debounce": "5.0.0", "@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
"@types/uuid": "9.0.2", "@types/uuid": "9.0.3",
"@vitejs/plugin-vue": "4.3.1", "@vitejs/plugin-vue": "4.3.4",
"@vue/compiler-sfc": "3.3.4", "@vue/compiler-sfc": "3.3.4",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "6.0.1", "autosize": "6.0.1",
"blurhash": "2.0.5", "blurhash": "2.0.5",
"broadcast-channel": "5.2.0", "broadcast-channel": "5.3.0",
"browser-image-resizer": "github:misskey-dev/browser-image-resizer", "browser-image-resizer": "github:misskey-dev/browser-image-resizer",
"chart.js": "4.3.3", "chart.js": "4.4.0",
"chartjs-adapter-date-fns": "3.0.0", "chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "^2.0.1", "chartjs-chart-matrix": "^2.0.1",
"chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-gradient": "0.6.1",
@ -63,36 +63,35 @@
"katex": "0.16.8", "katex": "0.16.8",
"matter-js": "0.19.0", "matter-js": "0.19.0",
"mfm-js": "0.23.3", "mfm-js": "0.23.3",
"paralint": "^1.2.1",
"photoswipe": "5.3.8", "photoswipe": "5.3.8",
"prettier": "3.0.2", "prettier": "3.0.3",
"prettier-plugin-vue": "1.1.6", "prettier-plugin-vue": "1.1.6",
"prismjs": "1.29.0", "prismjs": "1.29.0",
"punycode": "2.3.0", "punycode": "2.3.0",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"rollup": "3.28.0", "rollup": "3.28.1",
"s-age": "1.1.2", "s-age": "1.1.2",
"sass": "1.66.0", "sass": "1.66.1",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"swiper": "10.2.0", "swiper": "10.2.0",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.155.0", "three": "0.156.0",
"throttle-debounce": "5.0.0", "throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tsc-alias": "1.8.7", "tsc-alias": "1.8.7",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"twemoji-parser": "14.0.0", "twemoji-parser": "14.0.0",
"typescript": "5.1.6", "typescript": "5.2.2",
"unicode-emoji-json": "^0.4.0", "unicode-emoji-json": "^0.4.0",
"uuid": "9.0.0", "uuid": "9.0.0",
"vanilla-tilt": "1.8.1", "vanilla-tilt": "1.8.1",
"vite": "4.4.9", "vite": "4.4.9",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vue": "3.3.4", "vue": "3.3.4",
"vue-draggable-plus": "^0.2.5", "vue-draggable-plus": "^0.2.6",
"vue-isyourpasswordsafe": "^2.0.0", "vue-isyourpasswordsafe": "^2.0.0",
"vue-plyr": "^7.0.0", "vue-plyr": "^7.0.0",
"vue-prism-editor": "2.0.0-alpha.2" "vue-prism-editor": "2.0.0-alpha.2"

View file

@ -1,10 +1,10 @@
import { defineAsyncComponent, reactive } from "vue"; import { defineAsyncComponent, reactive } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { i18n } from "./i18n"; import { i18n } from "./i18n";
import { del, get, set } from "@/scripts/idb-proxy"; import { del, get, set } from "@/scripts/idb-proxy";
import { apiUrl } from "@/config"; import { apiUrl } from "@/config";
import { waiting, api, popup, popupMenu, success, alert } from "@/os"; import { alert, api, popup, popupMenu, success, waiting } from "@/os";
import { unisonReload, reloadChannel } from "@/scripts/unison-reload"; import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
// TODO: 他のタブと永続化されたstateを同期 // TODO: 他のタブと永続化されたstateを同期

View file

@ -199,7 +199,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from "vue"; import { computed, onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
import * as Acct from "firefish-js/built/acct"; import * as Acct from "firefish-js/built/acct";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -281,7 +281,9 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
const inputValue = ref<string | number | null>(props.input?.default ?? null); const inputValue = ref<string | number | null>(props.input?.default ?? null);
const selectedValue = ref(props.select?.default ?? null); const selectedValue = ref(props.select?.default ?? null);
let disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(null); const disabledReason = ref<null | "charactersExceeded" | "charactersBelow">(
null,
);
const okButtonDisabled = computed<boolean>(() => { const okButtonDisabled = computed<boolean>(() => {
if (props.input) { if (props.input) {
if (props.input.minLength) { if (props.input.minLength) {

View file

@ -39,7 +39,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from "vue"; import { computed, defineAsyncComponent, ref } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import copyToClipboard from "@/scripts/copy-to-clipboard"; import copyToClipboard from "@/scripts/copy-to-clipboard";
import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue"; import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
import bytes from "@/filters/bytes"; import bytes from "@/filters/bytes";
@ -160,7 +160,7 @@ function rename() {
if (canceled) return; if (canceled) return;
os.api("drive/files/update", { os.api("drive/files/update", {
fileId: props.file.id, fileId: props.file.id,
name: name, name,
}); });
}); });
} }
@ -179,7 +179,7 @@ function describe() {
{ {
done: (result) => { done: (result) => {
if (!result || result.canceled) return; if (!result || result.canceled) return;
let comment = result.result; const comment = result.result;
os.api("drive/files/update", { os.api("drive/files/update", {
fileId: props.file.id, fileId: props.file.id,
comment: comment.length === 0 ? null : comment, comment: comment.length === 0 ? null : comment,

View file

@ -38,7 +38,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from "vue"; import { computed, defineAsyncComponent, ref } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -207,7 +207,7 @@ function rename() {
if (canceled) return; if (canceled) return;
os.api("drive/folders/update", { os.api("drive/folders/update", {
folderId: props.folder.id, folderId: props.folder.id,
name: name, name,
}); });
}); });
} }

View file

@ -15,7 +15,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";

View file

@ -139,7 +139,7 @@ import {
ref, ref,
watch, watch,
} from "vue"; } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import MkButton from "./MkButton.vue"; import MkButton from "./MkButton.vue";
import XNavFolder from "@/components/MkDrive.navFolder.vue"; import XNavFolder from "@/components/MkDrive.navFolder.vue";
import XFolder from "@/components/MkDrive.folder.vue"; import XFolder from "@/components/MkDrive.folder.vue";
@ -354,7 +354,7 @@ function urlUpload() {
}).then(({ canceled, result: url }) => { }).then(({ canceled, result: url }) => {
if (canceled || !url) return; if (canceled || !url) return;
os.api("drive/files/upload-from-url", { os.api("drive/files/upload-from-url", {
url: url, url,
folderId: folder.value ? folder.value.id : undefined, folderId: folder.value ? folder.value.id : undefined,
}); });
@ -372,7 +372,7 @@ function createFolder() {
}).then(({ canceled, result: name }) => { }).then(({ canceled, result: name }) => {
if (canceled) return; if (canceled) return;
os.api("drive/folders/create", { os.api("drive/folders/create", {
name: name, name,
parentId: folder.value ? folder.value.id : undefined, parentId: folder.value ? folder.value.id : undefined,
}).then((createdFolder) => { }).then((createdFolder) => {
addFolder(createdFolder, true); addFolder(createdFolder, true);
@ -389,7 +389,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) {
if (canceled) return; if (canceled) return;
os.api("drive/folders/update", { os.api("drive/folders/update", {
folderId: folderToRename.id, folderId: folderToRename.id,
name: name, name,
}).then((updatedFolder) => { }).then((updatedFolder) => {
// FIXME: // FIXME:
move(updatedFolder); move(updatedFolder);

View file

@ -68,7 +68,7 @@ const is = computed(() => {
"application/x-tar", "application/x-tar",
"application/gzip", "application/gzip",
"application/x-7z-compressed", "application/x-7z-compressed",
].some((archiveType) => archiveType === props.file.type) ].includes(props.file.type)
) )
return "archive"; return "archive";
return "unknown"; return "unknown";

View file

@ -37,7 +37,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import XDrive from "@/components/MkDrive.vue"; import XDrive from "@/components/MkDrive.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import number from "@/filters/number"; import number from "@/filters/number";

View file

@ -15,7 +15,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import {} from "vue"; import {} from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import XDrive from "@/components/MkDrive.vue"; import XDrive from "@/components/MkDrive.vue";
import XWindow from "@/components/MkWindow.vue"; import XWindow from "@/components/MkWindow.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";

View file

@ -48,7 +48,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch, onMounted } from "vue"; import { onMounted, ref, watch } from "vue";
import { addSkinTone } from "@/scripts/emojilist"; import { addSkinTone } from "@/scripts/emojilist";
const props = defineProps<{ const props = defineProps<{

View file

@ -1,5 +1,5 @@
<template> <template>
<FocusTrap v-bind:active="isActive"> <FocusTrap :active="isActive">
<div <div
class="omfetrab" class="omfetrab"
:class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]"
@ -163,14 +163,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch, onMounted } from "vue"; import { computed, onMounted, ref, watch } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import { FocusTrap } from "focus-trap-vue";
import XSection from "@/components/MkEmojiPicker.section.vue"; import XSection from "@/components/MkEmojiPicker.section.vue";
import type { UnicodeEmojiDef } from "@/scripts/emojilist";
import { import {
emojilist, emojilist,
unicodeEmojiCategories,
UnicodeEmojiDef,
getNicelyLabeledCategory, getNicelyLabeledCategory,
unicodeEmojiCategories,
} from "@/scripts/emojilist"; } from "@/scripts/emojilist";
import { getStaticImageUrl } from "@/scripts/get-static-image-url"; import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import Ripple from "@/components/MkRipple.vue"; import Ripple from "@/components/MkRipple.vue";
@ -180,7 +181,6 @@ import { deviceKind } from "@/scripts/device-kind";
import { emojiCategories, instance } from "@/instance"; import { emojiCategories, instance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import { FocusTrap } from "focus-trap-vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{

View file

@ -8,7 +8,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import * as Misskey from "firefish-js"; import type * as Misskey from "firefish-js";
import * as os from "@/os"; import * as os from "@/os";
const meta = ref<Misskey.entities.DetailedInstanceMetadata>(); const meta = ref<Misskey.entities.DetailedInstanceMetadata>();

View file

@ -1,14 +1,15 @@
<template> <template>
<button <button
v-if="!hideMenu" v-if="!hideMenu"
v-tooltip="i18n.ts.menu"
class="menu _button" class="menu _button"
@click.stop="menu" @click.stop="menu"
v-tooltip="i18n.ts.menu"
> >
<i class="ph-dots-three-outline ph-bold ph-lg"></i> <i class="ph-dots-three-outline ph-bold ph-lg"></i>
</button> </button>
<button <button
v-if="$i != null && $i.id != user.id" v-if="$i != null && $i.id != user.id"
v-tooltip="full ? null : `${state} ${user.name || user.username}`"
class="kpoogebi _button follow-button" class="kpoogebi _button follow-button"
:class="{ :class="{
wait, wait,
@ -18,9 +19,8 @@
blocking: isBlocking, blocking: isBlocking,
}" }"
:disabled="wait" :disabled="wait"
@click.stop="onClick"
:aria-label="`${state} ${user.name || user.username}`" :aria-label="`${state} ${user.name || user.username}`"
v-tooltip="full ? null : `${state} ${user.name || user.username}`" @click.stop="onClick"
> >
<template v-if="!wait"> <template v-if="!wait">
<template v-if="isBlocking"> <template v-if="isBlocking">
@ -88,13 +88,13 @@ const props = withDefaults(
const isBlocking = computed(() => props.user.isBlocking); const isBlocking = computed(() => props.user.isBlocking);
let state = ref(i18n.ts.processing); const state = ref(i18n.ts.processing);
let isFollowing = ref(props.user.isFollowing); const isFollowing = ref(props.user.isFollowing);
let hasPendingFollowRequestFromYou = ref( const hasPendingFollowRequestFromYou = ref(
props.user.hasPendingFollowRequestFromYou, props.user.hasPendingFollowRequestFromYou,
); );
let wait = ref(false); const wait = ref(false);
const connection = stream.useChannel("main"); const connection = stream.useChannel("main");
if (props.user.isFollowing == null) { if (props.user.isFollowing == null) {

View file

@ -64,7 +64,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import {} from "vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
@ -77,11 +76,11 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let dialog: InstanceType<typeof XModalWindow> = ref(); const dialog: InstanceType<typeof XModalWindow> = ref();
let username = ref(""); const username = ref("");
let email = ref(""); const email = ref("");
let processing = ref(false); const processing = ref(false);
async function onSubmit() { async function onSubmit() {
processing.value = true; processing.value = true;

View file

@ -3,7 +3,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, defineAsyncComponent } from "vue"; import { defineAsyncComponent, defineComponent } from "vue";
export default defineComponent({ export default defineComponent({
components: { components: {

View file

@ -8,7 +8,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, nextTick, watch, shallowRef, ref } from "vue"; import { nextTick, onMounted, ref, shallowRef, watch } from "vue";
import { Chart } from "chart.js"; import { Chart } from "chart.js";
import * as os from "@/os"; import * as os from "@/os";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
@ -26,8 +26,8 @@ const props = defineProps<{
const rootEl = shallowRef<HTMLDivElement>(null); const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>(null); const chartEl = shallowRef<HTMLCanvasElement>(null);
const now = new Date(); const now = new Date();
let chartInstance: Chart = null; let chartInstance: Chart = null,
let fetching = ref(true); fetching = ref(true);
const { handler: externalTooltipHandler } = useChartTooltip({ const { handler: externalTooltipHandler } = useChartTooltip({
position: "middle", position: "middle",

View file

@ -28,7 +28,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import {} from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import bytes from "@/filters/bytes"; import bytes from "@/filters/bytes";
import number from "@/filters/number"; import number from "@/filters/number";

View file

@ -49,7 +49,7 @@ const props = withDefaults(
); );
const canvas = ref<HTMLCanvasElement>(); const canvas = ref<HTMLCanvasElement>();
let loaded = ref(false); const loaded = ref(false);
function draw() { function draw() {
if (props.hash == null || canvas.value == null) return; if (props.hash == null || canvas.value == null) return;

View file

@ -11,8 +11,8 @@
v-if="closeable" v-if="closeable"
v-tooltip="i18n.ts.close" v-tooltip="i18n.ts.close"
class="_buttonIcon close" class="_buttonIcon close"
@click.stop="close"
:aria-label="i18n.t('close')" :aria-label="i18n.t('close')"
@click.stop="close"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>

View file

@ -26,7 +26,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import * as firefish from "firefish-js"; import type * as firefish from "firefish-js";
import MkMiniChart from "@/components/MkMiniChart.vue"; import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os"; import * as os from "@/os";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy"; import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
@ -35,7 +35,7 @@ const props = defineProps<{
instance: firefish.entities.Instance; instance: firefish.entities.Instance;
}>(); }>();
let chartValues = ref<number[] | null>(null); const chartValues = ref<number[] | null>(null);
os.apiGet("charts/instance", { os.apiGet("charts/instance", {
host: props.instance.host, host: props.instance.host,

View file

@ -58,11 +58,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import type { Instance } from "firefish-js/built/entities";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { Instance } from "firefish-js/built/entities";
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "ok", selected: Instance): void; (ev: "ok", selected: Instance): void;
@ -70,10 +70,10 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let hostname = ref(""); const hostname = ref("");
let instances: Instance[] = ref([]); const instances: Instance[] = ref([]);
let selected: Instance | null = ref(null); const selected: Instance | null = ref(null);
let dialogEl = ref<InstanceType<typeof XModalWindow>>(); const dialogEl = ref<InstanceType<typeof XModalWindow>>();
let searchOrderLatch = 0; let searchOrderLatch = 0;
const search = () => { const search = () => {

View file

@ -116,11 +116,11 @@ import { initChart } from "@/scripts/init-chart";
initChart(); initChart();
const chartLimit = 500; const chartLimit = 500;
let chartSpan = ref<"hour" | "day">("hour"); const chartSpan = ref<"hour" | "day">("hour");
let chartSrc = ref("active-users"); const chartSrc = ref("active-users");
let heatmapSrc = ref("active-users"); const heatmapSrc = ref("active-users");
let subDoughnutEl = shallowRef<HTMLCanvasElement>(); const subDoughnutEl = shallowRef<HTMLCanvasElement>();
let pubDoughnutEl = shallowRef<HTMLCanvasElement>(); const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
const { handler: externalTooltipHandler1 } = useChartTooltip({ const { handler: externalTooltipHandler1 } = useChartTooltip({
position: "middle", position: "middle",

View file

@ -1,8 +1,8 @@
<template> <template>
<div <div
class="hpaizdrt"
v-tooltip="capitalize(instance.softwareName)"
ref="ticker" ref="ticker"
v-tooltip="capitalize(instance.softwareName)"
class="hpaizdrt"
:style="bg" :style="bg"
> >
<img class="icon" :src="getInstanceIcon(instance)" aria-hidden="true" /> <img class="icon" :src="getInstanceIcon(instance)" aria-hidden="true" />
@ -26,7 +26,7 @@ const props = defineProps<{
}; };
}>(); }>();
let ticker = ref<HTMLElement | null>(null); const ticker = ref<HTMLElement | null>(null);
// if no instance data is given, this is for the local instance // if no instance data is given, this is for the local instance
const instance = props.instance ?? { const instance = props.instance ?? {

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="media" v-size="{ max: [350] }"> <div v-size="{ max: [350] }" class="media">
<button v-if="hide" class="hidden" @click="hide = false"> <button v-if="hide" class="hidden" @click="hide = false">
<ImgWithBlurhash <ImgWithBlurhash
:hash="media.blurhash" :hash="media.blurhash"
@ -89,7 +89,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, ref, computed } from "vue"; import { computed, ref, watch } from "vue";
import VuePlyr from "vue-plyr"; import VuePlyr from "vue-plyr";
import "vue-plyr/dist/vue-plyr.css"; import "vue-plyr/dist/vue-plyr.css";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
@ -104,7 +104,7 @@ const props = defineProps<{
raw?: boolean; raw?: boolean;
}>(); }>();
let hide = ref(true); const hide = ref(true);
const plyr = ref(); const plyr = ref();

View file

@ -71,7 +71,7 @@ const props = withDefaults(
); );
const audioEl = ref<HTMLAudioElement | null>(); const audioEl = ref<HTMLAudioElement | null>();
let hide = ref(true); const hide = ref(true);
function volumechange() { function volumechange() {
if (audioEl.value) if (audioEl.value)

View file

@ -29,7 +29,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import PhotoSwipeLightbox from "photoswipe/lightbox"; import PhotoSwipeLightbox from "photoswipe/lightbox";
import PhotoSwipe from "photoswipe"; import PhotoSwipe from "photoswipe";
import "photoswipe/style.css"; import "photoswipe/style.css";
@ -125,11 +125,11 @@ onMounted(() => {
className: "pwsp__alt-text-container", className: "pwsp__alt-text-container",
appendTo: "wrapper", appendTo: "wrapper",
onInit: (el, pwsp) => { onInit: (el, pwsp) => {
let textBox = document.createElement("p"); const textBox = document.createElement("p");
textBox.className = "pwsp__alt-text"; textBox.className = "pwsp__alt-text";
el.appendChild(textBox); el.appendChild(textBox);
let preventProp = function (ev: Event): void { const preventProp = function (ev: Event): void {
ev.stopPropagation(); ev.stopPropagation();
}; };

View file

@ -14,7 +14,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, ref } from "vue"; import { nextTick, onMounted, ref } from "vue";
import MkMenu from "./MkMenu.vue"; import MkMenu from "./MkMenu.vue";
import { MenuItem } from "@/types/menu"; import type { MenuItem } from "@/types/menu";
const props = defineProps<{ const props = defineProps<{
items: MenuItem[]; items: MenuItem[];

View file

@ -14,8 +14,8 @@
width: width && !asDrawer ? width + 'px' : '', width: width && !asDrawer ? width + 'px' : '',
maxHeight: maxHeight ? maxHeight + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '',
}" }"
@contextmenu.self="(e) => e.preventDefault()"
tabindex="-1" tabindex="-1"
@contextmenu.self="(e) => e.preventDefault()"
> >
<template v-for="item in items2"> <template v-for="item in items2">
<div v-if="item === null" class="divider"></div> <div v-if="item === null" class="divider"></div>
@ -47,7 +47,7 @@
v-if="item.avatar" v-if="item.avatar"
:user="item.avatar" :user="item.avatar"
class="avatar" class="avatar"
disableLink disable-link
/> />
<span :style="item.textStyle || ''">{{ <span :style="item.textStyle || ''">{{
item.text item.text
@ -100,7 +100,7 @@
<MkAvatar <MkAvatar
:user="item.user" :user="item.user"
class="avatar" class="avatar"
disableLink disable-link
/><MkUserName :user="item.user" /> /><MkUserName :user="item.user" />
<span <span
v-if="item.indicate" v-if="item.indicate"
@ -168,7 +168,7 @@
v-if="item.avatar" v-if="item.avatar"
:user="item.avatar" :user="item.avatar"
class="avatar" class="avatar"
disableLink disable-link
/> />
<span :style="item.textStyle || ''">{{ <span :style="item.textStyle || ''">{{
item.text item.text
@ -210,11 +210,16 @@ import {
ref, ref,
watch, watch,
} from "vue"; } from "vue";
import { FocusTrap } from "focus-trap-vue";
import FormSwitch from "@/components/form/switch.vue"; import FormSwitch from "@/components/form/switch.vue";
import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from "@/types/menu"; import type {
InnerMenuItem,
MenuAction,
MenuItem,
MenuPending,
} from "@/types/menu";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { FocusTrap } from "focus-trap-vue";
const XChild = defineAsyncComponent(() => import("./MkMenu.child.vue")); const XChild = defineAsyncComponent(() => import("./MkMenu.child.vue"));
const focusTrap = ref(); const focusTrap = ref();
@ -233,13 +238,13 @@ const emit = defineEmits<{
(ev: "close", actioned?: boolean): void; (ev: "close", actioned?: boolean): void;
}>(); }>();
let itemsEl = ref<HTMLDivElement>(); const itemsEl = ref<HTMLDivElement>();
let items2: InnerMenuItem[] = ref([]); const items2: InnerMenuItem[] = ref([]);
let child = ref<InstanceType<typeof XChild>>(); const child = ref<InstanceType<typeof XChild>>();
let childShowingItem = ref<MenuItem | null>(); const childShowingItem = ref<MenuItem | null>();
watch( watch(
() => props.items, () => props.items,
@ -267,8 +272,8 @@ watch(
}, },
); );
let childMenu = ref<MenuItem[] | null>(); const childMenu = ref<MenuItem[] | null>();
let childTarget = ref<HTMLElement | null>(); const childTarget = ref<HTMLElement | null>();
function closeChild() { function closeChild() {
childMenu.value = null; childMenu.value = null;

View file

@ -25,7 +25,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, ref } from "vue"; import { ref, watch } from "vue";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
import { useInterval } from "@/scripts/use-interval"; import { useInterval } from "@/scripts/use-interval";
@ -37,10 +37,10 @@ const props = defineProps<{
const viewBoxX = 50; const viewBoxX = 50;
const viewBoxY = 50; const viewBoxY = 50;
const gradientId = uuid(); const gradientId = uuid();
let polylinePoints = ref(""); const polylinePoints = ref("");
let polygonPoints = ref(""); const polygonPoints = ref("");
let headX = ref<number | null>(null); const headX = ref<number | null>(null);
let headY = ref<number | null>(null); const headY = ref<number | null>(null);
const accent = tinycolor( const accent = tinycolor(
getComputedStyle(document.documentElement).getPropertyValue("--accent"), getComputedStyle(document.documentElement).getPropertyValue("--accent"),
); );

View file

@ -25,6 +25,7 @@
<div <div
v-show="manualShowing != null ? manualShowing : showing" v-show="manualShowing != null ? manualShowing : showing"
v-hotkey.global="keymap" v-hotkey.global="keymap"
v-focus
:class="[ :class="[
$style.root, $style.root,
{ {
@ -44,7 +45,6 @@
'--transformOrigin': transformOrigin, '--transformOrigin': transformOrigin,
}" }"
tabindex="-1" tabindex="-1"
v-focus
> >
<div <div
class="_modalBg data-cy-bg" class="_modalBg data-cy-bg"
@ -78,20 +78,20 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import {
computed,
nextTick, nextTick,
onMounted, onMounted,
watch,
provide,
onUnmounted, onUnmounted,
provide,
ref, ref,
shallowRef, shallowRef,
computed, watch,
} from "vue"; } from "vue";
import { FocusTrap } from "focus-trap-vue";
import * as os from "@/os"; import * as os from "@/os";
import { isTouchUsing } from "@/scripts/touch"; import { isTouchUsing } from "@/scripts/touch";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import { deviceKind } from "@/scripts/device-kind"; import { deviceKind } from "@/scripts/device-kind";
import { FocusTrap } from "focus-trap-vue";
function getFixedContainer(el: Element | null): Element | null { function getFixedContainer(el: Element | null): Element | null {
if (el == null || el.tagName === "BODY") return null; if (el == null || el.tagName === "BODY") return null;
@ -139,13 +139,13 @@ const emit = defineEmits<{
provide("modal", true); provide("modal", true);
let maxHeight = ref<number>(); const maxHeight = ref<number>();
let fixed = ref(false); const fixed = ref(false);
let transformOrigin = ref("center"); const transformOrigin = ref("center");
let showing = ref(true); const showing = ref(true);
let content = shallowRef<HTMLElement>(); const content = shallowRef<HTMLElement>();
const zIndex = os.claimZIndex(props.zPriority); const zIndex = os.claimZIndex(props.zPriority);
let useSendAnime = ref(false); const useSendAnime = ref(false);
const type = computed<ModalTypes>(() => { const type = computed<ModalTypes>(() => {
if (props.preferType === "auto") { if (props.preferType === "auto") {
if ( if (
@ -164,7 +164,7 @@ const type = computed<ModalTypes>(() => {
const isEnableBgTransparent = computed( const isEnableBgTransparent = computed(
() => props.transparentBg && type.value === "popup", () => props.transparentBg && type.value === "popup",
); );
let transitionName = computed(() => const transitionName = computed(() =>
defaultStore.state.animation defaultStore.state.animation
? useSendAnime.value ? useSendAnime.value
? "send" ? "send"
@ -175,7 +175,7 @@ let transitionName = computed(() =>
: "modal" : "modal"
: "", : "",
); );
let transitionDuration = computed(() => const transitionDuration = computed(() =>
transitionName.value === "send" transitionName.value === "send"
? 400 ? 400
: transitionName.value === "modal-popup" : transitionName.value === "modal-popup"
@ -235,8 +235,7 @@ const align = () => {
const width = content.value!.offsetWidth; const width = content.value!.offsetWidth;
const height = content.value!.offsetHeight; const height = content.value!.offsetHeight;
let left; let left, top;
let top;
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset); const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset); const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
@ -321,8 +320,8 @@ const align = () => {
left = 0; left = 0;
} }
let transformOriginX = "center"; let transformOriginX = "center",
let transformOriginY = "center"; transformOriginY = "center";
if ( if (
top >= top >=

View file

@ -28,8 +28,8 @@
</span> </span>
<button <button
class="_button" class="_button"
@click="$refs.modal.close()"
:aria-label="i18n.t('close')" :aria-label="i18n.t('close')"
@click="$refs.modal.close()"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>
@ -52,7 +52,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, provide, ref, computed } from "vue"; import type { ComputedRef } from "vue";
import { computed, provide, ref } from "vue";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import { popout as _popout } from "@/scripts/popout"; import { popout as _popout } from "@/scripts/popout";
import copyToClipboard from "@/scripts/copy-to-clipboard"; import copyToClipboard from "@/scripts/copy-to-clipboard";
@ -60,7 +61,8 @@ import { url } from "@/config";
import * as os from "@/os"; import * as os from "@/os";
import { mainRouter, routes } from "@/router"; import { mainRouter, routes } from "@/router";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { PageMetadata, provideMetadataReceiver } from "@/scripts/page-metadata"; import type { PageMetadata } from "@/scripts/page-metadata";
import { provideMetadataReceiver } from "@/scripts/page-metadata";
import { Router } from "@/nirax"; import { Router } from "@/nirax";
const props = defineProps<{ const props = defineProps<{
@ -76,12 +78,12 @@ const router = new Router(routes, props.initialPath);
router.addListener("push", (ctx) => {}); router.addListener("push", (ctx) => {});
let pageMetadata = ref<null | ComputedRef<PageMetadata>>(); const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let rootEl = ref(); const rootEl = ref();
let modal = ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
let path = ref(props.initialPath); const path = ref(props.initialPath);
let width = ref(860); const width = ref(860);
let height = ref(660); const height = ref(660);
const history = []; const history = [];
provide("router", router); provide("router", router);

View file

@ -25,10 +25,10 @@
<div ref="headerEl" class="header"> <div ref="headerEl" class="header">
<button <button
v-if="props.withOkButton" v-if="props.withOkButton"
v-tooltip="i18n.ts.close"
:aria-label="i18n.t('close')" :aria-label="i18n.t('close')"
class="_button" class="_button"
@click="$emit('close')" @click="$emit('close')"
v-tooltip="i18n.ts.close"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>
@ -92,9 +92,9 @@ const emit = defineEmits<{
(event: "ok"): void; (event: "ok"): void;
}>(); }>();
let modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
let rootEl = shallowRef<HTMLElement>(); const rootEl = shallowRef<HTMLElement>();
let headerEl = shallowRef<HTMLElement>(); const headerEl = shallowRef<HTMLElement>();
const close = (ev) => { const close = (ev) => {
modal.value?.close(ev); modal.value?.close(ev);

View file

@ -1,15 +1,15 @@
<template> <template>
<div <div
:aria-label="accessibleLabel"
v-if="!muted.muted" v-if="!muted.muted"
v-show="!isDeleted" v-show="!isDeleted"
:id="appearNote.id"
ref="el" ref="el"
v-hotkey="keymap" v-hotkey="keymap"
v-size="{ max: [500, 350] }" v-size="{ max: [500, 350] }"
:aria-label="accessibleLabel"
class="tkcbzcuz note-container" class="tkcbzcuz note-container"
:tabindex="!isDeleted ? '-1' : null" :tabindex="!isDeleted ? '-1' : null"
:class="{ renote: isRenote }" :class="{ renote: isRenote }"
:id="appearNote.id"
> >
<MkNoteSub <MkNoteSub
v-if="appearNote.reply && !detailedView && !collapsedReply" v-if="appearNote.reply && !detailedView && !collapsedReply"
@ -19,10 +19,10 @@
<div <div
v-if="!detailedView" v-if="!detailedView"
class="note-context" class="note-context"
@click="noteClick"
:class="{ :class="{
collapsedReply: collapsedReply && appearNote.reply, collapsedReply: collapsedReply && appearNote.reply,
}" }"
@click="noteClick"
> >
<div class="line"></div> <div class="line"></div>
<div v-if="appearNote._prId_" class="info"> <div v-if="appearNote._prId_" class="info">
@ -87,11 +87,11 @@
</div> </div>
<article <article
class="article" class="article"
@contextmenu.stop="onContextmenu"
@click="noteClick"
:style="{ :style="{
cursor: expandOnNoteClick && !detailedView ? 'pointer' : '', cursor: expandOnNoteClick && !detailedView ? 'pointer' : '',
}" }"
@contextmenu.stop="onContextmenu"
@click="noteClick"
> >
<div class="main"> <div class="main">
<div class="header-container"> <div class="header-container">
@ -103,8 +103,8 @@
class="text" class="text"
:note="appearNote" :note="appearNote"
:detailed="true" :detailed="true"
:detailedView="detailedView" :detailed-view="detailedView"
:parentId="appearNote.parentId" :parent-id="appearNote.parentId"
@push="(e) => router.push(notePage(e))" @push="(e) => router.push(notePage(e))"
@focusfooter="footerEl.focus()" @focusfooter="footerEl.focus()"
@expanded="(e) => setPostExpanded(e)" @expanded="(e) => setPostExpanded(e)"
@ -171,7 +171,7 @@
class="button" class="button"
:note="appearNote" :note="appearNote"
:count="appearNote.renoteCount" :count="appearNote.renoteCount"
:detailedView="detailedView" :detailed-view="detailedView"
/> />
<XStarButtonNoEmoji <XStarButtonNoEmoji
v-if="!enableEmojiReactions" v-if="!enableEmojiReactions"
@ -212,9 +212,9 @@
appearNote.myReaction != null appearNote.myReaction != null
" "
ref="reactButton" ref="reactButton"
v-tooltip.noDelay.bottom="i18n.ts.removeReaction"
class="button _button reacted" class="button _button reacted"
@click.stop="undoReact(appearNote)" @click.stop="undoReact(appearNote)"
v-tooltip.noDelay.bottom="i18n.ts.removeReaction"
> >
<i class="ph-minus ph-bold ph-lg"></i> <i class="ph-minus ph-bold ph-lg"></i>
</button> </button>
@ -259,8 +259,8 @@ import { computed, inject, onMounted, ref } from "vue";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import type { Ref } from "vue"; import type { Ref } from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import MkNoteSub from "@/components/MkNoteSub.vue";
import MkSubNoteContent from "./MkSubNoteContent.vue"; import MkSubNoteContent from "./MkSubNoteContent.vue";
import MkNoteSub from "@/components/MkNoteSub.vue";
import XNoteHeader from "@/components/MkNoteHeader.vue"; import XNoteHeader from "@/components/MkNoteHeader.vue";
import XRenoteButton from "@/components/MkRenoteButton.vue"; import XRenoteButton from "@/components/MkRenoteButton.vue";
import XReactionsViewer from "@/components/MkReactionsViewer.vue"; import XReactionsViewer from "@/components/MkReactionsViewer.vue";
@ -271,7 +271,7 @@ import MkVisibility from "@/components/MkVisibility.vue";
import copyToClipboard from "@/scripts/copy-to-clipboard"; import copyToClipboard from "@/scripts/copy-to-clipboard";
import { url } from "@/config"; import { url } from "@/config";
import { pleaseLogin } from "@/scripts/please-login"; import { pleaseLogin } from "@/scripts/please-login";
import { focusPrev, focusNext } from "@/scripts/focus"; import { focusNext, focusPrev } from "@/scripts/focus";
import { getWordSoftMute } from "@/scripts/check-word-mute"; import { getWordSoftMute } from "@/scripts/check-word-mute";
import { useRouter } from "@/router"; import { useRouter } from "@/router";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
@ -297,7 +297,7 @@ const props = defineProps<{
const inChannel = inject("inChannel", null); const inChannel = inject("inChannel", null);
let note = ref(deepClone(props.note)); const note = ref(deepClone(props.note));
const softMuteReasonI18nSrc = (what?: string) => { const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason; if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -333,7 +333,7 @@ const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const renoteTime = ref<HTMLElement>(); const renoteTime = ref<HTMLElement>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = computed(() => const appearNote = computed(() =>
isRenote ? (note.value.renote as misskey.entities.Note) : note.value, isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
); );
const isMyRenote = $i && $i.id === note.value.userId; const isMyRenote = $i && $i.id === note.value.userId;
@ -385,7 +385,7 @@ function react(viaKeyboard = false): void {
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: appearNote.value.id, noteId: appearNote.value.id,
reaction: reaction, reaction,
}); });
}, },
() => { () => {
@ -516,7 +516,7 @@ function showRenoteMenu(viaKeyboard = false): void {
], ],
renoteTime.value, renoteTime.value,
{ {
viaKeyboard: viaKeyboard, viaKeyboard,
}, },
); );
} }
@ -560,7 +560,7 @@ function readPromo() {
isDeleted.value = true; isDeleted.value = true;
} }
let postIsExpanded = ref(false); const postIsExpanded = ref(false);
function setPostExpanded(val: boolean) { function setPostExpanded(val: boolean) {
postIsExpanded.value = val; postIsExpanded.value = val;

View file

@ -10,27 +10,27 @@
:class="{ renote: isRenote }" :class="{ renote: isRenote }"
> >
<MkNoteSub <MkNoteSub
v-if="conversation"
v-for="note in conversation" v-for="note in conversation"
v-if="conversation"
:key="note.id" :key="note.id"
class="reply-to" class="reply-to"
:note="note" :note="note"
:detailedView="true" :detailed-view="true"
/> />
<MkLoading v-else-if="note.reply" mini /> <MkLoading v-else-if="note.reply" mini />
<MkNoteSub <MkNoteSub
v-if="note.reply" v-if="note.reply"
:note="note.reply" :note="note.reply"
class="reply-to" class="reply-to"
:detailedView="true" :detailed-view="true"
/> />
<MkNote <MkNote
ref="noteEl" ref="noteEl"
@contextmenu.stop="onContextmenu"
tabindex="-1" tabindex="-1"
:note="note" :note="note"
detailedView detailed-view
@contextmenu.stop="onContextmenu"
></MkNote> ></MkNote>
<MkTab v-model="tab" :style="'underline'" @update:modelValue="loadTab"> <MkTab v-model="tab" :style="'underline'" @update:modelValue="loadTab">
@ -41,22 +41,22 @@
}}</span> }}</span>
{{ i18n.ts._notification._types.reply }} {{ i18n.ts._notification._types.reply }}
</option> </option>
<option value="renotes" v-if="note.renoteCount > 0"> <option v-if="note.renoteCount > 0" value="renotes">
<!-- <i class="ph-repeat ph-bold ph-lg"></i> --> <!-- <i class="ph-repeat ph-bold ph-lg"></i> -->
<span class="count">{{ note.renoteCount }}</span> <span class="count">{{ note.renoteCount }}</span>
{{ i18n.ts._notification._types.renote }} {{ i18n.ts._notification._types.renote }}
</option> </option>
<option value="reactions" v-if="reactionsCount > 0"> <option v-if="reactionsCount > 0" value="reactions">
<!-- <i class="ph-smiley ph-bold ph-lg"></i> --> <!-- <i class="ph-smiley ph-bold ph-lg"></i> -->
<span class="count">{{ reactionsCount }}</span> <span class="count">{{ reactionsCount }}</span>
{{ i18n.ts.reaction }} {{ i18n.ts.reaction }}
</option> </option>
<option value="quotes" v-if="directQuotes?.length > 0"> <option v-if="directQuotes?.length > 0" value="quotes">
<!-- <i class="ph-quotes ph-bold ph-lg"></i> --> <!-- <i class="ph-quotes ph-bold ph-lg"></i> -->
<span class="count">{{ directQuotes.length }}</span> <span class="count">{{ directQuotes.length }}</span>
{{ i18n.ts._notification._types.quote }} {{ i18n.ts._notification._types.quote }}
</option> </option>
<option value="clips" v-if="clips?.length > 0"> <option v-if="clips?.length > 0" value="clips">
<!-- <i class="ph-paperclip ph-bold ph-lg"></i> --> <!-- <i class="ph-paperclip ph-bold ph-lg"></i> -->
<span class="count">{{ clips.length }}</span> <span class="count">{{ clips.length }}</span>
{{ i18n.ts.clips }} {{ i18n.ts.clips }}
@ -64,26 +64,26 @@
</MkTab> </MkTab>
<MkNoteSub <MkNoteSub
v-if="directReplies && tab === 'replies'"
v-for="note in directReplies" v-for="note in directReplies"
v-if="directReplies && tab === 'replies'"
:key="note.id" :key="note.id"
:note="note" :note="note"
class="reply" class="reply"
:conversation="replies" :conversation="replies"
:detailedView="true" :detailed-view="true"
:parentId="note.id" :parent-id="note.id"
/> />
<MkLoading v-else-if="tab === 'replies' && note.repliesCount > 0" /> <MkLoading v-else-if="tab === 'replies' && note.repliesCount > 0" />
<MkNoteSub <MkNoteSub
v-if="directQuotes && tab === 'quotes'"
v-for="note in directQuotes" v-for="note in directQuotes"
v-if="directQuotes && tab === 'quotes'"
:key="note.id" :key="note.id"
:note="note" :note="note"
class="reply" class="reply"
:conversation="replies" :conversation="replies"
:detailedView="true" :detailed-view="true"
:parentId="note.id" :parent-id="note.id"
/> />
<MkLoading v-else-if="tab === 'quotes' && directQuotes.length > 0" /> <MkLoading v-else-if="tab === 'quotes' && directQuotes.length > 0" />
@ -94,8 +94,8 @@
:pagination="pagination" :pagination="pagination"
> --> > -->
<MkUserCardMini <MkUserCardMini
v-if="tab === 'renotes' && renotes"
v-for="item in renotes" v-for="item in renotes"
v-if="tab === 'renotes' && renotes"
:key="item.user.id" :key="item.user.id"
:user="item.user" :user="item.user"
:with-chart="false" :with-chart="false"
@ -151,11 +151,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, onUpdated, ref } from "vue"; import { onMounted, onUnmounted, onUpdated, ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import type { NoteUpdatedEvent } from "firefish-js/built/streaming.types";
import MkTab from "@/components/MkTab.vue"; import MkTab from "@/components/MkTab.vue";
import MkNote from "@/components/MkNote.vue"; import MkNote from "@/components/MkNote.vue";
import MkNoteSub from "@/components/MkNoteSub.vue"; import MkNoteSub from "@/components/MkNoteSub.vue";
import XRenoteButton from "@/components/MkRenoteButton.vue"; import type XRenoteButton from "@/components/MkRenoteButton.vue";
import MkUserCardMini from "@/components/MkUserCardMini.vue"; import MkUserCardMini from "@/components/MkUserCardMini.vue";
import MkReactedUsers from "@/components/MkReactedUsers.vue"; import MkReactedUsers from "@/components/MkReactedUsers.vue";
import { pleaseLogin } from "@/scripts/please-login"; import { pleaseLogin } from "@/scripts/please-login";
@ -170,16 +171,15 @@ import { getNoteMenu } from "@/scripts/get-note-menu";
import { useNoteCapture } from "@/scripts/use-note-capture"; import { useNoteCapture } from "@/scripts/use-note-capture";
import { deepClone } from "@/scripts/clone"; import { deepClone } from "@/scripts/clone";
import { stream } from "@/stream"; import { stream } from "@/stream";
import { NoteUpdatedEvent } from "firefish-js/built/streaming.types";
const props = defineProps<{ const props = defineProps<{
note: misskey.entities.Note; note: misskey.entities.Note;
pinned?: boolean; pinned?: boolean;
}>(); }>();
let tab = ref("replies"); const tab = ref("replies");
let note = ref(deepClone(props.note)); const note = ref(deepClone(props.note));
const softMuteReasonI18nSrc = (what?: string) => { const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason; if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -214,12 +214,12 @@ const muted = ref(
); );
const translation = ref(null); const translation = ref(null);
const translating = ref(false); const translating = ref(false);
let conversation = ref<null | misskey.entities.Note[]>([]); const conversation = ref<null | misskey.entities.Note[]>([]);
const replies = ref<misskey.entities.Note[]>([]); const replies = ref<misskey.entities.Note[]>([]);
let directReplies = ref<null | misskey.entities.Note[]>([]); const directReplies = ref<null | misskey.entities.Note[]>([]);
let directQuotes = ref<null | misskey.entities.Note[]>([]); const directQuotes = ref<null | misskey.entities.Note[]>([]);
let clips = ref(); const clips = ref();
let renotes = ref(); const renotes = ref();
let isScrolling; let isScrolling;
const reactionsCount = Object.values(props.note.reactions).reduce( const reactionsCount = Object.values(props.note.reactions).reduce(
@ -238,7 +238,7 @@ const keymap = {
useNoteCapture({ useNoteCapture({
rootEl: el, rootEl: el,
note: note, note,
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
@ -260,7 +260,7 @@ function react(viaKeyboard = false): void {
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: note.value.id, noteId: note.value.id,
reaction: reaction, reaction,
}); });
}, },
() => { () => {

View file

@ -49,7 +49,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import {} from "vue";
import type * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import MkVisibility from "@/components/MkVisibility.vue"; import MkVisibility from "@/components/MkVisibility.vue";
@ -63,7 +62,7 @@ const props = defineProps<{
pinned?: boolean; pinned?: boolean;
}>(); }>();
let note = ref(props.note); const note = ref(props.note);
const showTicker = const showTicker =
defaultStore.state.instanceTicker === "always" || defaultStore.state.instanceTicker === "always" ||

View file

@ -1,6 +1,6 @@
<template> <template>
<div v-size="{ min: [350, 500] }" class="fefdfafb"> <div v-size="{ min: [350, 500] }" class="fefdfafb">
<MkAvatar class="avatar" :user="$i" disableLink /> <MkAvatar class="avatar" :user="$i" disable-link />
<div class="main"> <div class="main">
<div class="header"> <div class="header">
<MkUserName :user="$i" /> <MkUserName :user="$i" />
@ -11,7 +11,7 @@
:text="preprocess(text).trim()" :text="preprocess(text).trim()"
:author="$i" :author="$i"
:i="$i" :i="$i"
advancedMfm advanced-mfm
/> />
</div> </div>
</div> </div>

View file

@ -11,7 +11,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import XNoteHeader from "@/components/MkNoteHeader.vue"; import XNoteHeader from "@/components/MkNoteHeader.vue";
import MkSubNoteContent from "@/components/MkSubNoteContent.vue"; import MkSubNoteContent from "@/components/MkSubNoteContent.vue";

View file

@ -1,10 +1,10 @@
<template> <template>
<article <article
v-if="!muted.muted || muted.what === 'reply'" v-if="!muted.muted || muted.what === 'reply'"
:id="detailedView ? appearNote.id : null"
ref="el" ref="el"
v-size="{ max: [450, 500] }" v-size="{ max: [450, 500] }"
class="wrpstxzv" class="wrpstxzv"
:id="detailedView ? appearNote.id : null"
tabindex="-1" tabindex="-1"
:class="{ :class="{
children: depth > 1, children: depth > 1,
@ -16,8 +16,8 @@
<div v-if="conversation && depth > 1" class="line"></div> <div v-if="conversation && depth > 1" class="line"></div>
<div <div
class="main" class="main"
@click="noteClick"
:style="{ cursor: expandOnNoteClick ? 'pointer' : '' }" :style="{ cursor: expandOnNoteClick ? 'pointer' : '' }"
@click="noteClick"
> >
<div class="avatar-container"> <div class="avatar-container">
<MkAvatar class="avatar" :user="appearNote.user" /> <MkAvatar class="avatar" :user="appearNote.user" />
@ -32,9 +32,9 @@
<MkSubNoteContent <MkSubNoteContent
class="text" class="text"
:note="note" :note="note"
:parentId="parentId" :parent-id="parentId"
:conversation="conversation" :conversation="conversation"
:detailedView="detailedView" :detailed-view="detailedView"
@focusfooter="footerEl.focus()" @focusfooter="footerEl.focus()"
/> />
<div v-if="translating || translation" class="translation"> <div v-if="translating || translation" class="translation">
@ -117,9 +117,9 @@
appearNote.myReaction != null appearNote.myReaction != null
" "
ref="reactButton" ref="reactButton"
v-tooltip.noDelay.bottom="i18n.ts.removeReaction"
class="button _button reacted" class="button _button reacted"
@click.stop="undoReact(appearNote)" @click.stop="undoReact(appearNote)"
v-tooltip.noDelay.bottom="i18n.ts.removeReaction"
> >
<i class="ph-minus ph-bold ph-lg"></i> <i class="ph-minus ph-bold ph-lg"></i>
</button> </button>
@ -137,17 +137,17 @@
</div> </div>
<template v-if="conversation"> <template v-if="conversation">
<MkNoteSub <MkNoteSub
v-if="replyLevel < 11 && depth < 5"
v-for="reply in replies" v-for="reply in replies"
v-if="replyLevel < 11 && depth < 5"
:key="reply.id" :key="reply.id"
:note="reply" :note="reply"
class="reply" class="reply"
:class="{ single: replies.length == 1 }" :class="{ single: replies.length == 1 }"
:conversation="conversation" :conversation="conversation"
:depth="replies.length == 1 ? depth : depth + 1" :depth="replies.length == 1 ? depth : depth + 1"
:replyLevel="replyLevel + 1" :reply-level="replyLevel + 1"
:parentId="appearNote.id" :parent-id="appearNote.id"
:detailedView="detailedView" :detailed-view="detailedView"
/> />
<div v-else-if="replies.length > 0" class="more"> <div v-else-if="replies.length > 0" class="more">
<div class="line"></div> <div class="line"></div>
@ -177,9 +177,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, ref, computed } from "vue"; import { computed, inject, ref } from "vue";
import type { Ref } from "vue"; import type { Ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import XNoteHeader from "@/components/MkNoteHeader.vue"; import XNoteHeader from "@/components/MkNoteHeader.vue";
import MkSubNoteContent from "@/components/MkSubNoteContent.vue"; import MkSubNoteContent from "@/components/MkSubNoteContent.vue";
import XReactionsViewer from "@/components/MkReactionsViewer.vue"; import XReactionsViewer from "@/components/MkReactionsViewer.vue";
@ -223,7 +223,7 @@ const props = withDefaults(
}, },
); );
let note = ref(deepClone(props.note)); const note = ref(deepClone(props.note));
const softMuteReasonI18nSrc = (what?: string) => { const softMuteReasonI18nSrc = (what?: string) => {
if (what === "note") return i18n.ts.userSaysSomethingReason; if (what === "note") return i18n.ts.userSaysSomethingReason;
@ -247,7 +247,7 @@ const menuButton = ref<HTMLElement>();
const starButton = ref<InstanceType<typeof XStarButton>>(); const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
let appearNote = computed(() => const appearNote = computed(() =>
isRenote ? (note.value.renote as misskey.entities.Note) : note.value, isRenote ? (note.value.renote as misskey.entities.Note) : note.value,
); );
const isDeleted = ref(false); const isDeleted = ref(false);
@ -291,7 +291,7 @@ function react(viaKeyboard = false): void {
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: appearNote.value.id, noteId: appearNote.value.id,
reaction: reaction, reaction,
}); });
}, },
() => { () => {

View file

@ -12,7 +12,7 @@
</template> </template>
<template #default="{ items: notes }"> <template #default="{ items: notes }">
<div class="giivymft" :class="{ noGap }" ref="tlEl"> <div ref="tlEl" class="giivymft" :class="{ noGap }">
<XList <XList
ref="notes" ref="notes"
v-slot="{ item: note }" v-slot="{ item: note }"

View file

@ -219,7 +219,7 @@
<MkFollowButton <MkFollowButton
:user="notification.user" :user="notification.user"
:full="true" :full="true"
:hideMenu="true" :hide-menu="true"
/></div /></div
></span> ></span>
<span <span
@ -273,8 +273,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch } from "vue"; import { onMounted, onUnmounted, ref, watch } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import XReactionIcon from "@/components/MkReactionIcon.vue"; import XReactionIcon from "@/components/MkReactionIcon.vue";
import MkFollowButton from "@/components/MkFollowButton.vue"; import MkFollowButton from "@/components/MkFollowButton.vue";
import XReactionTooltip from "@/components/MkReactionTooltip.vue"; import XReactionTooltip from "@/components/MkReactionTooltip.vue";
@ -310,8 +310,7 @@ const defaultReaction = ["⭐", "👍", "❤️"].includes(instance.defaultReact
? instance.defaultReaction ? instance.defaultReaction
: "⭐"; : "⭐";
let readObserver: IntersectionObserver | undefined; let readObserver: IntersectionObserver | undefined, connection;
let connection;
onMounted(() => { onMounted(() => {
if (!props.notification.isRead) { if (!props.notification.isRead) {

View file

@ -41,7 +41,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import {} from "vue";
import { notificationTypes } from "firefish-js"; import { notificationTypes } from "firefish-js";
import MkSwitch from "./form/switch.vue"; import MkSwitch from "./form/switch.vue";
import MkInfo from "./MkInfo.vue"; import MkInfo from "./MkInfo.vue";
@ -65,12 +64,12 @@ const props = withDefaults(
}, },
); );
let includingTypes = computed(() => props.includingTypes || []); const includingTypes = computed(() => props.includingTypes || []);
const dialog = ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
let typesMap = ref<Record<(typeof notificationTypes)[number], boolean>>({}); const typesMap = ref<Record<(typeof notificationTypes)[number], boolean>>({});
let useGlobalSetting = ref( const useGlobalSetting = ref(
(includingTypes.value === null || includingTypes.value.length === 0) && (includingTypes.value === null || includingTypes.value.length === 0) &&
props.showGlobalToggle, props.showGlobalToggle,
); );

View file

@ -28,7 +28,7 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex("high"); const zIndex = os.claimZIndex("high");
let showing = ref(true); const showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {

View file

@ -26,7 +26,7 @@
" "
:key="notification.id" :key="notification.id"
:note="notification.note" :note="notification.note"
:collapsedReply=" :collapsed-reply="
notification.type === 'reply' || notification.type === 'reply' ||
(notification.type === 'mention' && (notification.type === 'mention' &&
notification.note.replyId != null) notification.note.replyId != null)
@ -46,9 +46,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onUnmounted, onMounted, computed, ref } from "vue"; import { computed, onMounted, onUnmounted, ref } from "vue";
import { notificationTypes } from "firefish-js"; import type { notificationTypes } from "firefish-js";
import MkPagination, { Paging } from "@/components/MkPagination.vue"; import type { Paging } from "@/components/MkPagination.vue";
import MkPagination from "@/components/MkPagination.vue";
import XNotification from "@/components/MkNotification.vue"; import XNotification from "@/components/MkNotification.vue";
import XList from "@/components/MkDateSeparatedList.vue"; import XList from "@/components/MkDateSeparatedList.vue";
import XNote from "@/components/MkNote.vue"; import XNote from "@/components/MkNote.vue";

View file

@ -8,8 +8,8 @@
:buttons-left="buttonsLeft" :buttons-left="buttonsLeft"
:buttons-right="buttonsRight" :buttons-right="buttonsRight"
:contextmenu="contextmenu" :contextmenu="contextmenu"
@closed="$emit('closed')"
class="page-window" class="page-window"
@closed="$emit('closed')"
> >
<template #header> <template #header>
<template v-if="pageMetadata?.value"> <template v-if="pageMetadata?.value">
@ -30,7 +30,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, provide, ref, computed } from "vue"; import type { ComputedRef } from "vue";
import { computed, provide, ref } from "vue";
import RouterView from "@/components/global/RouterView.vue"; import RouterView from "@/components/global/RouterView.vue";
import XWindow from "@/components/MkWindow.vue"; import XWindow from "@/components/MkWindow.vue";
import { popout as _popout } from "@/scripts/popout"; import { popout as _popout } from "@/scripts/popout";
@ -39,7 +40,8 @@ import { url } from "@/config";
import { mainRouter, routes } from "@/router"; import { mainRouter, routes } from "@/router";
import { Router } from "@/nirax"; import { Router } from "@/nirax";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { PageMetadata, provideMetadataReceiver } from "@/scripts/page-metadata"; import type { PageMetadata } from "@/scripts/page-metadata";
import { provideMetadataReceiver } from "@/scripts/page-metadata";
const props = defineProps<{ const props = defineProps<{
initialPath: string; initialPath: string;
@ -51,8 +53,8 @@ defineEmits<{
const router = new Router(routes, props.initialPath); const router = new Router(routes, props.initialPath);
let pageMetadata = ref<null | ComputedRef<PageMetadata>>(); const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
let windowEl = ref<InstanceType<typeof XWindow>>(); const windowEl = ref<InstanceType<typeof XWindow>>();
const history = ref<{ path: string; key: any }[]>([ const history = ref<{ path: string; key: any }[]>([
{ {
path: router.getCurrentPath(), path: router.getCurrentPath(),

View file

@ -63,29 +63,22 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { import type { ComputedRef } from "vue";
computed, import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
ComputedRef, import type * as misskey from "firefish-js";
isRef,
onActivated,
onDeactivated,
ref,
watch,
} from "vue";
import * as misskey from "firefish-js";
import * as os from "@/os"; import * as os from "@/os";
import { import {
onScrollTop,
isTopVisible,
getScrollPosition,
getScrollContainer, getScrollContainer,
getScrollPosition,
isTopVisible,
onScrollTop,
} from "@/scripts/scroll"; } from "@/scripts/scroll";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
export type Paging< export interface Paging<
E extends keyof misskey.Endpoints = keyof misskey.Endpoints, E extends keyof misskey.Endpoints = keyof misskey.Endpoints,
> = { > {
endpoint: E; endpoint: E;
limit: number; limit: number;
params?: params?:
@ -104,7 +97,7 @@ export type Paging<
reversed?: boolean; reversed?: boolean;
offsetMode?: boolean; offsetMode?: boolean;
}; }
const SECOND_FETCH_LIMIT = 30; const SECOND_FETCH_LIMIT = 30;
@ -123,7 +116,10 @@ const emit = defineEmits<{
(ev: "queue", count: number): void; (ev: "queue", count: number): void;
}>(); }>();
type Item = { id: string; [another: string]: unknown }; interface Item {
id: string;
[another: string]: unknown;
}
const rootEl = ref<HTMLElement>(); const rootEl = ref<HTMLElement>();
const items = ref<Item[]>([]); const items = ref<Item[]>([]);
@ -207,12 +203,12 @@ const refresh = async (): void => {
}) })
.then( .then(
(res) => { (res) => {
let ids = items.value.reduce( const ids = items.value.reduce(
(a, b) => { (a, b) => {
a[b.id] = true; a[b.id] = true;
return a; return a;
}, },
{} as { [id: string]: boolean }, {} as Record<string, boolean>,
); );
for (let i = 0; i < res.length; i++) { for (let i = 0; i < res.length; i++) {

View file

@ -53,7 +53,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { sum } from "@/scripts/array"; import { sum } from "@/scripts/array";
import { pleaseLogin } from "@/scripts/please-login"; import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os"; import * as os from "@/os";

View file

@ -16,8 +16,8 @@
</MkInput> </MkInput>
<button <button
class="_button" class="_button"
@click="remove(i)"
:aria-label="i18n.t('remove')" :aria-label="i18n.t('remove')"
@click="remove(i)"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>

View file

@ -5,9 +5,9 @@
:z-priority="'high'" :z-priority="'high'"
:src="src" :src="src"
:transparent-bg="true" :transparent-bg="true"
tabindex="-1"
@click="modal?.close()" @click="modal?.close()"
@closed="emit('closed')" @closed="emit('closed')"
tabindex="-1"
> >
<MkMenu <MkMenu
:items="items" :items="items"
@ -28,7 +28,7 @@ import { ref } from "vue";
import MkModal from "./MkModal.vue"; import MkModal from "./MkModal.vue";
import MkMenu from "./MkMenu.vue"; import MkMenu from "./MkMenu.vue";
import { MenuItem } from "@/types/menu"; import type { MenuItem } from "@/types/menu";
defineProps<{ defineProps<{
items: MenuItem[]; items: MenuItem[];
@ -43,7 +43,7 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let modal = ref<InstanceType<typeof MkModal>>(); const modal = ref<InstanceType<typeof MkModal>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -86,8 +86,8 @@
{{ i18n.ts.quoteAttached {{ i18n.ts.quoteAttached
}}<button }}<button
class="_button" class="_button"
@click="quoteId = null"
:aria-label="i18n.t('removeQuote')" :aria-label="i18n.t('removeQuote')"
@click="quoteId = null"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>
@ -99,8 +99,8 @@
<MkAcct :user="u" /> <MkAcct :user="u" />
<button <button
class="_button" class="_button"
@click="removeVisibleUser(u)"
:aria-label="i18n.t('removeRecipient')" :aria-label="i18n.t('removeRecipient')"
@click="removeVisibleUser(u)"
> >
<i class="ph-x ph-bold ph-lg"></i> <i class="ph-x ph-bold ph-lg"></i>
</button> </button>
@ -234,16 +234,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import {
computed,
defineAsyncComponent,
inject, inject,
watch,
nextTick, nextTick,
onMounted, onMounted,
defineAsyncComponent,
ref, ref,
computed, watch,
} from "vue"; } from "vue";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import autosize from "autosize"; import autosize from "autosize";
import insertTextAtCursor from "insert-text-at-cursor"; import insertTextAtCursor from "insert-text-at-cursor";
import { length } from "stringz"; import { length } from "stringz";
@ -315,40 +315,42 @@ const cwInputEl = ref<HTMLInputElement | null>(null);
const hashtagsInputEl = ref<HTMLInputElement | null>(null); const hashtagsInputEl = ref<HTMLInputElement | null>(null);
const visibilityButton = ref<HTMLElement | null>(null); const visibilityButton = ref<HTMLElement | null>(null);
let posting = ref(false); const posting = ref(false);
let text = ref(props.initialText ?? ""); const text = ref(props.initialText ?? "");
let files = ref(props.initialFiles ?? []); const files = ref(props.initialFiles ?? []);
let poll = ref<{ const poll = ref<{
choices: string[]; choices: string[];
multiple: boolean; multiple: boolean;
expiresAt: string | null; expiresAt: string | null;
expiredAfter: string | null; expiredAfter: string | null;
} | null>(null); } | null>(null);
let useCw = ref(false); const useCw = ref(false);
let showPreview = ref(false); const showPreview = ref(false);
let cw = ref<string | null>(null); const cw = ref<string | null>(null);
let localOnly = ref<boolean>( const localOnly = ref<boolean>(
props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility
? defaultStore.state.localOnly ? defaultStore.state.localOnly
: defaultStore.state.defaultNoteLocalOnly, : defaultStore.state.defaultNoteLocalOnly,
); );
let visibility = ref( const visibility = ref(
props.initialVisibility ?? props.initialVisibility ??
((defaultStore.state.rememberNoteVisibility ((defaultStore.state.rememberNoteVisibility
? defaultStore.state.visibility ? defaultStore.state.visibility
: defaultStore.state : defaultStore.state
.defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]), .defaultNoteVisibility) as (typeof misskey.noteVisibilities)[number]),
); );
let visibleUsers = ref([]); const visibleUsers = ref([]);
if (props.initialVisibleUsers) { if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser); props.initialVisibleUsers.forEach(pushVisibleUser);
} }
let autocomplete = ref(null); const autocomplete = ref(null);
let draghover = ref(false); const draghover = ref(false);
let quoteId = ref(null); const quoteId = ref(null);
let hasNotSpecifiedMentions = ref(false); const hasNotSpecifiedMentions = ref(false);
let recentHashtags = ref(JSON.parse(localStorage.getItem("hashtags") || "[]")); const recentHashtags = ref(
let imeText = ref(""); JSON.parse(localStorage.getItem("hashtags") || "[]"),
);
const imeText = ref("");
const typing = throttle(3000, () => { const typing = throttle(3000, () => {
if (props.channel) { if (props.channel) {
@ -415,8 +417,8 @@ const maxTextLength = computed((): number => {
const canPost = computed((): boolean => { const canPost = computed((): boolean => {
return ( return (
!posting.value && !posting.value &&
(1 <= textLength.value || (textLength.value >= 1 ||
1 <= files.value.length || files.value.length >= 1 ||
!!poll.value || !!poll.value ||
!!props.renote) && !!props.renote) &&
textLength.value <= maxTextLength.value && textLength.value <= maxTextLength.value &&
@ -896,7 +898,7 @@ async function post() {
} }
} }
let token = undefined; let token;
if (postAccount.value) { if (postAccount.value) {
const storedAccounts = await getAccounts(); const storedAccounts = await getAccounts();
@ -976,7 +978,7 @@ function showActions(ev) {
); );
} }
let postAccount = ref<misskey.entities.UserDetailed | null>(null); const postAccount = ref<misskey.entities.UserDetailed | null>(null);
function openAccountMenu(ev: MouseEvent) { function openAccountMenu(ev: MouseEvent) {
openAccountMenu_( openAccountMenu_(

View file

@ -8,9 +8,9 @@
delay-on-touch-only="true" delay-on-touch-only="true"
> >
<div <div
class="file"
v-for="element in _files" v-for="element in _files"
:key="element.id" :key="element.id"
class="file"
@click="showFileMenu(element, $event)" @click="showFileMenu(element, $event)"
@contextmenu.prevent="showFileMenu(element, $event)" @contextmenu.prevent="showFileMenu(element, $event)"
> >
@ -30,7 +30,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, ref, computed } from "vue"; import { computed, defineAsyncComponent, ref } from "vue";
import { VueDraggable } from "vue-draggable-plus"; import { VueDraggable } from "vue-draggable-plus";
import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue"; import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -105,10 +105,11 @@ async function describe(file) {
{ {
done: (result) => { done: (result) => {
if (!result || result.canceled) return; if (!result || result.canceled) return;
let comment = result.result.length === 0 ? null : result.result; const comment =
result.result.length === 0 ? null : result.result;
os.api("drive/files/update", { os.api("drive/files/update", {
fileId: file.id, fileId: file.id,
comment: comment, comment,
}).then(() => { }).then(() => {
file.comment = comment; file.comment = comment;
}); });

View file

@ -21,8 +21,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef } from "vue"; import { shallowRef } from "vue";
import {} from "vue"; import type * as misskey from "firefish-js";
import * as misskey from "firefish-js";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import MkPostForm from "@/components/MkPostForm.vue"; import MkPostForm from "@/components/MkPostForm.vue";
@ -48,8 +47,8 @@ const emit = defineEmits<{
(ev: "closed"): void; (ev: "closed"): void;
}>(); }>();
let modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
let form = shallowRef<InstanceType<typeof MkPostForm>>(); const form = shallowRef<InstanceType<typeof MkPostForm>>();
function onPosted() { function onPosted() {
modal.value.close({ modal.value.close({

View file

@ -76,12 +76,12 @@ defineProps<{
}>(); }>();
// ServiceWorker registration // ServiceWorker registration
let registration = ref<ServiceWorkerRegistration | undefined>(); const registration = ref<ServiceWorkerRegistration | undefined>();
// If this browser supports push notification // If this browser supports push notification
let supported = ref(false); const supported = ref(false);
// If this browser has already subscribed to push notification // If this browser has already subscribed to push notification
let pushSubscription = ref<PushSubscription | null>(null); const pushSubscription = ref<PushSubscription | null>(null);
let pushRegistrationInServer = ref< const pushRegistrationInServer = ref<
| { | {
state?: string; state?: string;
key?: string; key?: string;
@ -209,6 +209,6 @@ if (navigator.serviceWorker == null) {
} }
defineExpose({ defineExpose({
pushRegistrationInServer: pushRegistrationInServer, pushRegistrationInServer,
}); });
</script> </script>

View file

@ -36,8 +36,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch, ref } from "vue"; import { onMounted, ref, watch } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import MkReactionIcon from "@/components/MkReactionIcon.vue"; import MkReactionIcon from "@/components/MkReactionIcon.vue";
import MkUserCardMini from "@/components/MkUserCardMini.vue"; import MkUserCardMini from "@/components/MkUserCardMini.vue";
import * as os from "@/os"; import * as os from "@/os";
@ -46,10 +46,10 @@ const props = defineProps<{
noteId: misskey.entities.Note["id"]; noteId: misskey.entities.Note["id"];
}>(); }>();
let note = ref<misskey.entities.Note>(); const note = ref<misskey.entities.Note>();
let tab = ref<string>(); const tab = ref<string>();
let reactions = ref<string[]>(); const reactions = ref<string[]>();
let users = ref(); const users = ref();
watch(tab, async () => { watch(tab, async () => {
const res = await os.api("notes/reactions", { const res = await os.api("notes/reactions", {

View file

@ -22,7 +22,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import XDetails from "@/components/MkReactionsViewer.details.vue"; import XDetails from "@/components/MkReactionsViewer.details.vue";
import XReactionIcon from "@/components/MkReactionIcon.vue"; import XReactionIcon from "@/components/MkReactionIcon.vue";
import * as os from "@/os"; import * as os from "@/os";

View file

@ -18,7 +18,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import { $i } from "@/account"; import { $i } from "@/account";
import XReaction from "@/components/MkReactionsViewer.reaction.vue"; import XReaction from "@/components/MkReactionsViewer.reaction.vue";

View file

@ -12,9 +12,9 @@
</button> </button>
<button <button
v-else v-else
v-tooltip.noDelay.bottom="i18n.ts.disabled"
class="eddddedb _button" class="eddddedb _button"
disabled="true" disabled="true"
v-tooltip.noDelay.bottom="i18n.ts.disabled"
> >
<i class="ph-repeat ph-bold ph-lg"></i> <i class="ph-repeat ph-bold ph-lg"></i>
</button> </button>
@ -31,7 +31,7 @@ import { $i } from "@/account";
import { useTooltip } from "@/scripts/use-tooltip"; import { useTooltip } from "@/scripts/use-tooltip";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import { MenuItem } from "@/types/menu"; import type { MenuItem } from "@/types/menu";
const props = defineProps<{ const props = defineProps<{
note: misskey.entities.Note; note: misskey.entities.Note;
@ -70,7 +70,7 @@ useTooltip(buttonRef, async (showing) => {
); );
}); });
let hasRenotedBefore = ref(false); const hasRenotedBefore = ref(false);
os.api("notes/renotes", { os.api("notes/renotes", {
noteId: props.note.id, noteId: props.note.id,
userId: $i.id, userId: $i.id,
@ -82,7 +82,7 @@ os.api("notes/renotes", {
const renote = (viaKeyboard = false, ev?: MouseEvent) => { const renote = (viaKeyboard = false, ev?: MouseEvent) => {
pleaseLogin(); pleaseLogin();
let buttonActions: Array<MenuItem> = []; const buttonActions: Array<MenuItem> = [];
if (props.note.visibility === "public") { if (props.note.visibility === "public") {
buttonActions.push({ buttonActions.push({
@ -191,7 +191,7 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
}); });
} }
if (canRenote) { if (canRenote.value) {
buttonActions.push({ buttonActions.push({
text: `${i18n.ts.renote} (${i18n.ts.local})`, text: `${i18n.ts.renote} (${i18n.ts.local})`,
icon: "ph-hand-fist ph-bold ph-lg", icon: "ph-hand-fist ph-bold ph-lg",

View file

@ -9,8 +9,8 @@
</button> </button>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { i18n } from "@/i18n";
import { ref } from "vue"; import { ref } from "vue";
import { i18n } from "@/i18n";
const props = defineProps<{ const props = defineProps<{
modelValue: boolean; modelValue: boolean;

View file

@ -160,7 +160,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, ref, computed } from "vue"; import { computed, defineAsyncComponent, ref } from "vue";
import { toUnicode } from "punycode/"; import { toUnicode } from "punycode/";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
@ -172,17 +172,17 @@ import { login } from "@/account";
import { instance } from "@/instance"; import { instance } from "@/instance";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
let signing = ref(false); const signing = ref(false);
let user = ref(null); const user = ref(null);
let username = ref(""); const username = ref("");
let password = ref(""); const password = ref("");
let token = ref(""); const token = ref("");
let host = ref(toUnicode(configHost)); const host = ref(toUnicode(configHost));
let totpLogin = ref(false); const totpLogin = ref(false);
let challengeData = ref(null); const challengeData = ref(null);
let queryingKey = ref(false); const queryingKey = ref(false);
let hCaptchaResponse = ref(null); const hCaptchaResponse = ref(null);
let reCaptchaResponse = ref(null); const reCaptchaResponse = ref(null);
const meta = computed(() => instance); const meta = computed(() => instance);

View file

@ -14,7 +14,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import {} from "vue";
import MkSignin from "@/components/MkSignin.vue"; import MkSignin from "@/components/MkSignin.vue";
import XModalWindow from "@/components/MkModalWindow.vue"; import XModalWindow from "@/components/MkModalWindow.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";

View file

@ -284,7 +284,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from "vue"; import { computed, ref } from "vue";
import getPasswordStrength from "syuilo-password-strength"; import getPasswordStrength from "syuilo-password-strength";
import { toUnicode } from "punycode/"; import { toUnicode } from "punycode/";
@ -314,15 +314,15 @@ const emit = defineEmits<{
const host = toUnicode(config.host); const host = toUnicode(config.host);
let hcaptcha = ref(); const hcaptcha = ref();
let recaptcha = ref(); const recaptcha = ref();
let username: string = ref(""); const username: string = ref("");
let password: string = ref(""); const password: string = ref("");
let retypedPassword: string = ref(""); const retypedPassword: string = ref("");
let invitationCode: string = ref(""); const invitationCode: string = ref("");
let email = ref(""); const email = ref("");
let usernameState: const usernameState:
| null | null
| "wait" | "wait"
| "ok" | "ok"
@ -331,8 +331,8 @@ let usernameState:
| "invalid-format" | "invalid-format"
| "min-range" | "min-range"
| "max-range" = ref(null); | "max-range" = ref(null);
let invitationState: null | "entered" = ref(null); const invitationState: null | "entered" = ref(null);
let emailState: const emailState:
| null | null
| "wait" | "wait"
| "ok" | "ok"
@ -343,12 +343,12 @@ let emailState:
| "unavailable:smtp" | "unavailable:smtp"
| "unavailable" | "unavailable"
| "error" = ref(null); | "error" = ref(null);
let passwordStrength: "" | "low" | "medium" | "high" = ref(""); const passwordStrength: "" | "low" | "medium" | "high" = ref("");
let passwordRetypeState: null | "match" | "not-match" = ref(null); const passwordRetypeState: null | "match" | "not-match" = ref(null);
let submitting: boolean = ref(false); const submitting: boolean = ref(false);
let ToSAgreement: boolean = ref(false); const ToSAgreement: boolean = ref(false);
let hCaptchaResponse = ref(null); const hCaptchaResponse = ref(null);
let reCaptchaResponse = ref(null); const reCaptchaResponse = ref(null);
const shouldDisableSubmitting = computed((): boolean => { const shouldDisableSubmitting = computed((): boolean => {
return ( return (

View file

@ -79,8 +79,8 @@ const el = ref<HTMLElement>();
const width = ref(0); const width = ref(0);
const height = ref(0); const height = ref(0);
const colors = ["#eb6f92", "#9ccfd8", "#f6c177", "#f6c177", "#f6c177"]; const colors = ["#eb6f92", "#9ccfd8", "#f6c177", "#f6c177", "#f6c177"];
let stop = false; let stop = false,
let ro: ResizeObserver | undefined; ro: ResizeObserver | undefined;
onMounted(() => { onMounted(() => {
if (!reducedMotion()) { if (!reducedMotion()) {

View file

@ -1,9 +1,9 @@
<template> <template>
<button <button
ref="buttonRef"
v-tooltip.noDelay.bottom="i18n.ts._gallery.like" v-tooltip.noDelay.bottom="i18n.ts._gallery.like"
class="button _button" class="button _button"
:class="$style.root" :class="$style.root"
ref="buttonRef"
@click.stop="toggleStar($event)" @click.stop="toggleStar($event)"
> >
<span v-if="!reacted"> <span v-if="!reacted">

View file

@ -13,13 +13,13 @@
</MkA> </MkA>
<MkA <MkA
v-else-if="!detailed && note.replyId" v-else-if="!detailed && note.replyId"
v-tooltip="i18n.ts.jumpToPrevious"
:to=" :to="
detailedView detailedView
? `#${note.replyId}` ? `#${note.replyId}`
: `${notePage(note)}#${note.replyId}` : `${notePage(note)}#${note.replyId}`
" "
behavior="browser" behavior="browser"
v-tooltip="i18n.ts.jumpToPrevious"
class="reply-icon" class="reply-icon"
@click.stop @click.stop
> >
@ -46,18 +46,18 @@
}" }"
> >
<XShowMoreButton <XShowMoreButton
ref="showMoreButton"
v-if="isLong && collapsed" v-if="isLong && collapsed"
ref="showMoreButton"
v-model="collapsed" v-model="collapsed"
v-on:keydown="focusFooter" @keydown="focusFooter"
></XShowMoreButton> ></XShowMoreButton>
<XCwButton <XCwButton
ref="cwButton"
v-if="note.cw && !showContent" v-if="note.cw && !showContent"
ref="cwButton"
v-model="showContent" v-model="showContent"
:note="note" :note="note"
v-on:keydown="focusFooter" @keydown="focusFooter"
v-on:update:model-value="(val) => emit('expanded', val)" @update:model-value="(val) => emit('expanded', val)"
/> />
<div <div
class="body" class="body"
@ -85,13 +85,13 @@
</MkA> </MkA>
<MkA <MkA
v-else-if="!detailed && note.replyId" v-else-if="!detailed && note.replyId"
v-tooltip="i18n.ts.jumpToPrevious"
:to=" :to="
detailedView detailedView
? `#${note.replyId}` ? `#${note.replyId}`
: `${notePage(note)}#${note.replyId}` : `${notePage(note)}#${note.replyId}`
" "
behavior="browser" behavior="browser"
v-tooltip="i18n.ts.jumpToPrevious"
class="reply-icon" class="reply-icon"
@click.stop @click.stop
> >
@ -139,7 +139,7 @@
(showMoreButton && collapsed) (showMoreButton && collapsed)
" "
tabindex="0" tabindex="0"
v-on:focus=" @focus="
cwButton?.focus(); cwButton?.focus();
showMoreButton?.focus(); showMoreButton?.focus();
" "
@ -157,9 +157,9 @@
</div> </div>
<MkButton <MkButton
v-if="hasMfm && defaultStore.state.animatedMfm" v-if="hasMfm && defaultStore.state.animatedMfm"
@click.stop="toggleMfm"
mini mini
rounded rounded
@click.stop="toggleMfm"
> >
<template v-if="disableMfm"> <template v-if="disableMfm">
<i class="ph-play ph-bold"></i> {{ i18n.ts._mfm.play }} <i class="ph-play ph-bold"></i> {{ i18n.ts._mfm.play }}
@ -177,7 +177,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import * as misskey from "firefish-js"; import type * as misskey from "firefish-js";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import * as os from "@/os"; import * as os from "@/os";
import XNoteSimple from "@/components/MkNoteSimple.vue"; import XNoteSimple from "@/components/MkNoteSimple.vue";
@ -222,7 +222,7 @@ const urls = props.note.text
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5) ? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
: null; : null;
let showContent = ref(false); const showContent = ref(false);
const mfms = props.note.text const mfms = props.note.text
? extractMfmWithAnimation(mfm.parse(props.note.text)) ? extractMfmWithAnimation(mfm.parse(props.note.text))
@ -230,7 +230,7 @@ const mfms = props.note.text
const hasMfm = ref(mfms && mfms.length > 0); const hasMfm = ref(mfms && mfms.length > 0);
let disableMfm = ref(defaultStore.state.animatedMfm); const disableMfm = ref(defaultStore.state.animatedMfm);
async function toggleMfm() { async function toggleMfm() {
if (disableMfm.value) { if (disableMfm.value) {

View file

@ -17,7 +17,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, watch, onBeforeUnmount, ref } from "vue"; import { onBeforeUnmount, onMounted, ref, watch } from "vue";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
const loaded = !!window.TagCanvas; const loaded = !!window.TagCanvas;
@ -39,11 +39,11 @@ const idForTags = Array.from(Array(16))
], ],
) )
.join(""); .join("");
let available = ref(false); const available = ref(false);
let rootEl = ref<HTMLElement | null>(null); const rootEl = ref<HTMLElement | null>(null);
let canvasEl = ref<HTMLCanvasElement | null>(null); const canvasEl = ref<HTMLCanvasElement | null>(null);
let tagsEl = ref<HTMLElement | null>(null); const tagsEl = ref<HTMLElement | null>(null);
let width = ref(300); const width = ref(300);
watch(available, () => { watch(available, () => {
try { try {

View file

@ -12,8 +12,8 @@
<div v-if="queue > 0" class="new"> <div v-if="queue > 0" class="new">
<button <button
class="_buttonPrimary _shadow" class="_buttonPrimary _shadow"
@click="tlComponent.scrollTop()"
:class="{ instant: !$store.state.animation }" :class="{ instant: !$store.state.animation }"
@click="tlComponent.scrollTop()"
> >
{{ i18n.ts.newNoteRecived }} {{ i18n.ts.newNoteRecived }}
<i class="ph-arrow-up ph-bold"></i> <i class="ph-arrow-up ph-bold"></i>
@ -28,7 +28,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, provide, onUnmounted, ref } from "vue"; import { computed, onUnmounted, provide, ref } from "vue";
import XNotes from "@/components/MkNotes.vue"; import XNotes from "@/components/MkNotes.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
import { stream } from "@/stream"; import { stream } from "@/stream";
@ -45,7 +45,7 @@ const props = defineProps<{
sound?: boolean; sound?: boolean;
}>(); }>();
let queue = ref(0); const queue = ref(0);
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "note"): void; (ev: "note"): void;
@ -83,13 +83,7 @@ const onChangeFollowing = () => {
} }
}; };
let endpoint; let endpoint, query, connection, connection2, tlHint, tlHintClosed;
let query;
let connection;
let connection2;
let tlHint;
let tlHintClosed;
if (props.src === "antenna") { if (props.src === "antenna") {
endpoint = "antennas/notes"; endpoint = "antennas/notes";
@ -223,7 +217,7 @@ function closeHint() {
} }
const pagination = { const pagination = {
endpoint: endpoint, endpoint,
limit: 10, limit: 10,
params: query, params: query,
}; };

View file

@ -30,7 +30,7 @@ const emit = defineEmits<{
}>(); }>();
const zIndex = os.claimZIndex("high"); const zIndex = os.claimZIndex("high");
let showing = ref(true); const showing = ref(true);
onMounted(() => { onMounted(() => {
window.setTimeout(() => { window.setTimeout(() => {

View file

@ -6,10 +6,10 @@
:with-ok-button="true" :with-ok-button="true"
:ok-button-disabled="false" :ok-button-disabled="false"
:can-close="false" :can-close="false"
style="padding: 12px"
@close="dialog.close()" @close="dialog.close()"
@closed="$emit('closed')" @closed="$emit('closed')"
@ok="ok()" @ok="ok()"
style="padding: 12px"
> >
<template #header>{{ title || i18n.ts.generateAccessToken }}</template> <template #header>{{ title || i18n.ts.generateAccessToken }}</template>
<div v-if="information" class="_section"> <div v-if="information" class="_section">
@ -19,7 +19,7 @@
<div style="margin-bottom: 16px"> <div style="margin-bottom: 16px">
<b>{{ i18n.ts.name }}</b> <b>{{ i18n.ts.name }}</b>
</div> </div>
<MkInput style="margin-bottom: 16px" v-model="name" /> <MkInput v-model="name" style="margin-bottom: 16px" />
</div> </div>
<div class="_section"> <div class="_section">
<div style="margin-bottom: 16px"> <div style="margin-bottom: 16px">
@ -32,10 +32,10 @@
i18n.ts.enableAll i18n.ts.enableAll
}}</MkButton> }}</MkButton>
<MkSwitch <MkSwitch
style="margin-bottom: 6px"
v-for="kind in initialPermissions || kinds" v-for="kind in initialPermissions || kinds"
:key="kind" :key="kind"
v-model="permissions[kind]" v-model="permissions[kind]"
style="margin-bottom: 6px"
>{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch >{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch
> >
</div> </div>
@ -45,7 +45,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import {} from "vue";
import { permissions as kinds } from "firefish-js"; import { permissions as kinds } from "firefish-js";
import MkInput from "./form/input.vue"; import MkInput from "./form/input.vue";
import MkSwitch from "./form/switch.vue"; import MkSwitch from "./form/switch.vue";
@ -75,8 +74,8 @@ const emit = defineEmits<{
}>(); }>();
const dialog = ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
let name = ref(props.initialName); const name = ref(props.initialName);
let permissions = ref({}); const permissions = ref({});
if (props.initialPermissions) { if (props.initialPermissions) {
for (const kind of props.initialPermissions) { for (const kind of props.initialPermissions) {

View file

@ -230,7 +230,7 @@ const isGlobalTimelineAvailable =
!instance.disableGlobalTimeline || !instance.disableGlobalTimeline ||
($i != null && ($i.isModerator || $i.isAdmin)); ($i != null && ($i.isModerator || $i.isAdmin));
let timelines = ["home"]; const timelines = ["home"];
if (isLocalTimelineAvailable) { if (isLocalTimelineAvailable) {
timelines.push("local"); timelines.push("local");

View file

@ -33,7 +33,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { shallowRef, ref } from "vue"; import { ref, shallowRef } from "vue";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import MkSparkle from "@/components/MkSparkle.vue"; import MkSparkle from "@/components/MkSparkle.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
@ -43,8 +43,8 @@ import * as os from "@/os";
const modal = shallowRef<InstanceType<typeof MkModal>>(); const modal = shallowRef<InstanceType<typeof MkModal>>();
let newRelease = ref(false); const newRelease = ref(false);
let data = ref(Object); const data = ref(Object);
os.api("release").then((res) => { os.api("release").then((res) => {
data.value = res; data.value = res;

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