File: D:/web/matomo.vdk/plugins/TagManager/vue/src/Tag/Tags.store.ts
/*!
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
import {
computed,
DeepReadonly,
reactive,
readonly,
} from 'vue';
import { AjaxHelper } from 'CoreHome';
import {
TagTypeCategory,
Tag,
} from '../types';
interface TagsStoreState {
tags: Tag[];
isLoadingTags: boolean;
isLoadingSingle: boolean;
isUpdating: boolean;
}
type AvailableTagPromises = Record<string, Promise<DeepReadonly<TagTypeCategory[]>>>;
class TagsStore {
private privateState = reactive<TagsStoreState>({
tags: [],
isLoadingTags: false,
isLoadingSingle: false,
isUpdating: false,
});
private state = computed(() => readonly(this.privateState));
readonly isLoading = computed(() => {
const state = this.state.value;
return state.isLoadingTags || state.isLoadingSingle;
});
readonly isUpdating = computed(() => this.state.value.isUpdating);
readonly tags = computed(() => this.state.value.tags);
private fetchPromise: Promise<Tag[]> | null = null;
private availableTagsPromises: AvailableTagPromises = {};
fetchTags(idContainer: string, idContainerVersion: number): Promise<DeepReadonly<Tag[]>> {
this.privateState.isLoadingTags = true;
this.privateState.tags = [];
if (!this.fetchPromise) {
this.fetchPromise = AjaxHelper.fetch<Tag[]>({
method: 'TagManager.getContainerTags',
idContainer,
idContainerVersion,
filter_limit: '-1',
});
}
return Promise.resolve(this.fetchPromise).then((tags) => {
this.privateState.tags = tags;
this.privateState.isLoadingTags = false;
return this.tags.value;
}).finally(() => {
this.privateState.isLoadingTags = false;
});
}
reload(
idContainer: string,
idContainerVersion: number,
): ReturnType<TagsStore['fetchTags']> {
this.privateState.tags = [];
this.fetchPromise = null;
this.availableTagsPromises = {};
return this.fetchTags(idContainer, idContainerVersion);
}
findTag(
idContainer: string,
idContainerVersion: number,
idTag: number,
): Promise<DeepReadonly<Tag>> {
// before going through an API request we first try to find it in loaded variables
const found = this.tags.value.find((v) => v.idtag === idTag);
if (found) {
return Promise.resolve(found);
}
// otherwise we fetch it via API
this.privateState.isLoadingSingle = true;
return AjaxHelper.fetch<Tag>({
idTag,
idContainer,
idContainerVersion,
method: 'TagManager.getContainerTag',
filter_limit: '-1',
}).then((record) => {
this.privateState.tags = [...this.privateState.tags, record];
return readonly(record);
}).finally(() => {
this.privateState.isLoadingSingle = false;
});
}
fetchAvailableTags(idContext: string): TagsStore['availableTagsPromises'][''] {
if (!this.availableTagsPromises[idContext]) {
this.availableTagsPromises[idContext] = AjaxHelper.fetch<TagTypeCategory[]>({
method: 'TagManager.getAvailableTagTypesInContext',
idContext,
filter_limit: '-1',
}).then((tags) => readonly(tags));
}
return Promise.resolve(this.availableTagsPromises[idContext]);
}
createOrUpdateTag(
tag: DeepReadonly<Tag>|Tag,
method: string,
idContainer: string,
idContainerVersion: number,
parameterValues: Record<string, unknown>,
fireTriggerIds: number[],
blockTriggerIds: number[],
): Promise<{ value: number }> {
this.privateState.isUpdating = true;
const mappedEntries = Object.entries(parameterValues).map(([key, value]) => {
let newValue = value;
if (typeof value === 'boolean') {
newValue = (+value).toString();
}
return [key, newValue];
});
const parameters = Object.fromEntries(mappedEntries);
return AjaxHelper.post<{ value: number }>(
{
idTag: tag.idtag,
method,
idContainer,
idContainerVersion,
type: tag.type,
name: tag.name,
description: tag.description,
startDate: tag.start_date,
endDate: tag.end_date,
fireLimit: tag.fire_limit,
fireDelay: tag.fire_delay,
priority: tag.priority,
},
{
parameters,
fireTriggerIds,
blockTriggerIds,
},
{ withTokenInUrl: true },
).finally(() => {
this.privateState.isUpdating = false;
});
}
deleteTag(
idContainer: string,
idContainerVersion: number,
idTag: number,
): Promise<void> {
this.privateState.isUpdating = true;
this.privateState.tags = [];
return AjaxHelper.fetch(
{
idTag,
idContainerVersion,
idContainer,
method: 'TagManager.deleteContainerTag',
},
{ withTokenInUrl: true },
).finally(() => {
this.privateState.isUpdating = false;
});
}
pauseTag(
idContainer: string,
idContainerVersion: number,
idTag: number,
): Promise<void> {
this.privateState.isUpdating = true;
this.privateState.tags = [];
return AjaxHelper.fetch(
{
idTag,
idContainerVersion,
idContainer,
method: 'TagManager.pauseContainerTag',
},
{ withTokenInUrl: true },
).finally(() => {
this.privateState.isUpdating = false;
});
}
resumeTag(
idContainer: string,
idContainerVersion: number,
idTag: number,
): Promise<void> {
this.privateState.isUpdating = true;
this.privateState.tags = [];
return AjaxHelper.fetch(
{
idTag,
idContainerVersion,
idContainer,
method: 'TagManager.resumeContainerTag',
},
{ withTokenInUrl: true },
).finally(() => {
this.privateState.isUpdating = false;
});
}
suggestNameForType(templateId: string): string|undefined {
for (let counter = 0; counter < 100; counter += 1) {
let name = templateId;
if (counter) {
name = `${name} (${counter})`;
}
const isFree = !this.tags.value.some((v) => v.name === name);
if (isFree) {
return name;
}
}
return undefined;
}
}
export default new TagsStore();