diff --git a/data/constants.ts b/data/constants.ts index 5556a8966..b10f63d7e 100644 --- a/data/constants.ts +++ b/data/constants.ts @@ -1,3 +1,2 @@ export const REPO_URL = 'ajnart/homarr'; -export const CURRENT_VERSION = 'v0.11.2'; export const ICON_PICKER_SLICE_LIMIT = 36; diff --git a/next-i18next.config.js b/next-i18next.config.js index 5d52e6dcf..32d339fe9 100644 --- a/next-i18next.config.js +++ b/next-i18next.config.js @@ -24,6 +24,7 @@ module.exports = { 'vi', 'uk', 'zh', + 'el', ], localePath: path.resolve('./public/locales'), fallbackLng: 'en', diff --git a/package.json b/package.json index 05047d4d4..0ce42e768 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homarr", - "version": "0.11.2", + "version": "0.11.3", "description": "Homarr - A homepage for your server.", "license": "MIT", "repository": { @@ -45,6 +45,7 @@ "@nivo/line": "^0.79.1", "@tabler/icons": "^1.106.0", "@tanstack/react-query": "^4.2.1", + "@tanstack/react-query-devtools": "^4.24.4", "axios": "^0.27.2", "consola": "^2.15.3", "cookies-next": "^2.1.1", diff --git a/public/locales/da/layout/modals/icon-picker.json b/public/locales/da/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/da/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/de/layout/modals/icon-picker.json b/public/locales/de/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/de/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/el/authentication/login.json b/public/locales/el/authentication/login.json new file mode 100644 index 000000000..a3d52ba4d --- /dev/null +++ b/public/locales/el/authentication/login.json @@ -0,0 +1,27 @@ +{ + "title": "Καλώς ήρθατε!", + "text": "Παρακαλώ εισάγετε τον κωδικό σας", + "form": { + "fields": { + "password": { + "label": "Κωδικός", + "placeholder": "Ο κωδικός σας" + } + }, + "buttons": { + "submit": "Σύνδεση" + } + }, + "notifications": { + "checking": { + "title": "Έλεγχος κωδικού πρόσβασης", + "message": "Ο κωδικός πρόσβασής σας ελέγχεται..." + }, + "correct": { + "title": "Σύνδεση επιτυχής, ανακατεύθυνση..." + }, + "wrong": { + "title": "Ο κωδικός που εισαγάγατε είναι εσφαλμένος. Προσπαθήστε ξανά." + } + } +} diff --git a/public/locales/el/common.json b/public/locales/el/common.json new file mode 100644 index 000000000..8e125cf4d --- /dev/null +++ b/public/locales/el/common.json @@ -0,0 +1,28 @@ +{ + "save": "Αποθήκευση", + "about": "Σχετικά", + "cancel": "Ακύρωση", + "close": "Κλείσιμο", + "delete": "Διαγραφή", + "ok": "ΟΚ", + "edit": "Επεξεργασία", + "version": "Έκδοση", + "changePosition": "Αλλαγή θέσης", + "remove": "Αφαίρεση", + "removeConfirm": "Είστε σίγουροι ότι θέλετε να καταργήσετε το {{item}} ;", + "sections": { + "settings": "Ρυθμίσεις", + "dangerZone": "Επικίνδυνη Περιοχή" + }, + "secrets": { + "apiKey": "Κλειδί Api", + "username": "Όνομα Χρήστη", + "password": "Κωδικός" + }, + "tip": "Συμβουλές: ", + "time": { + "seconds": "δευτερόλεπτα", + "minutes": "λεπτά", + "hours": "ώρες" + } +} \ No newline at end of file diff --git a/public/locales/el/layout/element-selector/selector.json b/public/locales/el/layout/element-selector/selector.json new file mode 100644 index 000000000..9e80fc27d --- /dev/null +++ b/public/locales/el/layout/element-selector/selector.json @@ -0,0 +1,11 @@ +{ + "modal": { + "title": "Προσθήκη νέου πλακιδίου", + "text": "Τα πλακάκια είναι το κύριο στοιχείο του Homarr. Χρησιμοποιούνται για την εμφάνιση των εφαρμογών σας και άλλων πληροφοριών. Μπορείτε να προσθέσετε όσα πλακίδια θέλετε." + }, + "widgetDescription": "Τα widgets αλληλεπιδρούν με τις εφαρμογές σας, για να σας παρέχουν περισσότερο έλεγχο των εφαρμογών σας. Συνήθως απαιτούν πρόσθετες ρυθμίσεις πριν από τη χρήση.", + "goBack": "Επιστροφή στο προηγούμενο βήμα", + "actionIcon": { + "tooltip": "Προσθέστε ένα πλακίδιο" + } +} diff --git a/public/locales/el/layout/header/actions/toggle-edit-mode.json b/public/locales/el/layout/header/actions/toggle-edit-mode.json new file mode 100644 index 000000000..6651e1402 --- /dev/null +++ b/public/locales/el/layout/header/actions/toggle-edit-mode.json @@ -0,0 +1,16 @@ +{ + "description": "Στη Λειτουργία επεξεργασίας, μπορείτε να προσαρμόσετε τα πλακίδια και να ρυθμίσετε τις εφαρμογές. Οι αλλαγές δεν αποθηκεύονται μέχρι να βγείτε από τη λειτουργία επεξεργασίας.", + "button": { + "disabled": "Λειτουργία επεξεργασίας", + "enabled": "Έξοδος και Αποθήκευση" + }, + "popover": { + "title": "Η λειτουργία επεξεργασίας είναι ενεργοποιημένη για <1>{{size}} μέγεθος", + "text": "Μπορείτε να προσαρμόσετε και να ρυθμίσετε τις εφαρμογές σας τώρα. Οι αλλαγές δεν αποθηκεύονται μέχρι να βγείτε από τη λειτουργία επεξεργασίας" + }, + "screenSizes": { + "small": "μικρό", + "medium": "μεσαίο", + "large": "μεγάλο" + } +} diff --git a/public/locales/el/layout/mobile/drawer.json b/public/locales/el/layout/mobile/drawer.json new file mode 100644 index 000000000..a8a2f5d85 --- /dev/null +++ b/public/locales/el/layout/mobile/drawer.json @@ -0,0 +1,3 @@ +{ + "title": "{{position}} πλαϊνή μπάρα" +} diff --git a/public/locales/el/layout/modals/about.json b/public/locales/el/layout/modals/about.json new file mode 100644 index 000000000..f81207014 --- /dev/null +++ b/public/locales/el/layout/modals/about.json @@ -0,0 +1,7 @@ +{ + "description": "Το Homarr είναι ένα κομψό, μοντέρνο ταμπλό που βάζει όλες τις εφαρμογές και τις υπηρεσίες σας στα χέρια σας. Με το Homarr, μπορείτε να έχετε πρόσβαση και να ελέγχετε τα πάντα σε μια βολική τοποθεσία. Το Homarr ενσωματώνεται απρόσκοπτα με τις εφαρμογές που έχετε προσθέσει, παρέχοντάς σας πολύτιμες πληροφορίες και δίνοντάς σας πλήρη έλεγχο. Η εγκατάσταση είναι πανεύκολη και το Homarr υποστηρίζει ένα ευρύ φάσμα μεθόδων ανάπτυξης.", + "i18n": "Φορτωμένα πεδία ονομάτων μετάφρασης I18n", + "locales": "Διαμορφωμένες τοπικές ρυθμίσεις I18n", + "contact": "Έχετε προβλήματα ή ερωτήσεις; Συνδεθείτε μαζί μας!", + "addToDashboard": "Προσθήκη στο ταμπλό" +} diff --git a/public/locales/el/layout/modals/add-app.json b/public/locales/el/layout/modals/add-app.json new file mode 100644 index 000000000..d5047b489 --- /dev/null +++ b/public/locales/el/layout/modals/add-app.json @@ -0,0 +1,68 @@ +{ + "tabs": { + "general": "Γενικά", + "behaviour": "Συμπεριφορά", + "network": "Δίκτυο", + "appearance": "Εμφάνιση", + "integration": "Ενσωμάτωση" + }, + "general": { + "appname": { + "label": "Όνομα εφαρμογής", + "description": "Χρησιμοποιείται για την εμφάνιση της εφαρμογής στο ταμπλό." + }, + "internalAddress": { + "label": "Εσωτερική διεύθυνση", + "description": "Η εσωτερική διεύθυνση IP της εφαρμογής." + }, + "externalAddress": { + "label": "Εξωτερική διεύθυνση", + "description": "URL που θα ανοίγει όταν κάνετε κλικ στην εφαρμογή." + } + }, + "behaviour": { + "isOpeningNewTab": { + "label": "Άνοιγμα σε νέα καρτέλα", + "description": "Ανοίξτε την εφαρμογή σε νέα καρτέλα αντί της τρέχουσας." + } + }, + "network": { + "statusChecker": { + "label": "Έλεγχος κατάστασης", + "description": "Ελέγχει αν η εφαρμογή σας είναι συνδεδεμένη χρησιμοποιώντας ένα απλό αίτημα HTTP(S)." + }, + "statusCodes": { + "label": "Κωδικοί κατάστασης HTTP", + "description": "Οι κωδικοί κατάστασης HTTP που θεωρούνται online." + } + }, + "appearance": { + "icon": { + "label": "Εικονίδιο εφαρμογής", + "description": "Το εικονίδιο που θα εμφανίζεται στο ταμπλό." + } + }, + "integration": { + "type": { + "label": "Διαμόρφωση ενσωμάτωσης", + "description": "Η διαμόρφωση ενσωμάτωσης που θα χρησιμοποιηθεί για τη σύνδεση με την εφαρμογή σας.", + "placeholder": "Επιλέξτε Ενσωμάτωση", + "defined": "Καθορισμένο", + "undefined": "Απροσδιόριστο", + "public": "Δημόσιο", + "private": "Ιδιωτικό", + "explanationPrivate": "Ένα ιδιωτικό μυστικό θα αποσταλεί στον διακομιστή μόνο μία φορά. Μόλις το πρόγραμμα περιήγησής σας ανανεώσει τη σελίδα, δεν θα αποσταλεί ποτέ ξανά.", + "explanationPublic": "Ένα δημόσιο μυστικό αποστέλλεται πάντα στον πελάτη και είναι προσβάσιμο μέσω του API. Δεν πρέπει να περιέχει εμπιστευτικές τιμές όπως ονόματα χρηστών, κωδικούς πρόσβασης, μάρκες, πιστοποιητικά και παρόμοια!" + }, + "secrets": { + "description": "Για να ενημερώσετε ένα μυστικό, εισαγάγετε μια τιμή και κάντε κλικ στο κουμπί αποθήκευσης. Για να διαγράψετε ένα μυστικό, χρησιμοποιήστε το κουμπί διαγραφής.", + "warning": "Τα διαπιστευτήριά σας λειτουργούν ως πρόσβαση για τις ενσωματώσεις σας και δεν θα πρέπει ποτέ να τα μοιράζεστε με κανέναν άλλον. Η ομάδα Homarr δεν θα σας ζητήσει ποτέ διαπιστευτήρια. Βεβαιωθείτε ότι αποθηκεύετε και διαχειρίζεστε τα μυστικά σας με ασφάλεια.", + "clear": "Καθαρισμός μυστικού", + "save": "Αποθήκευση μυστικού", + "update": "Ενημέρωση μυστικού" + } + }, + "validation": { + "popover": "Η φόρμα σας περιέχει άκυρα δεδομένα. Ως εκ τούτου, δεν μπορεί να αποθηκευτεί. Παρακαλούμε επιλύστε όλα τα προβλήματα και κάντε ξανά κλικ σε αυτό το κουμπί για να αποθηκεύσετε τις αλλαγές σας" + } +} diff --git a/public/locales/el/layout/modals/change-position.json b/public/locales/el/layout/modals/change-position.json new file mode 100644 index 000000000..52619c888 --- /dev/null +++ b/public/locales/el/layout/modals/change-position.json @@ -0,0 +1,8 @@ +{ + "xPosition": "Θέση του άξονα X", + "width": "Πλάτος", + "height": "Ύψος", + "yPosition": "Θέση του άξονα Y", + "zeroOrHigher": "0 ή υψηλότερο", + "betweenXandY": "Μεταξύ {min} και {max}" +} \ No newline at end of file diff --git a/public/locales/el/layout/modals/icon-picker.json b/public/locales/el/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/el/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/el/modules/calendar.json b/public/locales/el/modules/calendar.json new file mode 100644 index 000000000..8d5e0422c --- /dev/null +++ b/public/locales/el/modules/calendar.json @@ -0,0 +1,15 @@ +{ + "descriptor": { + "name": "Ημερολόγιο", + "description": "Εμφανίζει ένα ημερολόγιο με τις επερχόμενες κυκλοφορίες, από τις υποστηριζόμενες ενσωματώσεις.", + "settings": { + "title": "Ρυθμίσεις για το widget ημερολογίου", + "sundayStart": { + "label": "Ξεκινήστε την εβδομάδα από την Κυριακή" + }, + "radarrReleaseType": { + "label": "Τύπος κυκλοφορίας Radarr" + } + } + } +} diff --git a/public/locales/el/modules/common-media-cards.json b/public/locales/el/modules/common-media-cards.json new file mode 100644 index 000000000..ea5ff769a --- /dev/null +++ b/public/locales/el/modules/common-media-cards.json @@ -0,0 +1,6 @@ +{ + "buttons": { + "play": "Αναπαραγωγή", + "request": "Αίτημα" + } +} \ No newline at end of file diff --git a/public/locales/el/modules/common.json b/public/locales/el/modules/common.json new file mode 100644 index 000000000..1919e13df --- /dev/null +++ b/public/locales/el/modules/common.json @@ -0,0 +1,10 @@ +{ + "settings": { + "label": "Ρυθμίσεις" + }, + "errors": { + "unmappedOptions": { + "text": "Εντοπίστηκε αχρησιμοποίητη παράμετρος στη διαμόρφωση
{{key}}. Το Homarr δεν μπορεί να ερμηνεύσει και να χρησιμοποιήσει αυτή την παράμετρο. Για να αποφύγετε οποιαδήποτε απροσδόκητη συμπεριφορά, δημιουργήστε αντίγραφα ασφαλείας των ρυθμίσεων σας και διορθώστε τις ρυθμίσεις σας." + } + } +} diff --git a/public/locales/el/modules/dashdot.json b/public/locales/el/modules/dashdot.json new file mode 100644 index 000000000..00e95954d --- /dev/null +++ b/public/locales/el/modules/dashdot.json @@ -0,0 +1,54 @@ +{ + "descriptor": { + "name": "Dash.", + "description": "Εμφανίζει τα γραφήματα μιας εξωτερικής Dash. μέσα στο Homarr.", + "settings": { + "title": "Ρυθμίσεις για το widget Dash", + "cpuMultiView": { + "label": "Προβολή πολλαπλών πυρήνων CPU" + }, + "storageMultiView": { + "label": "Προβολή πολλαπλών μονάδων αποθήκευσης" + }, + "useCompactView": { + "label": "Χρήση Συμπαγούς Προβολής" + }, + "graphs": { + "label": "Γραφήματα" + }, + "url": { + "label": "Dash. URL" + } + } + }, + "card": { + "title": "Dash.", + "errors": { + "noService": "Δεν βρέθηκε υπηρεσία Dash. Παρακαλούμε προσθέστε μία στο ταμπλό Homarr ή ορίστε μια Dash. URL στις επιλογές της ενότητας", + "noInformation": "Δεν μπορεί να αποκτήσει πληροφορίες από το dash. - τρέχετε την τελευταία έκδοση;" + }, + "graphs": { + "storage": { + "title": "Αποθηκευτικός χώρος", + "label": "Αποθηκευτικός χώρος:" + }, + "network": { + "title": "Δίκτυο", + "label": "Δίκτυο:", + "metrics": { + "download": "Κάτω", + "upload": "Πάνω" + } + }, + "cpu": { + "title": "CPU" + }, + "memory": { + "title": "Μνήμη RAM" + }, + "gpu": { + "title": "GPU" + } + } + } +} diff --git a/public/locales/el/modules/date.json b/public/locales/el/modules/date.json new file mode 100644 index 000000000..73bf847ca --- /dev/null +++ b/public/locales/el/modules/date.json @@ -0,0 +1,12 @@ +{ + "descriptor": { + "name": "Ημερομηνία και ώρα", + "description": "Εμφανίζει την τρέχουσα ημερομηνία και ώρα.", + "settings": { + "title": "Ρυθμίσεις για το widget ημερομηνίας και ώρας", + "display24HourFormat": { + "label": "Εμφάνιση πλήρης ώρας(24-ώρο)" + } + } + } +} diff --git a/public/locales/el/modules/dlspeed.json b/public/locales/el/modules/dlspeed.json new file mode 100644 index 000000000..0be8f2295 --- /dev/null +++ b/public/locales/el/modules/dlspeed.json @@ -0,0 +1,35 @@ +{ + "descriptor": { + "name": "Ταχύτητα Λήψης", + "description": "Εμφανίζει την ταχύτητα λήψης και μεταφόρτωσης των υποστηριζόμενων ενσωματώσεων." + }, + "card": { + "table": { + "header": { + "name": "Όνομα", + "size": "Μέγεθος", + "download": "Κάτω", + "upload": "Πάνω", + "estimatedTimeOfArrival": "Εκτιμώμενος χρόνος αναμονής", + "progress": "Πρόοδος" + }, + "body": { + "nothingFound": "Δεν βρέθηκαν torrents" + } + }, + "lineChart": { + "title": "Τρέχουσα ταχύτητα λήψης", + "download": "Λήψη: {{download}}", + "upload": "Ανέβασμα: {{upload}}", + "timeSpan": "{{seconds}} δευτερόλεπτα πριν", + "totalDownload": "Λήψη: {{download}}/s", + "totalUpload": "Ανέβασμα: {{upload}}/s" + }, + "errors": { + "noDownloadClients": { + "title": "Δεν βρέθηκαν υποστηριζόμενα προγράμματα λήψης!", + "text": "Προσθέστε μια υπηρεσία λήψης για να δείτε τις τρέχουσες λήψεις σας" + } + } + } +} diff --git a/public/locales/el/modules/docker.json b/public/locales/el/modules/docker.json new file mode 100644 index 000000000..854d7bc4b --- /dev/null +++ b/public/locales/el/modules/docker.json @@ -0,0 +1,83 @@ +{ + "descriptor": { + "name": "Docker", + "description": "Σας επιτρέπει να βλέπετε και να διαχειρίζεστε εύκολα όλα τα Docker Containers σας." + }, + "search": { + "placeholder": "Αναζήτηση με βάση container ή όνομα εικόνας" + }, + "table": { + "header": { + "name": "Όνομα", + "image": "Εικόνα", + "ports": "Θύρες", + "state": "Κατάσταση" + }, + "body": { + "portCollapse": "{{ports}} περισσότερα" + }, + "states": { + "running": "Εκτελείται", + "created": "Δημιουργήθηκε", + "stopped": "Διακόπηκε", + "unknown": "Άγνωστο" + } + }, + "actionBar": { + "addService": { + "title": "Προσθήκη εφαρμογής", + "message": "Προσθήκη εφαρμογής στο Homarr" + }, + "restart": { + "title": "Επανεκκίνηση" + }, + "stop": { + "title": "Διακοπή" + }, + "start": { + "title": "Έναρξη" + }, + "refreshData": { + "title": "Ανανέωση δεδομένων" + }, + "remove": { + "title": "Αφαίρεση" + }, + "addToHomarr": { + "title": "Προσθήκη στο Homarr" + } + }, + "actions": { + "start": { + "start": "Ξεκινάει", + "end": "Ξεκίνησε" + }, + "stop": { + "start": "Διακόπτεται", + "end": "Διακόπηκε" + }, + "restart": { + "start": "Γίνεται επανεκκίνηση", + "end": "Επανεκκινήθηκε" + }, + "remove": { + "start": "Αφαιρείται", + "end": "Αφαιρέθηκε" + } + }, + "errors": { + "integrationFailed": { + "title": "Η ενσωμάτωση του Docker απέτυχε", + "message": "Μήπως ξεχάσατε να προσαρτήσετε την υποδοχή docker;" + }, + "unknownError": { + "title": "Παρουσιάστηκε σφάλμα" + }, + "oneServiceAtATime": { + "title": "Παρακαλώ προσθέστε μόνο μία εφαρμογή ή υπηρεσία τη φορά!" + } + }, + "actionIcon": { + "tooltip": "Docker" + } +} diff --git a/public/locales/el/modules/overseerr.json b/public/locales/el/modules/overseerr.json new file mode 100644 index 000000000..8fcc723d9 --- /dev/null +++ b/public/locales/el/modules/overseerr.json @@ -0,0 +1,30 @@ +{ + "descriptor": { + "name": "Overseerr", + "description": "Σας επιτρέπει να αναζητήσετε και να προσθέσετε πολυμέσα από το Overseerr ή το Jellyseerr." + }, + "popup": { + "item": { + "buttons": { + "askFor": "Ρωτήστε για {{title}}", + "cancel": "Ακύρωση", + "request": "Αίτημα" + }, + "alerts": { + "automaticApproval": { + "title": "Χρήση κλειδιού API", + "text": "Το αίτημα αυτό θα εγκριθεί αυτόματα" + } + } + }, + "seasonSelector": { + "caption": "Επιλέξτε τις σεζόν που θέλετε να κατεβάσετε", + "table": { + "header": { + "season": "Σεζόν", + "numberOfEpisodes": "Αριθμός επεισοδίων" + } + } + } + } +} diff --git a/public/locales/el/modules/ping.json b/public/locales/el/modules/ping.json new file mode 100644 index 000000000..70d316ee2 --- /dev/null +++ b/public/locales/el/modules/ping.json @@ -0,0 +1,11 @@ +{ + "descriptor": { + "name": "Ping", + "description": "Εμφανίζει μια ένδειξη κατάστασης ανάλογα με τον κωδικό απόκρισης HTTP μιας δεδομένης διεύθυνσης URL." + }, + "states": { + "online": "Online {{response}}", + "offline": "Χωρίς σύνδεση {{response}}", + "loading": "Φόρτωση..." + } +} diff --git a/public/locales/el/modules/search.json b/public/locales/el/modules/search.json new file mode 100644 index 000000000..34e73ce66 --- /dev/null +++ b/public/locales/el/modules/search.json @@ -0,0 +1,30 @@ +{ + "descriptor": { + "name": "Μπάρα Αναζήτησης", + "description": "Μια γραμμή αναζήτησης που σας επιτρέπει να κάνετε αναζήτηση στην προσαρμοσμένη μηχανή αναζήτησης σας, στο YouTube και στις υποστηριζόμενες ενσωματώσεις." + }, + "input": { + "placeholder": "Αναζήτηση στον Ιστό..." + }, + "switched-to": "Αλλαγή σε", + "searchEngines": { + "search": { + "name": "Ιστός", + "description": "Αναζήτηση..." + }, + "youtube": { + "name": "YouTube", + "description": "Αναζήτηση στο YouTube" + }, + "torrents": { + "name": "Τόρρεντ", + "description": "Αναζήτηση για Torrents" + }, + "overseerr": { + "name": "Overseerr", + "description": "Αναζήτηση για ταινίες και τηλεοπτικές εκπομπές στο Overseerr" + } + }, + "tip": "Μπορείτε να επιλέξετε τη γραμμή αναζήτησης με τη συντόμευση ", + "switchedSearchEngine": "Εναλλαγή για αναζήτηση με {{searchEngine}}" +} diff --git a/public/locales/el/modules/torrents-status.json b/public/locales/el/modules/torrents-status.json new file mode 100644 index 000000000..5411a4b2e --- /dev/null +++ b/public/locales/el/modules/torrents-status.json @@ -0,0 +1,72 @@ +{ + "descriptor": { + "name": "Τόρρεντ", + "description": "Εμφανίζει μια λίστα με torrents από υποστηριζόμενους εφαρμογές Torrent.", + "settings": { + "title": "Ρυθμίσεις για το widget Torrent", + "refreshInterval": { + "label": "Χρονικό διάστημα ανανέωσης (σε δευτερόλεπτα)" + }, + "displayCompletedTorrents": { + "label": "Εμφάνιση ολοκληρωμένων torrents" + }, + "displayStaleTorrents": { + "label": "Εμφάνιση stale torrents" + } + } + }, + "card": { + "footer": { + "error": "Σφάλμα", + "lastUpdated": "Τελευταία ενημέρωση {{time}} πριν" + }, + "table": { + "header": { + "name": "Όνομα", + "size": "Μέγεθος", + "download": "Κάτω", + "upload": "Πάνω", + "estimatedTimeOfArrival": "Εκτιμώμενος χρόνος αναμονής", + "progress": "Πρόοδος" + }, + "item": { + "text": "Διαχειρίζεται από {{appName}}, {{ratio}} αναλογία" + }, + "body": { + "nothingFound": "Δεν βρέθηκαν torrents" + } + }, + "lineChart": { + "title": "Τρέχουσα ταχύτητα λήψης", + "download": "Λήψη: {{download}}", + "upload": "Ανέβασμα: {{upload}}", + "timeSpan": "{{seconds}} δευτερόλεπτα πριν", + "totalDownload": "Λήψη: {{download}}/s", + "totalUpload": "Ανέβασμα: {{upload}}/s" + }, + "errors": { + "noDownloadClients": { + "title": "Δεν βρέθηκαν υποστηριζόμενες εφαρμογές Torrent!", + "text": "Προσθέστε έναν υποστηριζόμενης εφαρμογής Torrent για να δείτε τις τρέχουσες λήψεις σας" + }, + "generic": { + "title": "Παρουσιάστηκε ένα απροσδόκητο σφάλμα", + "text": "Το Homarr δεν μπόρεσε να επικοινωνήσει με τις εφαρμογές Torrent. Ελέγξτε τις ρυθμίσεις σας" + } + }, + "loading": { + "title": "Φόρτωση..." + }, + "popover": { + "introductionPrefix": "Διαχειριζόμενα από", + "metrics": { + "queuePosition": "Θέση ουράς - {{position}}", + "progress": "Πρόοδος - {{progress}}%", + "totalSelectedSize": "Σύνολο - {{totalSize}}", + "state": "Κατάσταση - {{state}}", + "ratio": "Αναλογία -", + "completed": "Ολοκληρώθηκε" + } + } + } +} diff --git a/public/locales/el/modules/usenet.json b/public/locales/el/modules/usenet.json new file mode 100644 index 000000000..a230785a7 --- /dev/null +++ b/public/locales/el/modules/usenet.json @@ -0,0 +1,49 @@ +{ + "descriptor": { + "name": "Usenet", + "description": "Σας επιτρέπει να δείτε και να διαχειριστείτε το Usenet instance σας." + }, + "card": { + "errors": { + "noDownloadClients": { + "title": "Δεν βρέθηκαν υποστηριζόμενα προγράμματα λήψης!", + "text": "Προσθέστε έναν υποστηριζόμενο πρόγραμμα λήψης Usenet για να δείτε τις τρέχουσες λήψεις σας" + } + } + }, + "tabs": { + "queue": "Ουρά", + "history": "Ιστορικό" + }, + "info": { + "sizeLeft": "Μέγεθος που απομένει", + "paused": "Σε παύση" + }, + "queue": { + "header": { + "name": "Όνομα", + "size": "Μέγεθος", + "eta": "Εκτιμώμενος χρόνος αναμονής", + "progress": "Πρόοδος" + }, + "empty": "Άδειο", + "error": { + "title": "Σφάλμα", + "message": "Παρουσιάστηκε σφάλμα" + }, + "paused": "Σε παύση" + }, + "history": { + "header": { + "name": "Όνομα", + "size": "Μέγεθος", + "duration": "Διάρκεια" + }, + "empty": "Άδειο", + "error": { + "title": "Σφάλμα", + "message": "Σφάλμα φόρτωσης ιστορικού" + }, + "paused": "Σε παύση" + } +} diff --git a/public/locales/el/modules/weather.json b/public/locales/el/modules/weather.json new file mode 100644 index 000000000..b358279a0 --- /dev/null +++ b/public/locales/el/modules/weather.json @@ -0,0 +1,33 @@ +{ + "descriptor": { + "name": "Καιρός", + "description": "Εμφανίζει τις τρέχουσες πληροφορίες καιρού μιας καθορισμένης τοποθεσίας.", + "settings": { + "title": "Ρυθμίσεις για το widget καιρού", + "displayInFahrenheit": { + "label": "Εμφάνιση σε Φαρενάιτ" + }, + "location": { + "label": "Τοποθεσία καιρού" + } + } + }, + "card": { + "weatherDescriptions": { + "clear": "Καθαρός", + "mainlyClear": "Κυρίως καθαρός", + "fog": "Ομίχλη", + "drizzle": "Ψιχάλες", + "freezingDrizzle": "Παγωμένο ψιλόβροχο", + "rain": "Βροχή", + "freezingRain": "Παγωμένη βροχή", + "snowFall": "Χιονόπτωση", + "snowGrains": "Κόκκοι χιονιού", + "rainShowers": "Βροχοπτώσεις", + "snowShowers": "Χιονοπτώσεις", + "thunderstorm": "Καταιγίδα", + "thunderstormWithHail": "Καταιγίδα με χαλάζι", + "unknown": "Άγνωστο" + } + } +} diff --git a/public/locales/el/settings/common.json b/public/locales/el/settings/common.json new file mode 100644 index 000000000..d5ae45d91 --- /dev/null +++ b/public/locales/el/settings/common.json @@ -0,0 +1,29 @@ +{ + "title": "Ρυθμίσεις", + "tooltip": "Ρυθμίσεις", + "tabs": { + "common": "Συχνές επιλογές", + "customizations": "Παραμετροποιήσεις" + }, + "tips": { + "configTip": "Ανεβάστε το αρχείο ρυθμίσεών σας σύροντάς το στη σελίδα!" + }, + "credits": { + "madeWithLove": "Φτιαγμένο με ❤️ από @" + }, + "grow": "Πλέγμα ανάπτυξης (παίρνει όλο το χώρο)", + "layout": { + "title": "Διάταξη ταμπλό", + "main": "Κύριο", + "sidebar": "Πλαϊνή μπάρα", + "cannotturnoff": "Δεν μπορεί να απενεργοποιηθεί", + "dashboardlayout": "Διάταξη ταμπλό", + "enablersidebar": "Απόκρυψη δεξιάς πλευρικής στήλης", + "enablelsidebar": "Ενεργοποίηση της αριστερής πλευρικής γραμμής", + "enablesearchbar": "Ενεργοποίηση της γραμμής αναζήτησης", + "enabledocker": "Ενεργοποίηση ενσωμάτωση docker", + "enableping": "Ενεργοποίηση pings", + "enablelsidebardesc": "Προαιρετικά. Μπορεί να χρησιμοποιηθεί μόνο για εφαρμογές και ενσωματώσεις", + "enablersidebardesc": "Προαιρετικά. Μπορεί να χρησιμοποιηθεί μόνο για εφαρμογές και ενσωματώσεις" + } +} diff --git a/public/locales/el/settings/customization/app-width.json b/public/locales/el/settings/customization/app-width.json new file mode 100644 index 000000000..d096c5e78 --- /dev/null +++ b/public/locales/el/settings/customization/app-width.json @@ -0,0 +1,3 @@ +{ + "label": "Πλάτος εφαρμογής" +} \ No newline at end of file diff --git a/public/locales/el/settings/customization/color-selector.json b/public/locales/el/settings/customization/color-selector.json new file mode 100644 index 000000000..5c2b6f5b4 --- /dev/null +++ b/public/locales/el/settings/customization/color-selector.json @@ -0,0 +1,3 @@ +{ + "suffix": "{{color}} χρώμα" +} \ No newline at end of file diff --git a/public/locales/el/settings/customization/opacity-selector.json b/public/locales/el/settings/customization/opacity-selector.json new file mode 100644 index 000000000..f9cb3c9c4 --- /dev/null +++ b/public/locales/el/settings/customization/opacity-selector.json @@ -0,0 +1,3 @@ +{ + "label": "Αδιαφάνεια εφαρμογής" +} \ No newline at end of file diff --git a/public/locales/el/settings/customization/page-appearance.json b/public/locales/el/settings/customization/page-appearance.json new file mode 100644 index 000000000..bd6e5bb2f --- /dev/null +++ b/public/locales/el/settings/customization/page-appearance.json @@ -0,0 +1,24 @@ +{ + "pageTitle": { + "label": "Τίτλος Σελίδας" + }, + "metaTitle": { + "label": "Meta Τίτλος" + }, + "logo": { + "label": "Λογότυπο" + }, + "favicon": { + "label": "Έμβλημα" + }, + "background": { + "label": "Φόντο" + }, + "customCSS": { + "label": "Προσαρμοσμένη CSS", + "placeholder": "Το προσαρμοσμένο CSS θα εφαρμοστεί τελευταίο" + }, + "buttons": { + "submit": "Υποβολή" + } +} diff --git a/public/locales/el/settings/customization/shade-selector.json b/public/locales/el/settings/customization/shade-selector.json new file mode 100644 index 000000000..30a19d429 --- /dev/null +++ b/public/locales/el/settings/customization/shade-selector.json @@ -0,0 +1,3 @@ +{ + "label": "Απόχρωση" +} \ No newline at end of file diff --git a/public/locales/el/settings/general/color-schema.json b/public/locales/el/settings/general/color-schema.json new file mode 100644 index 000000000..f2e9b1bb2 --- /dev/null +++ b/public/locales/el/settings/general/color-schema.json @@ -0,0 +1,3 @@ +{ + "label": "Εναλλαγή στη λειτουργία {{scheme}}" +} \ No newline at end of file diff --git a/public/locales/el/settings/general/config-changer.json b/public/locales/el/settings/general/config-changer.json new file mode 100644 index 000000000..4fe61e49c --- /dev/null +++ b/public/locales/el/settings/general/config-changer.json @@ -0,0 +1,86 @@ +{ + "configSelect": { + "label": "Αλλαγή παραμέτρων", + "description": "{{configCount}} ρυθμίσεις είναι διαθέσιμες", + "loadingNew": "Φόρτωση της διαμόρφωσής σας...", + "pleaseWait": "Παρακαλώ περιμένετε μέχρι να φορτωθεί η νέα σας διαμόρφωση!" + }, + "modal": { + "copy": { + "title": "Επιλέξτε το όνομα της νέας σας διαμόρφωσης", + "form": { + "configName": { + "label": "Όνομα διαμόρφωσης", + "validation": { + "required": "Απαιτείται όνομα διαμόρφωσης", + "notUnique": "Αυτό το όνομα είναι ήδη σε χρήση" + }, + "placeholder": "Το νέο σας όνομα ρυθμίσεων" + }, + "submitButton": "Επιβεβαίωση" + }, + "events": { + "configSaved": { + "title": "Η διαμόρφωση αποθηκεύτηκε", + "message": "Διαμόρφωση αποθηκεύτηκε ως {{configName}}" + }, + "configCopied": { + "title": "Η ρύθμιση αντιγράφηκε", + "message": "Η διαμόρφωση αντιγράφηκε ως {{configName}}" + }, + "configNotCopied": { + "title": "Αδυναμία αντιγραφής αρχείου ρυθμίσεων", + "message": "Οι ρυθμίσεις σας δεν αντιγράφηκαν ως {{configName}}" + } + } + }, + "confirmDeletion": { + "title": "Επιβεβαιώστε τη διαγραφή της διαμόρφωσής σας", + "warningText": "Πρόκειται να διαγράψετε το '{{configName}}'", + "text": "Λάβετε υπόψη ότι η διαγραφή δεν είναι αναστρέψιμη και τα δεδομένα σας θα χαθούν οριστικά. Αφού κάνετε κλικ σε αυτό το κουμπί, το αρχείο θα διαγραφεί οριστικά από το δίσκο σας. Φροντίστε να δημιουργήσετε ένα επαρκές αντίγραφο ασφαλείας της διαμόρφωσής σας.", + "buttons": { + "confirm": "Ναι, διαγράψτε το '{{configName}}'" + } + } + }, + "buttons": { + "download": "Λήψη ρυθμίσεων", + "delete": { + "text": "Διαγραφή ρυθμίσεων", + "notifications": { + "deleted": { + "title": "Η ρύθμιση διαγράφηκε", + "message": "Η ρύθμιση διαγράφηκε" + }, + "deleteFailed": { + "title": "Η διαγραφή ρυθμίσεων απέτυχε", + "message": "Η διαγραφή ρυθμίσεων απέτυχε" + }, + "deleteFailedDefaultConfig": { + "title": "Η προεπιλεγμένη ρύθμιση παραμέτρων δεν μπορεί να διαγραφεί", + "message": "Η διαμόρφωση δεν διαγράφηκε από το σύστημα αρχείων" + } + } + }, + "saveCopy": "Αποθηκεύστε ένα αντίγραφο" + }, + "dropzone": { + "notifications": { + "invalidConfig": { + "title": "Αποτυχία φόρτωσης του αρχείου ρυθμίσεων", + "message": "Δεν μπόρεσε να φορτώσει τις ρυθμίσεις σας. Μη έγκυρη μορφή JSON." + }, + "loadedSuccessfully": { + "title": "Οι ρυθμίσεις {{configName}} φορτώθηκαν επιτυχώς" + } + }, + "accept": { + "title": "Φόρτωση ρυθμίσεων", + "text": "Σύρετε αρχεία εδώ για να ανεβάσετε μια διαμόρφωση ρυθμίσεων. Υποστήριξη μόνο για αρχεία JSON." + }, + "reject": { + "title": "Η μεταφόρτωση απορρίφθηκε", + "text": "Αυτή η μορφή αρχείου δεν υποστηρίζεται. Παρακαλούμε ανεβάζετε μόνο αρχεία JSON." + } + } +} diff --git a/public/locales/el/settings/general/internationalization.json b/public/locales/el/settings/general/internationalization.json new file mode 100644 index 000000000..88537bf87 --- /dev/null +++ b/public/locales/el/settings/general/internationalization.json @@ -0,0 +1,3 @@ +{ + "label": "Γλώσσα" +} \ No newline at end of file diff --git a/public/locales/el/settings/general/search-engine.json b/public/locales/el/settings/general/search-engine.json new file mode 100644 index 000000000..f3915a4d1 --- /dev/null +++ b/public/locales/el/settings/general/search-engine.json @@ -0,0 +1,19 @@ +{ + "title": "Μηχανή αναζήτησης", + "configurationName": "Διαμόρφωση μηχανής αναζήτησης", + "tips": { + "generalTip": "Υπάρχουν πολλά προθέματα που μπορείτε να χρησιμοποιήσετε! Προσθέτοντας αυτά μπροστά από το ερώτημά σας θα φιλτράρετε τα αποτελέσματα. !s (Web), !t (Torrents), !y (YouTube) και !m (Media).", + "placeholderTip": "%s μπορεί να χρησιμοποιηθεί ως placeholder για το ερώτημα." + }, + "customEngine": { + "title": "Προσαρμοσμένη μηχανή αναζήτησης", + "label": "Ερώτημα URL", + "placeholder": "Προσαρμοσμένο URL ερώτησης" + }, + "searchNewTab": { + "label": "Άνοιγμα αποτελεσμάτων αναζήτησης σε νέα καρτέλα" + }, + "searchEnabled": { + "label": "Ενεργοποιημένη αναζήτηση" + } +} diff --git a/public/locales/el/settings/general/theme-selector.json b/public/locales/el/settings/general/theme-selector.json new file mode 100644 index 000000000..ec09f40ed --- /dev/null +++ b/public/locales/el/settings/general/theme-selector.json @@ -0,0 +1,3 @@ +{ + "label": "Εναλλαγή στη λειτουργία {{theme}}" +} \ No newline at end of file diff --git a/public/locales/el/settings/general/widget-positions.json b/public/locales/el/settings/general/widget-positions.json new file mode 100644 index 000000000..8712f6f7e --- /dev/null +++ b/public/locales/el/settings/general/widget-positions.json @@ -0,0 +1,3 @@ +{ + "label": "Τοποθετήστε τα widgets στα αριστερά" +} diff --git a/public/locales/en/layout/modals/icon-picker.json b/public/locales/en/layout/modals/icon-picker.json new file mode 100644 index 000000000..84f17ce54 --- /dev/null +++ b/public/locales/en/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "Search something...", + "searchLimitationTitle": "Limited to 30 results", + "searchLimitationMessage": "Search results were limited to 30 because there were too many matches" + } +} \ No newline at end of file diff --git a/public/locales/es/layout/modals/icon-picker.json b/public/locales/es/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/es/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/fr/layout/modals/icon-picker.json b/public/locales/fr/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/fr/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/he/layout/modals/icon-picker.json b/public/locales/he/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/he/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/it/layout/modals/icon-picker.json b/public/locales/it/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/it/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/it/modules/torrents-status.json b/public/locales/it/modules/torrents-status.json index 4b20d52fe..7b5ea8e30 100644 --- a/public/locales/it/modules/torrents-status.json +++ b/public/locales/it/modules/torrents-status.json @@ -18,7 +18,7 @@ "card": { "footer": { "error": "Errore", - "lastUpdated": "Ultimo aggiornamento {{time}} ago" + "lastUpdated": "Ultimo aggiornamento {{time}} fa" }, "table": { "header": { @@ -30,7 +30,7 @@ "progress": "Avanzamento" }, "item": { - "text": "Gestito da {{appName}}, rapporto {{ratio}}" + "text": "Gestito da {{appName}}, {{ratio}} ratio" }, "body": { "nothingFound": "Nessun torrent trovato" @@ -61,10 +61,10 @@ "introductionPrefix": "Gestito da", "metrics": { "queuePosition": "Posizione in coda - {{position}}", - "progress": "Progressi - {{progress}}%", + "progress": "Progresso - {{progress}}%", "totalSelectedSize": "Totale - {{totalSize}}", "state": "Stato - {{state}}", - "ratio": "Rapporto -", + "ratio": "Ratio -", "completed": "Completato" } } diff --git a/public/locales/ja/layout/modals/icon-picker.json b/public/locales/ja/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/ja/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/ko/layout/modals/icon-picker.json b/public/locales/ko/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/ko/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/lol/layout/modals/icon-picker.json b/public/locales/lol/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/lol/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/nl/layout/modals/icon-picker.json b/public/locales/nl/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/nl/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/pl/layout/modals/icon-picker.json b/public/locales/pl/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/pl/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/pt/layout/modals/icon-picker.json b/public/locales/pt/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/pt/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/ru/layout/modals/icon-picker.json b/public/locales/ru/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/ru/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/sl/layout/modals/icon-picker.json b/public/locales/sl/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/sl/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/sv/layout/modals/about.json b/public/locales/sv/layout/modals/about.json index e35dbbfed..ffd97b0b8 100644 --- a/public/locales/sv/layout/modals/about.json +++ b/public/locales/sv/layout/modals/about.json @@ -3,5 +3,5 @@ "i18n": "Laddade namnområden för I18n-översättningar", "locales": "Konfigurerade I18n lokalspråk", "contact": "Har du problem eller frågor? Kontakta oss!", - "addToDashboard": "Lägg till i instrumentpanel" + "addToDashboard": "Lägg till på instrumentpanel" } diff --git a/public/locales/sv/layout/modals/add-app.json b/public/locales/sv/layout/modals/add-app.json index 923c67f0d..7f7c8b6e8 100644 --- a/public/locales/sv/layout/modals/add-app.json +++ b/public/locales/sv/layout/modals/add-app.json @@ -39,7 +39,7 @@ "appearance": { "icon": { "label": "Appikon", - "description": "Ikonen som kommer att visas på instrumentpanelen." + "description": "Ikon som kommer att visas på instrumentpanelen." } }, "integration": { diff --git a/public/locales/sv/layout/modals/change-position.json b/public/locales/sv/layout/modals/change-position.json index 5d1714b75..2a358c34d 100644 --- a/public/locales/sv/layout/modals/change-position.json +++ b/public/locales/sv/layout/modals/change-position.json @@ -1,8 +1,8 @@ { - "xPosition": "X axel position", + "xPosition": "Position X-axel", "width": "Bredd", "height": "Höjd", - "yPosition": "Y axel position", + "yPosition": "Position Y-axel", "zeroOrHigher": "0 eller högre", "betweenXandY": "Mellan {{min}} och {{max}}" } \ No newline at end of file diff --git a/public/locales/sv/layout/modals/icon-picker.json b/public/locales/sv/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/sv/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/sv/modules/dashdot.json b/public/locales/sv/modules/dashdot.json index 2ae7fa75a..d6a019e56 100644 --- a/public/locales/sv/modules/dashdot.json +++ b/public/locales/sv/modules/dashdot.json @@ -8,7 +8,7 @@ "label": "Flerkärnig CPU vy" }, "storageMultiView": { - "label": "Visning av flera lagrings enheter" + "label": "Visning av flera lagringsenheter" }, "useCompactView": { "label": "Använd kompakt vy" diff --git a/public/locales/uk/layout/modals/icon-picker.json b/public/locales/uk/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/uk/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/uk/modules/torrents-status.json b/public/locales/uk/modules/torrents-status.json index f6df37230..69e8e03bc 100644 --- a/public/locales/uk/modules/torrents-status.json +++ b/public/locales/uk/modules/torrents-status.json @@ -58,12 +58,12 @@ "title": "Завантаження..." }, "popover": { - "introductionPrefix": "Під керівництвом", + "introductionPrefix": "Керується", "metrics": { "queuePosition": "Позиція в черзі - {{position}}", "progress": "Прогрес - {{progress}}%.", "totalSelectedSize": "Всього - {{totalSize}}", - "state": "Держава - {{state}}", + "state": "Стан - {{state}}", "ratio": "Коефіцієнт -", "completed": "Завершено" } diff --git a/public/locales/vi/layout/modals/icon-picker.json b/public/locales/vi/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/vi/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/zh/layout/modals/icon-picker.json b/public/locales/zh/layout/modals/icon-picker.json new file mode 100644 index 000000000..349810cb9 --- /dev/null +++ b/public/locales/zh/layout/modals/icon-picker.json @@ -0,0 +1,7 @@ +{ + "iconPicker": { + "textInputPlaceholder": "", + "searchLimitationTitle": "", + "searchLimitationMessage": "" + } +} \ No newline at end of file diff --git a/public/locales/zh/settings/common.json b/public/locales/zh/settings/common.json index f8d028289..fffc224de 100644 --- a/public/locales/zh/settings/common.json +++ b/public/locales/zh/settings/common.json @@ -22,7 +22,7 @@ "enablelsidebar": "启用左边的侧边栏", "enablesearchbar": "启用搜索栏", "enabledocker": "启用docker集成", - "enableping": "启用平移功能", + "enableping": "启用Ping功能", "enablelsidebardesc": "可选的。只能用于应用程序和集成", "enablersidebardesc": "可选的。只能用于应用程序和集成" } diff --git a/src/components/About/AboutModal.tsx b/src/components/About/AboutModal.tsx index 5617731f4..7381caf40 100644 --- a/src/components/About/AboutModal.tsx +++ b/src/components/About/AboutModal.tsx @@ -13,6 +13,7 @@ import { Title, } from '@mantine/core'; import { + IconAnchor, IconBrandDiscord, IconBrandGithub, IconFile, @@ -27,9 +28,9 @@ import { InitOptions } from 'i18next'; import { i18n, Trans, useTranslation } from 'next-i18next'; import Image from 'next/image'; import { ReactNode } from 'react'; -import { CURRENT_VERSION } from '../../../data/constants'; import { useConfigContext } from '../../config/provider'; import { useConfigStore } from '../../config/store'; +import { usePackageAttributesStore } from '../../tools/client/zustands/usePackageAttributesStore'; import { usePrimaryGradient } from '../layout/useGradient'; import Credits from '../Settings/Common/Credits'; @@ -140,6 +141,7 @@ interface ExtendedInitOptions extends InitOptions { const useInformationTableItems = (newVersionAvailable?: string): InformationTableItem[] => { // TODO: Fix this to not request. Pass it as a prop. const colorGradiant = usePrimaryGradient(); + const { attributes } = usePackageAttributesStore(); const { configVersion } = useConfigContext(); const { configs } = useConfigStore(); @@ -198,7 +200,7 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl content: ( - {CURRENT_VERSION} + {attributes.packageVersion ?? 'Unknown'} {newVersionAvailable && ( @@ -226,13 +228,22 @@ const useInformationTableItems = (newVersionAvailable?: string): InformationTabl {newVersionAvailable} {' '} - is available ! Current version: {CURRENT_VERSION} + is available ! Current version: {attributes.packageVersion} )} ), }, + { + icon: , + label: 'Node environment', + content: ( + + {attributes.environment} + + ), + }, ...items, ]; diff --git a/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx b/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx index f8e704b51..db3d06c79 100644 --- a/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx +++ b/src/components/Dashboard/Modals/ChangePosition/ChangePositionModal.tsx @@ -45,7 +45,14 @@ export const ChangePositionModal = ({ const width = parseInt(form.values.width, 10); const height = parseInt(form.values.height, 10); - if (!form.values.x || !form.values.y || Number.isNaN(width) || Number.isNaN(height)) return; + if ( + form.values.x === null || + form.values.y === null || + Number.isNaN(width) || + Number.isNaN(height) + ) { + return; + } onSubmit(form.values.x, form.values.y, width, height); }; diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx index 8e46d0f51..d8795d25f 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/AppereanceTab.tsx @@ -1,5 +1,6 @@ -import { createStyles, Flex, Tabs, TextInput } from '@mantine/core'; +import { Autocomplete, createStyles, Flex, Tabs } from '@mantine/core'; import { UseFormReturnType } from '@mantine/form'; +import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'next-i18next'; import { AppType } from '../../../../../../types/app'; import { DebouncedAppIcon } from '../Shared/DebouncedAppIcon'; @@ -18,16 +19,21 @@ export const AppearanceTab = ({ }: AppearanceTabProps) => { const { t } = useTranslation('layout/modals/add-app'); const { classes } = useStyles(); + const { isLoading, error, data } = useQuery({ + queryKey: ['autocompleteLocale'], + queryFn: () => fetch('/api/getLocalImages').then((res) => res.json()), + }); return ( - } label={t('appearance.icon.label')} description={t('appearance.icon.description')} variant="default" + data={data?.files ?? []} withAsterisk required {...form.getInputProps('appearance.iconUrl')} diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx index 8ec13cbb3..ee853678a 100644 --- a/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx +++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/AppereanceTab/IconSelector/IconSelector.tsx @@ -31,7 +31,7 @@ interface IconSelectorProps { } export const IconSelector = ({ onChange, allowAppNamePropagation, form }: IconSelectorProps) => { - const { t } = useTranslation('layout/tools'); + const { t } = useTranslation('layout/modals/icon-picker'); const { data, isLoading } = useRepositoryIconsQuery({ url: 'https://api.github.com/repos/walkxcode/Dashboard-Icons/contents/png', diff --git a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx index edb689821..d746ae334 100644 --- a/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx +++ b/src/components/Dashboard/Modals/SelectElement/Components/Overview/AvailableElementsOverview.tsx @@ -50,7 +50,7 @@ export const AvailableElementTypes = ({ { id: uuidv4(), // Thank you ChatGPT ;) - position: previousConfig.wrappers.length + 1, + position: previousConfig.categories.length + 1, }, ], categories: [ diff --git a/src/components/Dashboard/Wrappers/Category/Category.tsx b/src/components/Dashboard/Wrappers/Category/Category.tsx index 9034a2e1a..f2fda5028 100644 --- a/src/components/Dashboard/Wrappers/Category/Category.tsx +++ b/src/components/Dashboard/Wrappers/Category/Category.tsx @@ -1,6 +1,8 @@ -import { Group, Title } from '@mantine/core'; +import { Accordion, Title } from '@mantine/core'; +import { useLocalStorage } from '@mantine/hooks'; +import { useConfigContext } from '../../../../config/provider'; import { CategoryType } from '../../../../types/category'; -import { HomarrCardWrapper } from '../../Tiles/HomarrCardWrapper'; +import { useCardStyles } from '../../../layout/useCardStyles'; import { useEditModeStore } from '../../Views/useEditModeStore'; import { useGridstack } from '../gridstack/use-gridstack'; import { WrapperContent } from '../WrapperContent'; @@ -13,20 +15,47 @@ interface DashboardCategoryProps { export const DashboardCategory = ({ category }: DashboardCategoryProps) => { const { refs, apps, widgets } = useGridstack('category', category.id); const isEditMode = useEditModeStore((x) => x.enabled); + const { config } = useConfigContext(); + const { classes: cardClasses } = useCardStyles(true); + + const categoryList = config?.categories.map((x) => x.name) ?? []; + const [toggledCategories, setToggledCategories] = useLocalStorage({ + key: `${config?.configProperties.name}-app-shelf-toggled`, + // This is a bit of a hack to toggle the categories on the first load, return a string[] of the categories + defaultValue: categoryList, + }); return ( - - - {category.name} - {isEditMode ? : null} - -
- -
-
+ { + // Cancel if edit mode is on + if (isEditMode) return; + setToggledCategories([...state]); + }} + > + + }> + {category.name} + + +
+ +
+
+
+
); }; diff --git a/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx b/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx index 24c89bc8f..70b1ed85c 100644 --- a/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx +++ b/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx @@ -22,7 +22,7 @@ export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => { useCategoryActions(configName, category); return ( - + diff --git a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx index 777bdabf3..6be7e99a4 100644 --- a/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx +++ b/src/components/Dashboard/Wrappers/Category/useCategoryActions.tsx @@ -1,8 +1,10 @@ import { v4 as uuidv4 } from 'uuid'; import { useConfigStore } from '../../../../config/store'; import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions'; +import { AppType } from '../../../../types/app'; import { CategoryType } from '../../../../types/category'; import { WrapperType } from '../../../../types/wrapper'; +import { IWidget } from '../../../../widgets/widgets'; import { CategoryEditModalInnerProps } from './CategoryEditModal'; export const useCategoryActions = (configName: string | undefined, category: CategoryType) => { @@ -185,29 +187,75 @@ export const useCategoryActions = (configName: string | undefined, category: Cat const currentItem = previous.categories.find((x) => x.id === category.id); if (!currentItem) return previous; // Find the main wrapper - const mainWrapper = previous.wrappers.find((x) => x.position === 1); + const mainWrapper = previous.wrappers.find((x) => x.position === 0); + const mainWrapperId = mainWrapper?.id ?? 'default'; - // Check that the app has an area.type or "category" and that the area.id is the current category - const appsToMove = previous.apps.filter( - (x) => x.area && x.area.type === 'category' && x.area.properties.id === currentItem.id - ); - appsToMove.forEach((x) => { - // eslint-disable-next-line no-param-reassign - x.area = { type: 'wrapper', properties: { id: mainWrapper?.id ?? 'default' } }; - }); + const isAppAffectedFilter = (app: AppType): boolean => { + if (!app.area) { + return false; + } - const widgetsToMove = previous.widgets.filter( - (x) => x.area && x.area.type === 'category' && x.area.properties.id === currentItem.id - ); + if (app.area.type !== 'category') { + return false; + } - widgetsToMove.forEach((x) => { - // eslint-disable-next-line no-param-reassign - x.area = { type: 'wrapper', properties: { id: mainWrapper?.id ?? 'default' } }; - }); + if (app.area.properties.id === mainWrapperId) { + return false; + } + + return app.area.properties.id === currentItem.id; + }; + + const isWidgetAffectedFilter = (widget: IWidget): boolean => { + if (!widget.area) { + return false; + } + + if (widget.area.type !== 'category') { + return false; + } + + if (widget.area.properties.id === mainWrapperId) { + return false; + } + + return widget.area.properties.id === currentItem.id; + }; return { ...previous, - apps: previous.apps, + apps: [ + ...previous.apps.filter((x) => !isAppAffectedFilter(x)), + ...previous.apps + .filter((x) => isAppAffectedFilter(x)) + .map((app): AppType => ({ + ...app, + area: { + ...app.area, + type: 'wrapper', + properties: { + ...app.area.properties, + id: mainWrapperId, + }, + }, + })), + ], + widgets: [ + ...previous.widgets.filter((widget) => !isWidgetAffectedFilter(widget)), + ...previous.widgets + .filter((widget) => isWidgetAffectedFilter(widget)) + .map((widget): IWidget => ({ + ...widget, + area: { + ...widget.area, + type: 'wrapper', + properties: { + ...widget.area.properties, + id: mainWrapperId, + }, + }, + })), + ], categories: previous.categories.filter((x) => x.id !== category.id), wrappers: previous.wrappers.filter((x) => x.position !== currentItem.position), }; diff --git a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx index 4bfdc89a6..bee5be707 100644 --- a/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx +++ b/src/components/layout/header/Actions/ToggleEditMode/ToggleEditMode.tsx @@ -4,6 +4,7 @@ import { ActionIcon, Button, Group, Text, Title, Tooltip } from '@mantine/core'; import { IconEditCircle, IconEditCircleOff } from '@tabler/icons'; import { getCookie } from 'cookies-next'; import { Trans, useTranslation } from 'next-i18next'; +import { useHotkeys } from '@mantine/hooks'; import { hideNotification, showNotification } from '@mantine/notifications'; import { useConfigContext } from '../../../../../config/provider'; import { useScreenSmallerThan } from '../../../../../hooks/useScreenSmallerThan'; @@ -23,6 +24,8 @@ export const ToggleEditModeAction = () => { const { config } = useConfigContext(); const { classes } = useCardStyles(true); + useHotkeys([['ctrl+E', toggleEditMode]]); + const toggleButtonClicked = () => { toggleEditMode(); if (enabled || config === undefined || config?.schemaVersion === undefined) { diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index c70fa7128..ec7a9f33d 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -1,10 +1,10 @@ import { Box, createStyles, Group, Header as MantineHeader, Indicator } from '@mantine/core'; import { useEffect, useState } from 'react'; -import { CURRENT_VERSION, REPO_URL } from '../../../../data/constants'; -import { useConfigContext } from '../../../config/provider'; +import { REPO_URL } from '../../../../data/constants'; +import DockerMenuButton from '../../../modules/Docker/DockerModule'; +import { usePackageAttributesStore } from '../../../tools/client/zustands/usePackageAttributesStore'; import { Logo } from '../Logo'; import { useCardStyles } from '../useCardStyles'; -import DockerMenuButton from '../../../modules/Docker/DockerModule'; import { ToggleEditModeAction } from './Actions/ToggleEditMode/ToggleEditMode'; import { Search } from './Search'; import { SettingsMenu } from './SettingsMenu'; @@ -14,23 +14,22 @@ export const HeaderHeight = 64; export function Header(props: any) { const { classes } = useStyles(); const { classes: cardClasses } = useCardStyles(false); - - const { config } = useConfigContext(); + const { attributes } = usePackageAttributesStore(); const [newVersionAvailable, setNewVersionAvailable] = useState(''); useEffect(() => { // Fetch Data here when component first mounted fetch(`https://api.github.com/repos/${REPO_URL}/releases/latest`).then((res) => { res.json().then((data) => { - if (data.tag_name > CURRENT_VERSION) { + if (data.tag_name > `v${attributes.packageVersion}`) { setNewVersionAvailable(data.tag_name); } }); }); - }, [CURRENT_VERSION]); + }, []); return ( - + diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx index fafc86376..ba8d799d0 100644 --- a/src/components/layout/header/Search.tsx +++ b/src/components/layout/header/Search.tsx @@ -13,12 +13,14 @@ import { import { useDebouncedValue, useHotkeys } from '@mantine/hooks'; import { showNotification } from '@mantine/notifications'; import { IconBrandYoutube, IconDownload, IconMovie, IconSearch } from '@tabler/icons'; +import { useQuery } from '@tanstack/react-query'; import axios from 'axios'; import { useTranslation } from 'next-i18next'; import React, { forwardRef, useEffect, useRef, useState } from 'react'; import { useConfigContext } from '../../../config/provider'; import { OverseerrMediaDisplay } from '../../../modules/common'; import { IModule } from '../../../modules/ModuleTypes'; +import { ConfigType } from '../../../types/config'; import { searchUrls } from '../../Settings/Common/SearchEngine/SearchEngineSelector'; import Tip from '../Tip'; import { useCardStyles } from '../useCardStyles'; @@ -54,8 +56,8 @@ export function Search() { const { t } = useTranslation('modules/search'); const { config } = useConfigContext(); const [searchQuery, setSearchQuery] = useState(''); - const [debounced, cancel] = useDebouncedValue(searchQuery, 250); - const { classes: cardClasses } = useCardStyles(false); + const [debounced] = useDebouncedValue(searchQuery, 250); + const { classes: cardClasses } = useCardStyles(true); const isOverseerrEnabled = config?.apps.some( (x) => x.integration.type === 'overseerr' || x.integration.type === 'jellyseerr' @@ -136,30 +138,40 @@ export function Search() { const textInput = useRef(null); useHotkeys([['mod+K', () => textInput.current?.focus()]]); const { classes } = useStyles(); - const openInNewTab = config?.settings.common.searchEngine.properties.openInNewTab - ? '_blank' - : '_self'; - const [OverseerrResults, setOverseerrResults] = useState([]); + const openTarget = getOpenTarget(config); const [opened, setOpened] = useState(false); - useEffect(() => { - if (debounced !== '' && selectedSearchEngine.value === 'overseerr' && searchQuery.length > 3) { - axios.get(`/api/modules/overseerr?query=${searchQuery}`).then((res) => { - setOverseerrResults(res.data.results ?? []); - }); + const { + data: OverseerrResults, + isLoading, + error, + } = useQuery( + ['overseerr', debounced], + async () => { + if (debounced !== '' && selectedSearchEngine.value === 'overseerr' && debounced.length > 3) { + const res = await axios.get(`/api/modules/overseerr?query=${debounced}`); + return res.data.results ?? []; + } + return []; + }, + { + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchInterval: false, } - }, [debounced]); + ); const isModuleEnabled = config?.settings.customization.layout.enabledSearchbar; if (!isModuleEnabled) { return null; } + //TODO: Fix the bug where clicking anything inside the Modal to ask for a movie // will close it (Because it closes the underlying Popover) return ( 0 && opened && searchQuery.length > 3} + opened={OverseerrResults && OverseerrResults.length > 0 && opened && searchQuery.length > 3} position="bottom" withinPortal shadow="md" @@ -182,7 +194,7 @@ export function Search() { setOpened(false); if (item.url) { setSearchQuery(''); - window.open(item.openedUrl ? item.openedUrl : item.url, openInNewTab); + window.open(item.openedUrl ? item.openedUrl : item.url, openTarget); } }} // Replace %s if it is in selectedSearchEngine.url with searchQuery, otherwise append searchQuery at the end of it @@ -193,9 +205,9 @@ export function Search() { autocompleteData.length === 0 ) { if (selectedSearchEngine.url.includes('%s')) { - window.open(selectedSearchEngine.url.replace('%s', searchQuery), openInNewTab); + window.open(selectedSearchEngine.url.replace('%s', searchQuery), openTarget); } else { - window.open(selectedSearchEngine.url + searchQuery, openInNewTab); + window.open(selectedSearchEngine.url + searchQuery, openTarget); } } }} @@ -207,16 +219,17 @@ export function Search() { /> -
- - {OverseerrResults.slice(0, 5).map((result, index) => ( + + {OverseerrResults && + OverseerrResults.slice(0, 4).map((result: any, index: number) => ( - {index < OverseerrResults.length - 1 && } + {index < OverseerrResults.length - 1 && index < 3 && ( + + )} ))} - -
+
@@ -287,3 +300,11 @@ export function Search() { }); } } + +const getOpenTarget = (config: ConfigType | undefined): '_blank' | '_self' => { + if (!config || config.settings.common.searchEngine.properties.openInNewTab === undefined) { + return '_blank'; + } + + return config.settings.common.searchEngine.properties.openInNewTab ? '_blank' : '_self'; +}; diff --git a/src/hooks/widgets/download-speed/useGetNetworkSpeed.tsx b/src/hooks/widgets/download-speed/useGetNetworkSpeed.tsx new file mode 100644 index 000000000..965607e1c --- /dev/null +++ b/src/hooks/widgets/download-speed/useGetNetworkSpeed.tsx @@ -0,0 +1,11 @@ +import { useQuery } from '@tanstack/react-query'; +import { NormalizedDownloadQueueResponse } from '../../../types/api/downloads/queue/NormalizedDownloadQueueResponse'; + +export const useGetDownloadClientsQueue = () => useQuery({ + queryKey: ['network-speed'], + queryFn: async (): Promise => { + const response = await fetch('/api/modules/downloads'); + return response.json(); + }, + refetchInterval: 3000, +}); diff --git a/src/hooks/widgets/torrents/useGetTorrentData.tsx b/src/hooks/widgets/torrents/useGetTorrentData.tsx deleted file mode 100644 index 8027462e3..000000000 --- a/src/hooks/widgets/torrents/useGetTorrentData.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Query, useQuery } from '@tanstack/react-query'; -import axios from 'axios'; -import { NormalizedTorrentListResponse } from '../../../types/api/NormalizedTorrentListResponse'; - -interface TorrentsDataRequestParams { - appId: string; - refreshInterval: number; -} - -export const useGetTorrentData = (params: TorrentsDataRequestParams) => - useQuery({ - queryKey: ['torrentsData', params.appId], - queryFn: fetchData, - refetchOnWindowFocus: true, - refetchInterval(_: any, query: Query) { - if (query.state.fetchFailureCount < 3) { - return params.refreshInterval; - } - return false; - }, - enabled: !!params.appId, - }); - -const fetchData = async (): Promise => { - const response = await axios.post('/api/modules/torrents'); - return response.data as NormalizedTorrentListResponse; -}; diff --git a/src/modules/Docker/DockerModule.tsx b/src/modules/Docker/DockerModule.tsx index cfffbed00..684178dd9 100644 --- a/src/modules/Docker/DockerModule.tsx +++ b/src/modules/Docker/DockerModule.tsx @@ -1,4 +1,5 @@ import { ActionIcon, Drawer, Text, Tooltip } from '@mantine/core'; +import { useHotkeys } from '@mantine/hooks'; import { showNotification } from '@mantine/notifications'; import { IconBrandDocker, IconX } from '@tabler/icons'; import axios from 'axios'; @@ -17,6 +18,7 @@ export default function DockerMenuButton(props: any) { const [selection, setSelection] = useState([]); const { config } = useConfigContext(); const { classes } = useCardStyles(true); + useHotkeys([['mod+B', () => setOpened(!opened)]]); const dockerEnabled = config?.settings.customization.layout.enabledDocker || false; @@ -60,6 +62,7 @@ export default function DockerMenuButton(props: any) { <> setOpened(false)} padding="xl" position="right" diff --git a/src/modules/Docker/DockerTable.tsx b/src/modules/Docker/DockerTable.tsx index 2925e3990..70f597d23 100644 --- a/src/modules/Docker/DockerTable.tsx +++ b/src/modules/Docker/DockerTable.tsx @@ -7,6 +7,7 @@ import { ScrollArea, TextInput, useMantineTheme, + Text, } from '@mantine/core'; import { useElementSize } from '@mantine/hooks'; import { IconSearch } from '@tabler/icons'; @@ -78,8 +79,16 @@ export default function DockerTable({ transitionDuration={0} /> - {element.Names[0].replace('/', '')} - {width > MIN_WIDTH_MOBILE && {element.Image}} + + + {element.Names[0].replace('/', '')} + + + {width > MIN_WIDTH_MOBILE && ( + + {element.Image} + + )} {width > MIN_WIDTH_MOBILE && ( @@ -111,12 +120,13 @@ export default function DockerTable({ }); return ( - + } value={search} + autoFocus onChange={handleSearchChange} /> diff --git a/src/modules/common/MediaDisplay.tsx b/src/modules/common/MediaDisplay.tsx index 7c3631b18..7fea14508 100644 --- a/src/modules/common/MediaDisplay.tsx +++ b/src/modules/common/MediaDisplay.tsx @@ -180,7 +180,7 @@ export function MediaDisplay({ media }: { media: IMedia }) { const { t } = useTranslation('modules/common-media-cards'); return ( - + @@ -223,7 +223,7 @@ export function MediaDisplay({ media }: { media: IMedia }) { {media.overview} - + {media.plexUrl && ( - {data.torrents.map((concatenatedTorrentList) => { - const app = config?.apps.find((x) => x.id === concatenatedTorrentList.appId); - return concatenatedTorrentList.torrents - .filter(filter) - .map((item: NormalizedTorrent, index: number) => ( - - )); - })} + {torrents.map((torrent, index) => ( + + ))}
- {!data.allSuccess && ( + {data.apps.some((x) => !x.success) && ( {t('card.footer.error')} diff --git a/src/widgets/torrentNetworkTraffic/TorrentNetworkTrafficTile.tsx b/src/widgets/torrentNetworkTraffic/TorrentNetworkTrafficTile.tsx deleted file mode 100644 index 3b494d130..000000000 --- a/src/widgets/torrentNetworkTraffic/TorrentNetworkTrafficTile.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { NormalizedTorrent } from '@ctrl/shared-torrent'; -import { Text, Title, Group, useMantineTheme, Box, Card, ColorSwatch, Stack } from '@mantine/core'; -import { useListState } from '@mantine/hooks'; -import { showNotification } from '@mantine/notifications'; -import { linearGradientDef } from '@nivo/core'; -import { Datum, ResponsiveLine } from '@nivo/line'; -import { IconArrowsUpDown } from '@tabler/icons'; -import axios from 'axios'; -import { useTranslation } from 'next-i18next'; -import { useEffect, useState } from 'react'; -import { useConfigContext } from '../../config/provider'; -import { useSetSafeInterval } from '../../hooks/useSetSafeInterval'; -import { humanFileSize } from '../../tools/humanFileSize'; -import { NormalizedTorrentListResponse } from '../../types/api/NormalizedTorrentListResponse'; -import { defineWidget } from '../helper'; -import { IWidget } from '../widgets'; - -const definition = defineWidget({ - id: 'dlspeed', - icon: IconArrowsUpDown, - options: {}, - - gridstack: { - minWidth: 2, - minHeight: 2, - maxWidth: 12, - maxHeight: 6, - }, - component: TorrentNetworkTrafficTile, -}); - -export type ITorrentNetworkTraffic = IWidget; - -interface TorrentNetworkTrafficTileProps { - widget: ITorrentNetworkTraffic; -} - -function TorrentNetworkTrafficTile({ widget }: TorrentNetworkTrafficTileProps) { - const { t } = useTranslation(`modules/${definition.id}`); - const { colors } = useMantineTheme(); - const setSafeInterval = useSetSafeInterval(); - const { configVersion, config } = useConfigContext(); - - const [torrentHistory, torrentHistoryHandlers] = useListState([]); - const [torrents, setTorrents] = useState([]); - - const downloadServices = - config?.apps.filter( - (app) => - app.integration.type === 'qBittorrent' || - app.integration.type === 'transmission' || - app.integration.type === 'deluge' - ) ?? []; - const totalDownloadSpeed = torrents.reduce((acc, torrent) => acc + torrent.downloadSpeed, 0); - const totalUploadSpeed = torrents.reduce((acc, torrent) => acc + torrent.uploadSpeed, 0); - - useEffect(() => { - if (downloadServices.length === 0) return; - const interval = setSafeInterval(() => { - // Send one request with each download service inside - axios - .post('/api/modules/torrents') - .then((response) => { - const responseData: NormalizedTorrentListResponse = response.data; - setTorrents(responseData.torrents.flatMap((x) => x.torrents)); - }) - .catch((error) => { - if (error.status === 401) return; - setTorrents([]); - // eslint-disable-next-line no-console - console.error('Error while fetching torrents', error.response.data); - showNotification({ - title: 'Torrent speed module failed to fetch torrents', - autoClose: 1000, - disallowClose: true, - id: 'fail-torrent-speed-module', - color: 'red', - message: - 'Error fetching torrents, please check your config for any potential errors, check the console for more info', - }); - clearInterval(interval); - }); - }, 1000); - }, [configVersion]); - - useEffect(() => { - torrentHistoryHandlers.append({ - x: Date.now(), - down: totalDownloadSpeed, - up: totalUploadSpeed, - }); - }, [totalDownloadSpeed, totalUploadSpeed]); - - const history = torrentHistory.slice(-10); - const chartDataUp = history.map((load, i) => ({ - x: load.x, - y: load.up, - })) as Datum[]; - const chartDataDown = history.map((load, i) => ({ - x: load.x, - y: load.down, - })) as Datum[]; - - return ( - - {t('card.lineChart.title')} - - - - - {t('card.lineChart.totalDownload', { download: humanFileSize(totalDownloadSpeed) })} - - - - - - {t('card.lineChart.totalUpload', { upload: humanFileSize(totalUploadSpeed) })} - - - - - { - const Download = slice.points[0].data.y as number; - const Upload = slice.points[1].data.y as number; - // Get the number of seconds since the last update. - const seconds = (Date.now() - (slice.points[0].data.x as number)) / 1000; - // Round to the nearest second. - const roundedSeconds = Math.round(seconds); - return ( - - {t('card.lineChart.timeSpan', { seconds: roundedSeconds })} - - - - - - {t('card.lineChart.download', { download: humanFileSize(Download) })} - - - - - - {t('card.lineChart.upload', { upload: humanFileSize(Upload) })} - - - - - - ); - }} - data={[ - { - id: 'downloads', - data: chartDataUp, - }, - { - id: 'uploads', - data: chartDataDown, - }, - ]} - curve="monotoneX" - yFormat=" >-.2f" - axisTop={null} - axisRight={null} - enablePoints={false} - animate={false} - enableGridX={false} - enableGridY={false} - enableArea - defs={[ - linearGradientDef('gradientA', [ - { offset: 0, color: 'inherit' }, - { offset: 100, color: 'inherit', opacity: 0 }, - ]), - ]} - fill={[{ match: '*', id: 'gradientA' }]} - colors={[colors.blue[5], colors.green[5]]} - /> - - - ); -} - -export default definition; - -interface TorrentHistory { - x: number; - up: number; - down: number; -} diff --git a/src/widgets/useNet/UsenetHistoryList.tsx b/src/widgets/useNet/UsenetHistoryList.tsx index 28ee10ccf..2578fdec6 100644 --- a/src/widgets/useNet/UsenetHistoryList.tsx +++ b/src/widgets/useNet/UsenetHistoryList.tsx @@ -20,7 +20,7 @@ import { useTranslation } from 'next-i18next'; import { FunctionComponent, useState } from 'react'; import { useGetUsenetHistory } from '../../hooks/widgets/dashDot/api'; import { humanFileSize } from '../../tools/humanFileSize'; -import { parseDuration } from '../../tools/parseDuration'; +import { parseDuration } from '../../tools/client/parseDuration'; dayjs.extend(duration); diff --git a/yarn.lock b/yarn.lock index 398c6e544..399583df6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1971,6 +1971,15 @@ __metadata: languageName: node linkType: hard +"@tanstack/match-sorter-utils@npm:^8.7.0": + version: 8.7.6 + resolution: "@tanstack/match-sorter-utils@npm:8.7.6" + dependencies: + remove-accents: 0.4.2 + checksum: 3f3dda277e6e55ca1224a28b38a2deb3ac912c2f2f5263a32fa0d9126c6b6d05feb475539729fd248f1eb88b612109db90b847ec8fdfc05d0f4073c900a2d3f6 + languageName: node + linkType: hard + "@tanstack/query-core@npm:4.19.1": version: 4.19.1 resolution: "@tanstack/query-core@npm:4.19.1" @@ -1978,6 +1987,21 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-query-devtools@npm:^4.24.4": + version: 4.24.4 + resolution: "@tanstack/react-query-devtools@npm:4.24.4" + dependencies: + "@tanstack/match-sorter-utils": ^8.7.0 + superjson: ^1.10.0 + use-sync-external-store: ^1.2.0 + peerDependencies: + "@tanstack/react-query": 4.24.4 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 8b8f1ae8e55f016f25b383baae0000f0b608ec0327ee4eccb0a7b3b1c596b12f68c848e429be84c8a6039bd0a7d5bd36a7232fd7818868f1a3ae3d0462898e26 + languageName: node + linkType: hard + "@tanstack/react-query@npm:^4.2.1": version: 4.19.1 resolution: "@tanstack/react-query@npm:4.19.1" @@ -3271,6 +3295,15 @@ __metadata: languageName: node linkType: hard +"copy-anything@npm:^3.0.2": + version: 3.0.3 + resolution: "copy-anything@npm:3.0.3" + dependencies: + is-what: ^4.1.8 + checksum: d456dc5ec98dee7c7cf87d809eac30dc2ac942acd4cf970fab394e280ceb6dd7a8a7a5a44fcbcc50e0206658de3cc20b92863562f5797930bb2619f164f4c182 + languageName: node + linkType: hard + "core-js-pure@npm:^3.25.1": version: 3.26.1 resolution: "core-js-pure@npm:3.26.1" @@ -4947,6 +4980,7 @@ __metadata: "@nivo/line": ^0.79.1 "@tabler/icons": ^1.106.0 "@tanstack/react-query": ^4.2.1 + "@tanstack/react-query-devtools": ^4.24.4 "@types/dockerode": ^3.3.9 "@types/node": 17.0.1 "@types/ping": ^0.4.1 @@ -5478,6 +5512,13 @@ __metadata: languageName: node linkType: hard +"is-what@npm:^4.1.8": + version: 4.1.8 + resolution: "is-what@npm:4.1.8" + checksum: b9bec3acff102d14ad467f4c74c9886af310fa160e07a63292c8c181e6768c7c4c1054644e13d67185b963644e4a513bce8c6b8ce3d3ca6f9488a69fccad5f97 + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -7440,6 +7481,13 @@ __metadata: languageName: node linkType: hard +"remove-accents@npm:0.4.2": + version: 0.4.2 + resolution: "remove-accents@npm:0.4.2" + checksum: 84a6988555dea24115e2d1954db99509588d43fe55a1590f0b5894802776f7b488b3151c37ceb9e4f4b646f26b80b7325dcea2fae58bc3865df146e1fa606711 + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -8076,6 +8124,15 @@ __metadata: languageName: node linkType: hard +"superjson@npm:^1.10.0": + version: 1.12.2 + resolution: "superjson@npm:1.12.2" + dependencies: + copy-anything: ^3.0.2 + checksum: cf7735e172811ed87476a7c2f1bb0e83725a0e3c2d7a50a71303a973060b3c710288767fb767a7a7eee8e5625d3ccaee1176a93e27f43841627512c15c4cdf84 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0"