diff --git a/package-lock.json b/package-lock.json
index ef634c0b..7f080ea9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"apexcharts": "^3.35.0",
"axios": "^0.26.1",
"dayjs": "^1.10.4",
+ "jsonschema": "^1.4.1",
"lodash": "^4.17.21",
"quick-score": "^0.2.0",
"typeface-roboto": "^1.1.13",
@@ -5382,6 +5383,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/jsonschema": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
+ "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
diff --git a/package.json b/package.json
index 2dd24fbc..1fd9fb5d 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"apexcharts": "^3.35.0",
"axios": "^0.26.1",
"dayjs": "^1.10.4",
+ "jsonschema": "^1.4.1",
"lodash": "^4.17.21",
"quick-score": "^0.2.0",
"typeface-roboto": "^1.1.13",
diff --git a/src/components/Settings/Tabs/VueTorrent/VGeneral.vue b/src/components/Settings/Tabs/VueTorrent/VGeneral.vue
index 8e28d42e..fa9a7c9a 100644
--- a/src/components/Settings/Tabs/VueTorrent/VGeneral.vue
+++ b/src/components/Settings/Tabs/VueTorrent/VGeneral.vue
@@ -207,8 +207,15 @@
+
+
+
+
+ {{ $t('modals.settings.pageVueTorrent.pageGeneral.importSettings') }}
+ {{ $t('modals.settings.pageVueTorrent.pageGeneral.exportSettings') }}
+
- {{ $t('modals.settings.pageVueTorrent.pageGeneral.resetSettings') }}
+ {{ $t('modals.settings.pageVueTorrent.pageGeneral.resetSettings') }}
@@ -217,16 +224,21 @@
import { mapState, mapGetters } from 'vuex'
import { Qbit } from '@/services/qbit'
import { LOCALES } from '@/lang/locales'
-import {TitleOptions} from "@/enums/vuetorrent";
+import { General } from '@/mixins'
+import { TitleOptions } from '@/enums/vuetorrent'
+import { validate } from 'jsonschema'
+import { StoreState } from '@/schemas'
export default {
name: 'VueTorrent-General',
+ mixins: [General],
data() {
return {
languages: LOCALES,
paginationSizes: [5, 15, 30, 50],
titleOptions: [TitleOptions.DEFAULT, TitleOptions.GLOBAL_SPEED, TitleOptions.FIRST_TORRENT_STATUS],
- Qbitversion: 0
+ Qbitversion: 0,
+ settingsField: ''
}
},
computed: {
@@ -243,6 +255,32 @@ export default {
async fetchQbitVersion() {
this.Qbitversion = await Qbit.getAppVersion()
},
+ importSettings() {
+ let isValidJson = true
+ let userState
+ try {
+ userState = JSON.parse(this.settingsField)
+ let validatorResult = validate(userState, StoreState)
+ console.log(userState)
+ console.log(validatorResult)
+ if (!validatorResult.valid)
+ isValidJson = false
+ } catch (e) {
+ console.log(e)
+ isValidJson = false
+ }
+
+ if (!isValidJson) {
+ this.$toast.error(this.$t('toast.invalidJson').toString())
+ return
+ }
+
+ window.localStorage.setItem('vuetorrent', this.settingsField)
+ location.reload()
+ },
+ exportSettings() {
+ this.settingsField = window.localStorage.getItem('vuetorrent') ?? ''
+ },
resetSettings() {
window.localStorage.clear()
location.reload()
@@ -261,4 +299,11 @@ export default {
@import 'src/styles/styles.scss';
@include reverse-switch;
}
+
+.justify-content-evenly {
+ justify-content: space-evenly;
+}
+.remove-after::after {
+ content: unset;
+}
diff --git a/src/lang/en.json b/src/lang/en.json
index 9a008177..0ea56dc1 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -24,7 +24,7 @@
"inactive": "Inactive",
"stalled": "Stalled",
"errored": "Errored",
- "login": "Login in",
+ "login": "Log in",
"logout": "Log out",
"shutdownApp": "Shutdown Application",
"download": "Download",
@@ -50,6 +50,7 @@
"yes": "yes",
"no": "no",
"filter": "Filter",
+ "close": "Close",
"dashboard": {
"tooltips": {
"toggleSearch": "Toggle Search Filter",
@@ -190,6 +191,8 @@
"openSideBarOnStart": "Open Side Bar on launch",
"currentVersion": "Current Version:",
"qbittorrentVersion": "QBittorrent Version:",
+ "importSettings": "Import Settings",
+ "exportSettings": "Export Settings",
"resetSettings": "Reset Settings"
},
"pageDashboard": {
@@ -505,7 +508,8 @@
"pasteSuccess": "Text pasted!",
"pasteNotSupported": "Unable to paste, context isn't secured",
"shutdownSuccess": "qBittorrent was shutdown successfully!",
- "shutdownError": "Unable to shutdown app. Make sure qBittorrent is running!"
+ "shutdownError": "Unable to shutdown app. Make sure qBittorrent is running!",
+ "invalidJson": "Invalid JSON!"
},
"rightClick": {
"resume": "resume",
diff --git a/src/lang/fr.json b/src/lang/fr.json
index 228d9659..2d84ac7d 100644
--- a/src/lang/fr.json
+++ b/src/lang/fr.json
@@ -47,6 +47,7 @@
"yes": "oui",
"no": "non",
"filter": "Filtre",
+ "close": "Fermer",
"dashboard": {
"tooltips": {
"toggleSearch": "Rechercher un torrent",
@@ -185,7 +186,10 @@
"dateFormat": "Format de date",
"openSideBarOnStart": "Ouvrir la barre latérale au lancement",
"currentVersion": "Version actuelle:",
- "qbittorrentVersion": "Version de QBittorrent:"
+ "qbittorrentVersion": "Version de QBittorrent:",
+ "importSettings": "Importer la configuration",
+ "exportSettings": "Exporter la configuration",
+ "resetSettings": "Réinitialiser la configuration"
},
"pageDashboard": {
"busyTorrentTip": "Propriétés à afficher pour les torrents occupés",
@@ -491,6 +495,7 @@
"copySuccess": "Texte copié!",
"copyNotSupported": "Impossible de copier, le contexte n'est pas sécurisé",
"pasteSuccess": "Texte collé!",
+ "invalidJson": "JSON invalide!"
"pasteNotSupported": "Impossible de coller, le contexte n'est pas sécurisé"
},
"rightClick": {
diff --git a/src/schemas/StoreState.json b/src/schemas/StoreState.json
new file mode 100644
index 00000000..befa5c41
--- /dev/null
+++ b/src/schemas/StoreState.json
@@ -0,0 +1,170 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "sort_options": {
+ "type": "object",
+ "properties": {
+ "isCustomSortEnabled": {
+ "type": "boolean"
+ },
+ "sort": {
+ "type": "string"
+ },
+ "reverse": {
+ "type": "boolean"
+ },
+ "filter": {
+ "type": ["string", "null"]
+ },
+ "category": {
+ "type": ["string", "null"]
+ },
+ "tag": {
+ "type": ["string", "null"]
+ },
+ "tracker": {
+ "type": ["string", "null"]
+ }
+ },
+ "required": [
+ "isCustomSortEnabled",
+ "sort",
+ "reverse",
+ "filter",
+ "category",
+ "tag",
+ "tracker"
+ ]
+ },
+ "webuiSettings": {
+ "type": "object",
+ "properties": {
+ "lang": {
+ "type": "string"
+ },
+ "darkTheme": {
+ "type": "boolean"
+ },
+ "showFreeSpace": {
+ "type": "boolean"
+ },
+ "showSpeedGraph": {
+ "type": "boolean"
+ },
+ "showSessionStat": {
+ "type": "boolean"
+ },
+ "showAlltimeStat": {
+ "type": "boolean"
+ },
+ "showCurrentSpeed": {
+ "type": "boolean"
+ },
+ "showTrackerFilter": {
+ "type": "boolean"
+ },
+ "showSpeedInTitle": {
+ "type": "boolean"
+ },
+ "deleteWithFiles": {
+ "type": "boolean"
+ },
+ "title": {
+ "type": "string"
+ },
+ "rightDrawer": {
+ "type": "boolean"
+ },
+ "topPagination": {
+ "type": "boolean"
+ },
+ "paginationSize": {
+ "type": "integer"
+ },
+ "dateFormat": {
+ "type": "string"
+ },
+ "openSideBarOnStart": {
+ "type": "boolean"
+ },
+ "busyTorrentProperties": {
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "label": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "active",
+ "label"
+ ]
+ }
+ ]
+ },
+ "doneTorrentProperties": {
+ "type": "array",
+ "items": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "label": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "active",
+ "label"
+ ]
+ }
+ ]
+ }
+ },
+ "required": [
+ "lang",
+ "darkTheme",
+ "showFreeSpace",
+ "showSpeedGraph",
+ "showSessionStat",
+ "showAlltimeStat",
+ "showCurrentSpeed",
+ "showTrackerFilter",
+ "showSpeedInTitle",
+ "deleteWithFiles",
+ "title",
+ "rightDrawer",
+ "topPagination",
+ "paginationSize",
+ "dateFormat",
+ "openSideBarOnStart",
+ "busyTorrentProperties",
+ "doneTorrentProperties"
+ ]
+ },
+ "authenticated": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "sort_options",
+ "webuiSettings",
+ "authenticated"
+ ]
+}
diff --git a/src/schemas/index.ts b/src/schemas/index.ts
new file mode 100644
index 00000000..db323bbe
--- /dev/null
+++ b/src/schemas/index.ts
@@ -0,0 +1,3 @@
+import StoreState from './StoreState.json'
+
+export { StoreState }
diff --git a/src/services/qbit.ts b/src/services/qbit.ts
index 7363f573..5ad79d73 100644
--- a/src/services/qbit.ts
+++ b/src/services/qbit.ts
@@ -92,7 +92,6 @@ export class QBitApi {
const params: Parameters = {
sort: !payload.isCustomSortEnabled ? payload.sort : null,
reverse: !payload.isCustomSortEnabled ? payload.reverse : null,
- hashes: payload.hashes.length > 0 ? payload.hashes.join('|') : null,
filter: payload.filter ? payload.filter : null,
category: payload.category !== null ? payload.category : null,
tag: payload.tag !== null ? payload.tag : null
diff --git a/src/store/index.ts b/src/store/index.ts
index c553801b..463b1bef 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -7,6 +7,7 @@ import mutations from './mutations'
import type { StoreState } from '@/types/vuetorrent'
import { Status } from '@/models'
import { TitleOptions } from '@/enums/vuetorrent'
+import {AppPreferences} from "@/types/qbit/models";
const vuexPersist = new VuexPersist({
key: 'vuetorrent',
@@ -67,12 +68,11 @@ export default new Vuex.Store({
searchPlugins: [],
selectMode: false,
selected_torrents: [],
- settings: null,
+ settings: {} as AppPreferences,
sort_options: {
isCustomSortEnabled: false,
sort: 'priority',
reverse: false,
- hashes: [],
filter: null,
category: null,
tag: null,
diff --git a/src/store/mutations.ts b/src/store/mutations.ts
index d04a3670..1dbeddcb 100644
--- a/src/store/mutations.ts
+++ b/src/store/mutations.ts
@@ -70,8 +70,7 @@ export default {
FETCH_SETTINGS: async (state: StoreState, settings: AppPreferences) => {
state.settings = settings
},
- UPDATE_SORT_OPTIONS: (state: StoreState, { hashes = [], filter = null, category = null, tag = null, tracker = null }) => {
- state.sort_options.hashes = hashes
+ UPDATE_SORT_OPTIONS: (state: StoreState, { filter = null, category = null, tag = null, tracker = null }) => {
state.sort_options.filter = filter
state.sort_options.category = category
state.sort_options.tag = tag
diff --git a/src/types/vuetorrent/SortOptions.ts b/src/types/vuetorrent/SortOptions.ts
index c53a963c..8c7f76b1 100644
--- a/src/types/vuetorrent/SortOptions.ts
+++ b/src/types/vuetorrent/SortOptions.ts
@@ -5,7 +5,6 @@ export default interface SortOptions {
isCustomSortEnabled: boolean
sort: string
reverse: boolean
- hashes: string[]
filter: Optional
category: Optional
tag: Optional
diff --git a/src/types/vuetorrent/StoreState.ts b/src/types/vuetorrent/StoreState.ts
index 82871c69..725fe3dc 100644
--- a/src/types/vuetorrent/StoreState.ts
+++ b/src/types/vuetorrent/StoreState.ts
@@ -4,7 +4,6 @@ import type Category from '../qbit/models/Category'
import type Torrent from '@/models/Torrent'
import type SortOptions from './SortOptions'
import type { AppPreferences } from '../qbit/models'
-import type { Optional } from '@/global'
import type ModalTemplate from './ModalTemplate'
import type { Status } from '@/models'
import type WebUISettings from '@/types/vuetorrent/WebUISettings'
@@ -30,7 +29,7 @@ export default interface StoreState {
searchPlugins: SearchPlugin[]
selectMode: boolean
selected_torrents: string[]
- settings: Optional
+ settings: AppPreferences
sort_options: SortOptions
status: Status
tags: string[]
diff --git a/tests/unit/__snapshots__/General.spec.ts.snap b/tests/unit/__snapshots__/General.spec.ts.snap
index 8758b00e..451abded 100644
--- a/tests/unit/__snapshots__/General.spec.ts.snap
+++ b/tests/unit/__snapshots__/General.spec.ts.snap
@@ -126,8 +126,15 @@ exports[`General > render correctly 1`] = `
+
+
+
+
+
+
+
-
+
"
`;