From 89938adafab1b0803fe5f70e7be75cee44ff838f Mon Sep 17 00:00:00 2001 From: Bogdan Bogdanov Date: Thu, 24 Sep 2020 09:42:57 +0300 Subject: [PATCH] Add search plugin manager foundation (#58) --- src/Api.ts | 9 +++ .../dialogs/searchDialog/PluginsManager.vue | 49 ++++++++++++++ .../dialogs/searchDialog/SearchDialog.vue | 24 +++++-- .../dialogs/searchDialog/SearchDialogForm.vue | 49 +++++++------- src/store/index.ts | 6 +- src/store/searchEngine.ts | 65 +++++++++++++++++++ src/store/types.ts | 7 +- 7 files changed, 179 insertions(+), 30 deletions(-) create mode 100644 src/components/dialogs/searchDialog/PluginsManager.vue create mode 100644 src/store/searchEngine.ts diff --git a/src/Api.ts b/src/Api.ts index 7d3e555..8aa8103 100644 --- a/src/Api.ts +++ b/src/Api.ts @@ -307,6 +307,15 @@ class Api { return this.axios.get(`/search/results?id=${id}`).then(Api.handleResponse); } + public enablePlugin(plugin: SearchPlugin, enable: boolean) { + const body = new URLSearchParams({ + names: plugin.name, + enable: JSON.stringify(enable) + }); + + return this.axios.post('/search/enablePlugin', body).then(Api.handleResponse); + } + private actionTorrents(action: string, hashes: string[], extra?: any) { const params: any = { hashes: hashes.join('|'), diff --git a/src/components/dialogs/searchDialog/PluginsManager.vue b/src/components/dialogs/searchDialog/PluginsManager.vue new file mode 100644 index 0000000..1235937 --- /dev/null +++ b/src/components/dialogs/searchDialog/PluginsManager.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/components/dialogs/searchDialog/SearchDialog.vue b/src/components/dialogs/searchDialog/SearchDialog.vue index f3a212c..f1593ff 100644 --- a/src/components/dialogs/searchDialog/SearchDialog.vue +++ b/src/components/dialogs/searchDialog/SearchDialog.vue @@ -48,9 +48,14 @@ - + + + mdi-cog Plugins manager + + + @@ -59,9 +64,10 @@ import api from "@/Api"; import HasTask from "@/mixins/hasTask"; import { Component, Prop, Emit } from "vue-property-decorator"; import { SearchTaskTorrent } from "@/types"; -import { mapGetters, mapMutations } from "vuex"; +import { mapActions, mapGetters, mapMutations } from "vuex"; import { tr } from "@/locale"; import SearchDialogForm from "./SearchDialogForm.vue"; +import PluginManager from "./PluginsManager.vue"; interface GridConfig { searchItems: SearchTaskTorrent[]; @@ -71,7 +77,8 @@ interface GridConfig { @Component({ components: { - SearchDialogForm + SearchDialogForm, + PluginManager }, computed: { ...mapGetters({ @@ -80,7 +87,10 @@ interface GridConfig { }) }, methods: { - ...mapMutations(["openAddForm", "setPasteUrl", "addFormDownloadItem"]) + ...mapMutations(["openAddForm", "setPasteUrl", "addFormDownloadItem", "openPluginManager"]), + ...mapActions({ + loadSearchPlugins: 'fetchSearchPlugins' + }) } }) export default class SearchDialog extends HasTask { @@ -115,6 +125,12 @@ export default class SearchDialog extends HasTask { setPasteUrl!: (_: any) => void; openAddForm!: () => void; addFormDownloadItem!: (_: any) => void; + loadSearchPlugins!: () => void; + openPluginManager!: () => void; + + mounted() { + this.loadSearchPlugins(); // load the plugins so they are available in the entire module + } async downloadTorrent(item: SearchTaskTorrent) { this.addFormDownloadItem({ diff --git a/src/components/dialogs/searchDialog/SearchDialogForm.vue b/src/components/dialogs/searchDialog/SearchDialogForm.vue index 74e0748..a6cb0aa 100644 --- a/src/components/dialogs/searchDialog/SearchDialogForm.vue +++ b/src/components/dialogs/searchDialog/SearchDialogForm.vue @@ -25,6 +25,8 @@ x.enabled); + this.toggleSelectAll(); + } } - async getAvailablePlugins(): Promise { - const availablePlugins = await api.getSearchPlugins(); - - return availablePlugins - .filter(plugin => plugin.enabled === true) - .sort((p1, p2) => p1.fullName.localeCompare(p2.fullName)); - } - - @Emit('triggerSearch') + @Emit("triggerSearch") triggerSearch(): SearchForm | void { if (!this.searchForm.valid) { return; } - const plugins = this.hasSelectAllPlugins + const plugins = this.hasSelectAllPlugins ? ALL_KEY : this.searchForm.plugins.map(p => p.name).join("|"); @@ -209,7 +212,7 @@ export default class SearchDialogForm extends Vue { return searchForm; } - @Emit('stopSearch') + @Emit("stopSearch") stopSearch() { // } diff --git a/src/store/index.ts b/src/store/index.ts index 07a9834..9e47e5c 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -9,6 +9,7 @@ import { snackBarStore } from './snackBar'; import { addFormStore } from './addForm'; import { AllStateTypes } from '../consts'; import { torrentIsState } from '../utils'; +import searchEngineStore from './searchEngine'; import { RootState } from './types'; Vue.use(Vuex); @@ -18,7 +19,8 @@ const store = new Vuex.Store({ config: configStore, dialog: dialogStore, snackBar: snackBarStore, - addForm: addFormStore + addForm: addFormStore, + searchEngine: searchEngineStore }, state: { rid: 0, @@ -117,7 +119,7 @@ const store = new Vuex.Store({ return result; }, - }, + } }); export default store; diff --git a/src/store/searchEngine.ts b/src/store/searchEngine.ts new file mode 100644 index 0000000..f86f061 --- /dev/null +++ b/src/store/searchEngine.ts @@ -0,0 +1,65 @@ +import { Module } from "vuex"; +import { SearchPlugin } from "@/types"; +import { SearchEnginePage } from "./types"; +import api from "@/Api"; + +export default { + state: { + searchPlugins: [], + isPluginManagerOpen: false + }, + mutations: { + setSearchPlugins(state, plugins: SearchPlugin[] | undefined | null) { + state.searchPlugins = plugins; + }, + openPluginManager(state) { + state.isPluginManagerOpen = true; + }, + closePluginManager(state) { + state.isPluginManagerOpen = false; + } + }, + getters: { + allSearchPlugins(state): SearchPlugin[] | undefined | null { + return state.searchPlugins; + } + }, + actions: { + fetchSearchPlugins({ dispatch }) { + // semantic helper + dispatch("getSearchPluginsRequest"); + }, + async getSearchPluginsRequest({ dispatch }) { + try { + const searchPlugins = await api.getSearchPlugins(); + + dispatch("getSearchPluginRequestSuccess", searchPlugins); + } catch { + dispatch("getSearchPluginsRequestFailure"); + } + }, + getSearchPluginRequestSuccess({ commit }, searchPlugins) { + commit("setSearchPlugins", undefined); + + commit("setSearchPlugins", searchPlugins); + }, + getSearchPluginRequestFailure({ commit }) { + commit("setSearchPlugins", null); + }, + togglePluginAvailability({ dispatch }, plugin) { + dispatch("togglePluginEnableRequest", plugin); + }, + async togglePluginEnableRequest({ dispatch }, plugin: SearchPlugin) { + try { + await api.enablePlugin(plugin, !plugin.enabled); // switch plugin enable state + + dispatch("enablePluginRequestSuccess", plugin); + } catch { + // Do nothing + } + }, + enablePluginRequestSuccess({ dispatch }) { + dispatch('fetchSearchPlugins'); // refresh the plugins + } + } +} as Module; diff --git a/src/store/types.ts b/src/store/types.ts index 937e37e..262f2db 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -1,4 +1,4 @@ -import { MainData } from '@/types'; +import { MainData, SearchPlugin } from '@/types'; import { Config } from './config'; export interface RootState { @@ -8,6 +8,11 @@ export interface RootState { pasteUrl: string | null; } +export interface SearchEnginePage { + searchPlugins: SearchPlugin[] | null | undefined; + isPluginManagerOpen: boolean; +} + export interface AddFormState { isOpen: boolean; downloadItem: {