File: D:/web/matomo.vdk/plugins/Marketplace/vue/src/Marketplace/Marketplace.vue
<!--
Matomo - free/libre analytics platform
@link https://matomo.org
@license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
-->
<template>
<div class="row marketplaceActions" ref="root">
<div class="col s12 m6 l4">
<Field
uicontrol="select"
name="plugin_type"
:model-value="pluginTypeFilter"
@update:model-value="updateType"
:title="translate('Marketplace_Show')"
:full-width="true"
:options="pluginTypeOptions"
>
</Field>
</div>
<div class="col s12 m6 l4">
<Field
uicontrol="select"
name="plugin_sort"
:model-value="pluginSort"
@update:model-value="updateSort"
:title="translate('Marketplace_Sort')"
:full-width="true"
:options="pluginSortOptions"
>
</Field>
</div>
<!-- Hide filters and search for themes because we don't have many of them -->
<div class="col s12 m12 l4 " v-if="pluginsToShow?.length > 20 || searchQuery">
<div class="plugin-search">
<div>
<Field
uicontrol="text"
name="query"
:title="queryInputTitle"
:full-width="true"
:model-value="searchQuery"
@update:model-value="updateQuery"
>
</Field>
</div>
<span
class="icon-search"
/>
</div>
</div>
</div>
<PluginList v-if="!loading && pluginsToShow.length > 0"
:plugins-to-show="pluginsToShow"
:current-user-email="currentUserEmail"
:is-auto-update-possible="isAutoUpdatePossible"
:is-super-user="isSuperUser"
:is-multi-server-environment="isMultiServerEnvironment"
:has-some-admin-access="hasSomeAdminAccess"
:is-plugins-admin-enabled="isPluginsAdminEnabled"
:is-valid-consumer="isValidConsumer"
:deactivate-nonce="deactivateNonce"
:activate-nonce="activateNonce"
:install-nonce="installNonce"
:update-nonce="updateNonce"
:num-users="numUsers"
@trigger-update="this.updateMarketplace()"
@start-trial-start="this.$emit('startTrialStart')"
@start-trial-stop="this.$emit('startTrialStop')"
/>
<ContentBlock v-if="!loading && pluginsToShow.length == 0">
{{ translate(showThemes ? 'Marketplace_NoThemesFound' : 'Marketplace_NoPluginsFound') }}
</ContentBlock>
<ContentBlock v-if="loading">
<MatomoLoader />
{{ loadingMessage }}
</ContentBlock>
</template>
<script lang="ts">
import { defineComponent, watch } from 'vue';
import {
translate,
AjaxHelper,
ContentBlock,
Matomo,
MatomoLoader,
MatomoUrl,
} from 'CoreHome';
import { Field } from 'CorePluginsAdmin';
import PluginList from '../PluginList/PluginList.vue';
interface MarketplaceState {
loading: boolean;
fetchRequest: Promise<void>|null;
fetchRequestAbortController: AbortController|null;
pluginSort: string;
pluginTypeFilter: string;
searchQuery: string;
pluginsToShow: Array<Record<string, unknown>>;
}
const lcfirst = (s: string) => `${s[0].toLowerCase()}${s.substring(1)}`;
export default defineComponent({
props: {
pluginTypeOptions: {
type: Object,
required: true,
},
defaultSort: {
type: String,
required: true,
},
pluginSortOptions: {
type: Object,
required: true,
},
currentUserEmail: String,
isValidConsumer: Boolean,
isSuperUser: Boolean,
isAutoUpdatePossible: Boolean,
isPluginsAdminEnabled: Boolean,
isMultiServerEnvironment: Boolean,
hasSomeAdminAccess: Boolean,
installNonce: {
type: String,
required: true,
},
activateNonce: {
type: String,
required: true,
},
deactivateNonce: {
type: String,
required: true,
},
updateNonce: {
type: String,
required: true,
},
numUsers: {
type: Number,
required: true,
},
},
components: {
ContentBlock,
Field,
MatomoLoader,
PluginList,
},
data(): MarketplaceState {
return {
loading: false,
fetchRequest: null,
fetchRequestAbortController: null,
pluginSort: this.defaultSort,
pluginTypeFilter: 'plugins',
searchQuery: '',
pluginsToShow: [],
};
},
emits: ['triggerUpdate', 'startTrialStart', 'startTrialStop'],
mounted() {
Matomo.postEvent('Marketplace.Marketplace.mounted', { element: this.$refs.root });
watch(() => MatomoUrl.hashParsed.value, () => {
this.updateValuesFromHash(false);
});
this.updateValuesFromHash(true);
},
unmounted() {
Matomo.postEvent('Marketplace.Marketplace.unmounted', { element: this.$refs.root });
},
methods: {
updateValuesFromHash(forceFetch: boolean) {
let doFetch = forceFetch;
const newSearchQuery = (MatomoUrl.hashParsed.value.query || '') as string;
const newPluginSort = (MatomoUrl.hashParsed.value.sort || '') as string;
const newPluginTypeFilter = (MatomoUrl.hashParsed.value.pluginType || '') as string;
if (newSearchQuery || this.searchQuery) {
doFetch = doFetch || newSearchQuery !== this.searchQuery;
this.searchQuery = newSearchQuery;
}
if (newPluginSort) {
doFetch = doFetch || newPluginSort !== this.pluginSort;
this.pluginSort = newPluginSort;
}
if (newPluginTypeFilter) {
doFetch = doFetch || newPluginTypeFilter !== this.pluginTypeFilter;
this.pluginTypeFilter = newPluginTypeFilter;
}
if (!doFetch) {
return;
}
this.fetchPlugins();
},
updateQuery(event: Event) {
MatomoUrl.updateHash({
...MatomoUrl.hashParsed.value,
query: event,
});
},
updateType(event: Event) {
MatomoUrl.updateHash({
...MatomoUrl.hashParsed.value,
pluginType: event,
});
},
updateSort(event: Event) {
MatomoUrl.updateHash({
...MatomoUrl.hashParsed.value,
sort: event,
});
},
updateMarketplace() {
this.fetchPlugins(() => this.$emit('triggerUpdate'));
},
fetchPlugins(cb: (() => void) | void) {
this.loading = true;
this.pluginsToShow = [];
if (this.fetchRequestAbortController) {
this.fetchRequestAbortController.abort();
this.fetchRequestAbortController = null;
}
this.fetchRequestAbortController = new AbortController();
this.fetchRequest = AjaxHelper.post(
{
module: 'Marketplace',
action: 'searchPlugins',
format: 'JSON',
},
{
query: this.searchQuery,
sort: this.pluginSort,
themesOnly: this.showThemes,
purchaseType: this.pluginTypeFilter === 'premium' ? 'paid' : '',
},
{
withTokenInUrl: true,
abortController: this.fetchRequestAbortController,
},
).then((response) => {
this.pluginsToShow = response;
if (typeof cb === 'function') {
cb();
}
}).finally(() => {
this.loading = false;
this.fetchRequestAbortController = null;
});
},
},
computed: {
queryInputTitle(): string {
const plugins = lcfirst(translate('General_Plugins'));
return `${translate('General_Search')} ${plugins}...`;
},
loadingMessage(): string {
return translate(
'Mobile_LoadingReport',
translate(this.showThemes ? 'CorePluginsAdmin_Themes' : 'General_Plugins'),
);
},
showThemes(): boolean {
return this.pluginTypeFilter === 'themes';
},
},
});
</script>